【Pythonによるバックテスト改善】MACDの日数をベイズ最適化する

6月 22, 2021

MACDはシステムトレードの基本的な手法です.

以前,以下の記事でMACDをバックテストしてみました.

この記事ではPythonライブラリ『Backtesting.py』によってバックテストおよび手法の最適化を行いましたが,今回は最適化手法を変えてベイズ最適化で実行してみたいと思います.

MACDのバックテスト

基本的には,以下を参考にしてください.今回は簡単に説明します.

【Python】MACDシグナルでの売買をバックテストする(Backtesting.py&TA-Lib)

ライブラリインストール

今回使う主なライブラリは以下です.

pandas-datareader(株価データ取得)

datetime(株価の取得期間)

TA-Lib(MACD計算用)

backtesting.py(バックテスト)

scikit-optimize(手法最適化に使用)

株価データの取得にはpandas-datareader,MACDの計算にはTA-Libを使いますが,別の方法でデータを取得,計算しても大丈夫です.

ほとんどはpipでインストールできますが,TA-Libのインストールは少々厄介ですからこちらの記事をご覧ください.

また,これらのインストールにはNumpyやmatplotlibなどのライブラリも必要になるかもしれません.

バックテストのソースコード

今回は,2018/1/1~現在までのAAPL(Apple)の株価で行いたいと思います.

MACDのバックテストを行うコードはこちらです.


import pandas_datareader.data as web
import datetime

start = datetime.date(2018,1,1)
end = datetime.date.today()
data = web.DataReader('AAPL', 'yahoo', start, end)

from backtesting import Backtest, Strategy # バックテスト、ストラテジー
from backtesting.lib import crossover

import talib as ta

def MACD(close, n1, n2, ns):
    macd, macdsignal, macdhist = ta.MACD(close, fastperiod=n1, slowperiod=n2, signalperiod=ns)
    return macd, macdsignal

class MACDCross(Strategy):
    n1 = 12 #短期EMAの期間
    n2 = 26 #長期EMAの期間
    ns = 9 #シグナル(MACDのSMA)の期間

    def init(self):
        self.macd, self.macdsignal = self.I(MACD, self.data.Close, self.n1, self.n2, self.ns)

    def next(self): # チャートデータの行ごとに呼び出される
        if crossover(self.macd, self.macdsignal): #macdがsignalを上回った時
            self.buy() # 買い
        elif crossover(self.macdsignal, self.macd): #signalがmacdを上回った時
            self.position.close() # 売り

# バックテストを設定
bt = Backtest(
    data, # チャートデータ
    MACDCross, # 売買戦略
    cash=1000, # 最初の所持金
    commission=0.00495, # 取引手数料
    margin=1.0, # レバレッジ倍率の逆数(0.5で2倍レバレッジ)
    trade_on_close=True, # True:現在の終値で取引,False:次の時間の始値で取引
    exclusive_orders=True #自動でポジションをクローズ(オープン)
)

output = bt.run() # バックテスト実行
print(output) # 実行結果(データ)
bt.plot() # 実行結果(グラフ)

これを実行すると,以下のような結果が出力されます.

Start                     2018-01-02 00:00:00
End                       2021-04-16 00:00:00
Duration                   1200 days 00:00:00
Exposure Time [%]                     55.9179
Equity Final [$]                      2124.93
Equity Peak [$]                       2248.02
Return [%]                            112.493
Buy & Hold Return [%]                 211.529
Return (Ann.) [%]                     25.7844
Volatility (Ann.) [%]                 25.3374
Sharpe Ratio                          1.01764
Sortino Ratio                         1.93756
Calmar Ratio                         0.920809
Max. Drawdown [%]                    -28.0019
Avg. Drawdown [%]                    -3.60268
Max. Drawdown Duration      314 days 00:00:00
Avg. Drawdown Duration       35 days 00:00:00
# Trades                                   30
Win Rate [%]                               50
Best Trade [%]                        27.7723
Worst Trade [%]                      -9.20929
Avg. Trade [%]                        2.61761
Max. Trade Duration          68 days 00:00:00
Avg. Trade Duration          21 days 00:00:00
Profit Factor                         2.80024
Expectancy [%]                        2.90723
SQN                                   1.99739
_strategy                           MACDCross
_equity_curve                             ...
_trades                       Size  EntryB...
bokeh_plot (10).png
bokeh_plot (11).png
bokeh_plot (12).png
bokeh_plot (13).png
bokeh_plot (14).png

※結果の見方はこちらの記事を参照

結果の一部を抜粋すると,

短期SMAの日数 : 12日
長期SMAの日数 :
26日
シグナル(MACDのSMA) : 9日
資産額 :
1000 → 2124

となっています.

グリッドサーチによる手法最適化

Backtesting.pyには手法を最適化する関数が用意されています.

以下のコードで,グリッドサーチによって最終資産額を最大化するMACDを構成する移動平均線(短期EMA,長期EMA,シグナル)の日数を探索します.手法をmethod=’grid’としています.

#最適化
output2=bt.optimize(n1=range(10, 100, 10),n2=range(10, 300, 10),ns=range(10, 50, 5), maximize='Equity Final [$]', method='grid')
print(output2)
bt.plot()

結果は以下のようになりました.

method : grid
短期SMAの日数x1 : 40日
長期SMAの日数x2 : 50日

シグナル(MACDのSMA) : 25日
最終資産額 :
3521

最終資産額が3.5倍ほどに増えました.

ただし,探索範囲が広すぎるとグリッドサーチといっても全探索ではなくランダムグリッドサーチになるようなので注意が必要です.

Backtesting.pyのドキュメント

ベイズ最適化による手法最適化

ベイズ最適化で最適日数を最適化してみます.

Backtesting.pyではmethodを『skopt』に変えることでベイズ最適化を行うことができるようです(気になる方はBacktesting.pyのドキュメントをご覧ください).

skoptを使うには以下のインストールが必要です.

pip install scikit-optimize

以下でskoptによる最適化を行います.

#最適化
output2=bt.optimize(n1=range(10, 100, 10),n2=range(10, 300, 10),ns=range(10, 50, 5), maximize='Equity Final [$]', method='skopt', max_tries=200)
print(output2)
bt.plot()

結果は以下の通りです.

method : skopt
短期SMAの日数x1 : 29日
長期SMAの日数x2 :
50日
シグナル(MACDのSMA) : 32日
資産額 :
1000
→ 3456

グリッドサーチによりは悪い結果になりましたが,資産は3.4倍に増えました.