こんにちは!スナフキンです.
先日実装したBTC自動取引botのバックテストですが,単純な移動平均交差法だと損益がマイナスになってしまうことが分かりました.なぜそうなってしまったか,結果を眺めているだけでは分からないので,途中経過を可視化してみることにします.
結果・考察
バックテスト結果をグラフにしたものは以下です.
「ゴールデンクロスで買い,デッドクロスで売る」というロジック自体は問題なく動いているようです.パッと見た感じでわかる問題点は以下のようなものでしょうか.
- レンジ相場でのエントリーが多く,その多くが高いところで買い,安いところで売ってしまっている.
- トレンド相場でのエントリーは問題なく機能しているが,値幅がそれほど取れていない.
2つ目の問題点は,解決が難しそうです.なぜなら,天井および底の見極めは,人間にとってもそれほど簡単ではなく,出来高・相場の雰囲気などの価格データ以外のものも多く絡んでくるためです.
1つ目については,移動平均線の傾きを判断ロジックに組み込むなどによって解決できるかもしれません.
ぼくがこれまでに動かしてきたbotは,テクニカル指標を使わないタイプのbotだったので,これまでこのようなテクニカル指標を基にしたロジックの検証は行ってきませんでした.しかし,BitFLyerの遅延問題などもあり,遅延にあまり影響を受けない,少し長めの時間軸(と言っても見る足は最大でも15分足程度)でトレードを行うbotの開発も進めたいと感じています.今回のバックテストの実装で,ひとまずその環境が整ったと思うので,いろいろと試してみたいと思います.
書いたコード
一応ここまでに書いたコードを全て載せておきます.(参考になるほどきれいでもありませんが.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
import talib as ta import numpy as np import matplotlib.pyplot as plt myfile = '20180203.csv' data = np.loadtxt(myfile, delimiter=",", usecols=(1,2,3,4)) short_ema = ta.EMA(data[:,3], timeperiod=9) middle_ema = ta.EMA(data[:,3], timeperiod=50) #買いなら1売りなら-1,待機なら0を返す.入れるデータはiterableなもの. def judge_from_ema(ema_shorter, ema_longer): judgements = [0] * len(data) #デッドクロス,ゴールデンクロスの判断は最後はできないので,len(ema_shorter)-1までしかループは回さない. for i in range(len(ema_shorter)-1): if ema_shorter[i] < ema_longer[i] and ema_shorter[i+1] > ema_longer[i+1]: #ゴールデンクロス judgements[i] = 1 elif ema_shorter[i] > ema_longer[i] and ema_shorter[i+1] < ema_longer[i+1]: #デッドクロス judgements[i] = -1 else: judgements[i] = 0 return judgements #日本円初期保有量 capital = 10000 total = capital #取引単位(BTC) units = 0.1 #バックテスト可視化用変数 #横軸用インデックス index = range(len(data)) buy_prices = [] buy_index =[] sell_prices =[] sell_index = [] #買いポジを持っているときは1,売りポジを持っているときは-1,ポジを持っていないときは0 position = 0 number_of_trade = 0 judgements = judge_from_ema(short_ema, middle_ema) #バックテスト.ローソク足の終値で取引すると仮定.最後のローソク足で全てのポジションを決済する. for i in range(len(data)-1): if judgements[i] == 1: total = total - units * data[i+1,3] buy_prices.append(data[i+1,3]) buy_index.append(i) position += 1 number_of_trade += 1 elif judgements[i] == -1: total = total + units * data[i+1, 3] sell_prices.append(data[i+1,3]) sell_index.append(i) position += -1 number_of_trade += 1 else: pass #最後の足でポジションをまとめて決済. if position == 1:#買いポジを最後に持っていたら売り決済 total = total + units * data[-1,3] position += -1 number_of_trade += 1 sell_prices.append(data[-1,3]) sell_index.append(len(data)-1) elif position == -1:#売りポジを最後に持っていたら買い決済 total = total - units * data[-1,3] position += 1 number_of_trade += 1 buy_prices.append(data[-1,3]) buy_index.append(len(data)-1) #バックテストの可視化.pricedataは価格の配列,indexは横軸データ. def describe(index, pricedata, buy_prices, buy_index, sell_prices, sell_index): plt.plot(index, pricedata, label="price") plt.plot(buy_index, buy_prices, "go", label="buy points") plt.plot(sell_index, sell_prices, "ro", label="sell points") plt.plot(index, middle_ema, label="middle_ema") plt.plot(index, short_ema, color="black", label="short_ema") plt.ylabel("price") plt.xlabel("index") plt.title("The result of backtest") plt.legend() plt.show() number_of_trade = number_of_trade / 2 describe(index, data[:,3], buy_prices, buy_index, sell_prices, sell_index) print("Trade unit: {}".format(units)) print("The position code is {} now.".format(position)) print("Total P/L per day: {}".format(total-capital)) print("The number of trade: {}".format(number_of_trade)) |
ロジック部分を少し書き換えれば他の戦略も簡単に試せるので,今後もいろいろと試してみようと思います.ありがとうございました!