【Python】MACDをバックテストしてみた


以前，Backtesting.pyを使って2本のSMAのゴールデンクロス，デッドクロスによる売買をバックテストしてみました．

https://qiita.com/Fujinoinvestor/items/f2bdaabb766db443ddc0

今回は，[MACDによる売買](https://optrip.xyz/?p=2460)をバックテストしてみます．

分からないところがあれば，[以前の記事](https://qiita.com/Fujinoinvestor/items/f2bdaabb766db443ddc0)もしくは，もう少しだけ詳しく書いた[こちらの記事](https://optrip.xyz/?p=3346)を見てください．

また，まとめコード（実行すれば動くはずのコード）は本記事の一番下にあります．

#株価データの取得

今回は，2018/1/1~現在までのAAPL（Apple）の株価で行いたいと思います．

```python
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)
```

#MACDの計算
TA-Libを用います．詳しくは[こちらの記事](https://qiita.com/Fujinoinvestor/items/9314c404f591f8df53d4)をご覧ください．

```python
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による売買

とりあえずは，

短期EMA : 12日
長期EMA : 26日
シグナル（MACDのSMA） : 9日

でやってみます．

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

#バックテスト実行

```python
# バックテストを設定
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/857e0164-a358-df2a-bb87-dc2519b4555f.png)
![bokeh_plot (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/75f34a45-fdf3-8643-aa3b-120d072b5eca.png)
![bokeh_plot (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/6ec3159d-052d-422b-f312-51ed16dbf87a.png)
![bokeh_plot (13).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/f9a4f755-26bc-2774-3306-847244e57968.png)
![bokeh_plot (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/98662ed2-c09d-9a90-06fb-710a6e2d6517.png)



資産額1000 → 2124，リターン+112%という結果がでました．

しかし，Appleの株はこんなもんじゃないということで，手法を最適化してみます．

#手法最適化

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

```python
#最適化
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/fe02a979-902a-e9cf-5c26-0b2823144e29.png)
![bokeh_plot (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/7158c1eb-6d20-ae60-a922-7a2e811a0989.png)
![bokeh_plot (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/d2167f7f-7a81-fb94-e8aa-c1ef7b54ecd1.png)
![bokeh_plot (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/26cedc25-d59d-ae1e-3fb8-12478128b141.png)
![bokeh_plot (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1298289/efb1632b-33bb-1f77-f1af-4d973f5be085.png)



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

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


#他の銘柄でもやってみた
Appleがいいのか，MACDで売買するのがいいのかイマイチ分からないので，他の銘柄でもやってみます．ただし，最適化はなしで全て以下の条件で行います(先ほどと同じです)．

短期EMA：12日
長期EMA：26日
シグナル（MACDのSMA）：9日

ただし，日本株の場合は最初の所持金を100倍の100000にします（1ドル=100円と仮定）．
他の条件に付いては同じです．

ソースコードは省略し，結果の一部分だけ紹介します．


#テスラ(TSLA)
```
Equity Final [$]                      5785.32
Equity Peak [$]                       6173.32
Return [%]                            478.532
Buy & Hold Return [%]                 1053.99
Return (Ann.) [%]                     70.6132
```
資産：1000 → 5785
強いですね．

#日経平均株価
```
Equity Final [$]                       113223
Equity Peak [$]                        119930
Return [%]                            13.2232
Buy & Hold Return [%]                 26.2867
Return (Ann.) [%]                      3.9794
```
資産：100000 → 113223
日経でもプラスとなりました．

#キヤノン(7751)
```
Equity Final [$]                        55815
Equity Peak [$]                        101633
Return [%]                            -44.185
Buy & Hold Return [%]                 -38.131
Return (Ann.) [%]                    -16.4616
```
資産：100000 → 55815
さすがに2018年からのキヤノンはMACDでも厳しいですね．

結局，長期的に弱い株はどんな手法を使っても駄目だということですね．銘柄選定が大事なわけです．MACDの効果を知るには，資産額ではない別の指標で比べる必要があります．


#まとめコード

```python

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()

```


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