システムトレード検証と実践 を読みながらシステムトレードの勉強中です。システムトレードではシステムの評価に過去のデータを使ったバックテストを行います。今回の参考書では、
- ヒストリカルバックテスト
- システムのパラメータを、過去のデータを用いて最適化し、システムの成績を評価する
- アウトオブサンプルテスト
- 直近のデータを一部残し、その他のデータでシステムのパラメータを最適化した後、残しておいた直近のデータでシステムの成績を評価する
- ウォークフォワードテスト
- アウトオブサンプルテストを期間をずらしながら複数回、最適化と評価を行い、各評価期間の成績を繋げることでシステムの成績を評価する
の3つのバックテストについての紹介があり、システムの詳細な評価のためにはウォークフォワードテストが必要とのことです。
Pythonでバックテストを行うライブラリはたくさんあるのですが、日本語の情報が多いのはbacktesting.pyかと思います。ドキュメントにはヒストリカルバックテストの例のみ記載があり、ウォークフォワードテストは少し手を加える必要がありそうです。
ウォークフォワードテストを実装する前に、アウトオブサンプルテストを実装する必要があるため、その方法を考えました。ついでに指標をクイックスタートから少し変更し、TALibの指数移動平均を用いたシステムを作ります。
from backtesting import Strategy, Backtest
from backtesting.lib import crossover
from backtesting.test import GOOG
from talib import EMA
# 移動平均線のクロスで売買する
class EmaCross(Strategy):
n1 = 10
n2 = 30
def init(self):
# TALibの指数移動平均は直接渡せる
self.ema1 = self.I(EMA, self.data.Close, self.n1)
self.ema2 = self.I(EMA, self.data.Close, self.n2)
def next(self):
if crossover(self.ema1, self.ema2):
self.position.close()
self.buy()
elif crossover(self.ema2, self.ema1):
self.position.close()
slef.sell()
df = GOOG
# インサンプル期間のデータでパラメータを最適化
df_in = df.query('"2005-01-01" <= index < "2010-01-01"')
bt_in = Backtest(df_in, EmaCross, cash=100_000, commission=.002)
stats_in = bt_in.optimize(
n1=range(5, 30, 5),
n2=range(10, 70, 5),
maximize='Equity Final [$]',
constraint=lambda param: param.n1 < param.n2,
)
# アウトサンプル期間のデータでシステムを評価
df_out = df.query('"2010-01-01" >= index')
bt_out = Backtest(df_out, EmaCross)
# runメソッドに、インサンプル期間の結果(_stats_in)の中に
# 入っている戦略のパラメータを展開代入する
stats_out = bt_out.run(**stats_in._strategy._params)
print(stats_in)
print(stats_out)
実行するとインサンプル期間の評価結果と、最適化に使っていないアウトサンプル期間の評価結果が表示されます。
最適化時のリターンは年率46%でしたが、アウトサンプル期間のリターンは年率4%に減っています。
これでアウトオブサンプルテストをできるようになりました。
ウォークフォワードテストもこの調子で実装できそうです。