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

6月 21, 2021

以前,2本のSMAによるゴールデンクロス,デッドクロスでの売買手法をバックテストしました.バックテストとは,「その手法で取引したら儲けは出るか?」を過去のデータを使って検証することです.

今回は,MACD手法による売買をPythonでバックテストしてみたいと思います.

バックテストの方法

Pythonでバックテストを行う方法はいくつかあると思いますが,ここでは『Backtesting.py』というライブラリを用いてバックテストを行います.

Backtesting.pyでは,戦略を最適化する機能(例えば,リターンが最大になるように移動平均線の日数を変化させる)もあるので,今回はそちらも試してみます.

Backtesting.pyのインストール

pip install backtesting

でできます.

株価データの取得

まずは,バックテストを行うための株価を取得しましょう.

今回は,株価の取得にはpandas-datareaderを用います.もちろんウェブやローカルにあるCSVファイルを読み込むなどでも構いません(というか,そちらの方が好ましい).

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

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)

ここに関しては,Pythonのpandas datareaderをインストールして株価データを取得する方法【日本株・米国株】を参考にして好きな銘柄の株価を取得して下さい.

pandas-datareaderで取得したデータは以下のように,High(高値),Low(低値),Open(始値),Close(終値),Volume(出来高),Adj Close(調整後終値)を持つDataFarameとなっています.

                  High         Low        Open       Close       Volume   Adj Close
Date
2018-01-02   43.075001   42.314999   42.540001   43.064999  102223600.0   41.380238
2018-01-03   43.637501   42.990002   43.132500   43.057499  118071600.0   41.373032
2018-01-04   43.367500   43.020000   43.134998   43.257500   89738400.0   41.565216
2018-01-05   43.842499   43.262501   43.360001   43.750000   94640000.0   42.038452
2018-01-08   43.902500   43.482498   43.587502   43.587502   82271200.0   41.882305
...                ...         ...         ...         ...          ...         ...
2021-04-05  126.160004  123.070000  123.870003  125.900002   88651200.0  125.900002
2021-04-06  127.129997  125.650002  126.500000  126.209999   80171300.0  126.209999
2021-04-07  127.919998  125.139999  125.830002  127.900002   83466700.0  127.900002
2021-04-08  130.389999  128.520004  128.949997  130.360001   88844600.0  130.360001
2021-04-09  133.039993  129.470001  129.800003  133.000000  106513800.0  133.000000

Backtesting.pyを使用するには,データが"Open", “High", “Low", “Close"のカラムを持つ必要があります.pandas-datareaderで取得した場合はすでにこれらのカラムを持っているので問題ないですね.

Backtesting.pyでバックテスト

まずは,Backtesting.pyを動かすのに必要なモジュールをインポートしましょう.

from backtesting import Backtest, Strategy 
from backtesting.lib import crossover

売買手法(MACD)を作成

今回は,MACDによる売買を行います.

MACDの計算関数

MACDとそのシグナル(SMA)は,TA-Libで計算します.

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

もちろん,自力でMACDを計算しても構いません.MACDの説明はこちらを参照してください.

TA-Libによるテクニカル指標の計算は,以下の記事をご覧ください.

PythonライブラリTA-Libで色々なテクニカル指標を使ってみる

MACDによる売買(ゴールデンクロス,デッドクロス)

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() # 売り

今回はMACDの短期EMAを12日,長期EMAを26日とし,シグナルを9日としてみました.

バックテストを実行

# バックテストを設定
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() # 実行結果(グラフ)

所持金は株価よりも多く設定しておかないと購入できずに終わりますから,適切な値にしましょう.

取引手数料は楽天証券の米国株式手数料の0.495%としました.

結果

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

資産額1000 → 2124,リターン+112%というそこそこ良い結果がでました.

しかし,Appleの株はこんなもんじゃないということで,MACDを最適化してみます.

売買手法を最適化

MACDやそのシグナルの期間を変えることで,最終資産額が大きくなるように最適化してみたいと思います.

以下のように,MACDの短期EMAを10~100日,長期EMAを10~300日,シグナルを2~50日で変化させ最適化します.最適化手法にはskoptを使います.

#最適化
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()

最適化結果

Start                     2018-01-02 00:00:00
End                       2021-04-16 00:00:00
Duration                   1200 days 00:00:00
Exposure Time [%]                     54.9517
Equity Final [$]                      3521.92
Equity Peak [$]                        3648.8
Return [%]                            252.192
Buy & Hold Return [%]                 211.529
Return (Ann.) [%]                     46.6936
Volatility (Ann.) [%]                 29.6495
Sharpe Ratio                          1.57485
Sortino Ratio                         3.74247
Calmar Ratio                          2.83619
Max. Drawdown [%]                    -16.4635
Avg. Drawdown [%]                    -2.67232
Max. Drawdown Duration      175 days 00:00:00
Avg. Drawdown Duration       21 days 00:00:00
# Trades                                   11
Win Rate [%]                          72.7273
Best Trade [%]                        71.0948
Worst Trade [%]                      -3.82849
Avg. Trade [%]                        12.3665
Max. Trade Duration         161 days 00:00:00
Avg. Trade Duration          59 days 00:00:00
Profit Factor                         17.9512
Expectancy [%]                        14.4032
SQN                                   1.88405
_strategy                 MACDCross(n1=40,...
_equity_curve                             ...
_trades                       Size  EntryB...
bokeh_plot (8).png
bokeh_plot (9).png
bokeh_plot (10).png
bokeh_plot (11).png
bokeh_plot (12).png

資産額1000 → 3521,リターン+252%となりました.さすがApple株ですね.

最適化したMACDでは,短期EMA=40日,長期EMA=50日,シグナル=25日で,取引回数は先ほどの30回から11回に減っています.

やはりAppleのような長期的に伸びた株に対しては,最初に買ってずっと持っていたほうがよかったですからね.それに近くなっているということでしょう.

まとめコード


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() # 実行結果(グラフ)

#最適化
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()

投資に関する免責事項
プログラムや考え方の情報の提供を目的としており,投資勧誘を目的とするものではありません.また,この記事は投資成績を保証するものではありません.投資はあくまで自己責任でお願いします.