投資戦略のバックテストを行なうのは大変ですよね。
そんな時は、既存のライブラリを使うと簡単にそして、高速に行なえます。
今回は、バックテスト用のPythonライブラリであるBacktesting.pyのBacktestクラスについて内容を公式ドキュメントに基づいて見ていきたいと思います。
Backtestクラスの使い方
Backtestクラスは以下のように使います。
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from backtesting.test import SMA, GOOG
class SmaCross(Strategy):
def init(self):
price = self.data.Close
self.ma1 = self.I(SMA, price, 10)
self.ma2 = self.I(SMA, price, 20)
def next(self):
if crossover(self.ma1, self.ma2):
self.buy()
elif crossover(self.ma2, self.ma1):
self.sell()
# ここでBacktestを使用
bt = Backtest(GOOG, SmaCross, commission=.002,
exclusive_orders=True)
stats = bt.run()
bt.plot()
上記を実行すると、以下のように過去データに基づいた投資戦略・手法のパフォーマンスを可視化できます。
Start 2004-08-19 00:00:00
End 2013-03-01 00:00:00
Duration 3116 days 00:00:00
Exposure Time [%] 94.27
Equity Final [$] 68935.12
Equity Peak [$] 68991.22
Return [%] 589.35
Buy & Hold Return [%] 703.46
Return (Ann.) [%] 25.42
Volatility (Ann.) [%] 38.43
Sharpe Ratio 0.66
Sortino Ratio 1.30
Calmar Ratio 0.77
Max. Drawdown [%] -33.08
Avg. Drawdown [%] -5.58
Max. Drawdown Duration 688 days 00:00:00
Avg. Drawdown Duration 41 days 00:00:00
# Trades 93
Win Rate [%] 53.76
Best Trade [%] 57.12
Worst Trade [%] -16.63
Avg. Trade [%] 1.96
Max. Trade Duration 121 days 00:00:00
Avg. Trade Duration 32 days 00:00:00
Profit Factor 2.13
Expectancy [%] 6.91
SQN 1.78
Kelly Criterion 0.6134
_strategy SmaCross(n1=10, n2=20)
_equity_curve Equ...
_trades Size EntryB...
dtype: object
Backtestのインスタンス化の際に指定している引数や、そのあとに使用しているrun、plotメソッドなどについて、以下で解説していきます。
Backtestクラスのパラメータ
class Backtest(data, strategy, *, cash=10000, commission=0.0, margin=1.0,
trade_on_close=False, hedging=False, exclusive_orders=False)
Backtestのインスタンス化の際に指定できるパラメータとその内容は以下の通りです。
data
dataには、バックテストに用いる過去の価格データフレーム(pd.DataFrame)を指定します。
注意点としては、Open、High、Low、Closeを含んでおく必要があり、それぞれのカラム名も頭文字は大文字で設定しておく必要があります。
openやhighなど小文字の指定だと「`data` must be a pandas.DataFrame with columns ‘Open’, ‘High’, ‘Low’, ‘Close’, and (optionally) ‘Volume’」というエラーが返ってきます。
上記のOHLCデータ以外にも、任意でVolumeデータを渡すこともできます。
任意のカラムも付与可能
また、上記OHLCV以外にも、自身のストラテジーで使用する独自のカラム(例えば期間20の移動平均を格納したma20カラムなど)もデータフレームに付与しておけます。
インデックスはdatetimeまたは0~の数値
dataに渡すデータフレームのインデックスはtimestampまたは0以上の連続した数値のどちらでも使用できます。
strategy
strategyには、Strategyクラスを継承したサブクラスを指定します。
注意点としては、インスタンスではなくクラス名を記載するという点です。
cash
cashには、最初の残高を指定します。
おそらくですが、単位としてはdataとして渡す通貨の単位に合わすとよいと思います。
BTCUSDTの場合はUSDの単位で。BTCJPYの場合はJPYの単位でという感じです。
commision
commisionには、取引所の手数料を指定します。
例えば1%の手数料の場合は、0.01と指定します。
スプレッドを考慮したバックテストを行ないたい場合は、このcommisionの数値を調節することで対応できます。
例えば平均スプレッドがask価格の0.2%だとした場合は、0.0002と指定することである程度のスプレッドを見積もったバックテストを行なえます。
margin
marginには、レバレッジを指定します。
指定の仕方は、レバレッジ数ではなくレバレッジ比率を指定します。
例えば、50倍のレバレッジの場合0.02(1/50)を指定します。
trade_on_close
trade_on_closeでは、成行き注文の実行タイミングを変更できます。
Trueに設定した場合は、次のローソク足の始値ではなく、その足の終わりで約定するようになります。
デフォルトはFalseで、その場合は該当のローソク足の終値ではなく次の足の始値で約定になります。
hedging
hedgingでは両建ての有無の指定を行なえます。
Trueに設定した場合は、両建てを行うことができます。
デフォルトのFalseの場合は、FIFO注文として反対ポジションを保有している場合に新規注文とならず、保有している古い反対ポジションから順に決済されるようになります。
exclusive_orders
exclusive_ordersでは、各注文時にポジションを閉じるか否かの設定を行なえます。
Trueに設定すると各新規注文時に保持しているポジションを解消してから、新しく注文を行ないます。
ドテンのシュミレーションを行う場合には、これが使えそうですね。
Backtestクラスのメソッド3つ
Backtestクラスのメソッドは以下の3つです。
- run
- plot
- optimize
それぞれ簡単に解説していきます。
plot
def plot(self, *, results=None, filename=None, plot_width=None, plot_equity=True,
plot_return=False, plot_pl=True, plot_volume=True, plot_drawdown=False,
smooth_equity=False, relative_equity=True, superimpose=True, resample=True,
reverse_indicators=False, show_legend=True, open_browser=True)
plot
では、バックテストの経過を可視化できます。
指定できるパラメータはいろいろとありますが、重要そうなものは以下の通りです。
パラメータ | 内容 |
---|---|
results | バックテストの結果を指定できます。 指定しない場合は最後に run した結果が使用されます。 |
filename | HTMLとして保存するパスを指定できます。 デフォルトではstrategy/parameter-dependentファイルが現在のワーキングディレクトリに保存されます。 |
plot_width | プロットの幅を指定します。デフォルトでは100%が指定されています。 (高さは自動で調整されます) |
plot_equity | イクイティ(開始残高+アセット)を描写の有無を指定します。Trueにしておくことで資産の変動が描写されます。 |
plot_return | リターンの描写有無を指定します。 |
plot_pl | 損益(P/L)の描写の有無を指定します。 |
plot_volume | volumeを描写有無を指定します。 volumeの描写にはVolume情報をデータフレームに渡しておく必要があります。 |
plot_drawdown | ドローダウンの描写有無を指定します。 |
relative_equity | イクイティの%表示に指定します。 |
上記以外のパラメータについては公式ドキュメントをご参照ください。
run
def run(self, **kwargs)
run
では、バックテストの実行を行ないます。
返却値として結果と統計データがpd.Seriesで返ってきます。
ちなみに、**kwargsはストラテジーのパラメータとして解釈されるようです。
optimize
def optimize(self, *, maximize='SQN', method='grid', max_tries=None, constraint=None,
return_heatmap=False, return_optimization=False, random_state=None,
**kwargs)
optimize
では、ストラテジーのパラメータの最適値を探索できます。
optimize関数に指定できるパラメータと内容は以下の通りです。
パラメータ | 内容 |
---|---|
maximize | 最適化する際にどの値を最大化するかを指定します。 Strategy.run()で返却されるpd.Seriesのキーを指定できます。 例:最終残高の最大化の場合は Equity Final [$] と指定。 |
method | grid またはskopt を指定します。・gridでは、パラメーターの組み合わせの直積にわたる網羅的(またはランダムな)検索が実行されます。 ・最大でmax_triesの評価を行い、モデルベースの最適化を使用して近似最適な戦略パラメーターを見つけます。 |
max_tries | 実行最大回数を指定します。 methodがgridの場合は、最大回数を指定することでランダムグリッドサーチが実行されるようになります。 0~1の間の小数点 |
constraint | 最適化するパラメータのコンビネーションの制約を指定します。 例) backtest.optimize(sma1=[5, 10, 15], sma2=[10, 20, 40], constraint=lambda p: p.sma1 < p.sma2) この場合は、sma1が10とsma2が10の場合のパターンは実行されません。 |
return_heatmap | パラメータのコンビネーションのヒートマップを返却するか否かを指定します。 |
return_optimization | methodがskoptの場合、scipy.optimize.OptimizeResult も返却されます。 |
random_state | 最適化の結果を再現したい場合は、固定の整数をこのパラメータに指定します。 |
まとめ:Backtesting.pyを使って、らくにバックテストを行なおう
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from backtesting.test import SMA, GOOG
class SmaCross(Strategy):
def init(self):
price = self.data.Close
self.ma1 = self.I(SMA, price, 10)
self.ma2 = self.I(SMA, price, 20)
def next(self):
if crossover(self.ma1, self.ma2):
self.buy()
elif crossover(self.ma2, self.ma1):
self.sell()
# ここでBacktestを使用
bt = Backtest(GOOG, SmaCross, commission=.002,
exclusive_orders=True)
stats = bt.run()
bt.plot()
今回は、仮想通貨や株式、為替などのストラテジーのパフォーマンスを確認できるバックテストを行なえるPythonライブラリ『Backtesting.py』のBacktestクラスについて解説しました。
これがあると、わざわざ自分でバックテストのコードを書かずに、気軽にバックテストできるのでかなり良いですね。