ボリンジャーバンドによる株式売買をバックテストする(TA-Lib&Backtesting.py)

4月 29, 2021

今回は,ボリンジャーバンドを利用した売買をバックテストしてみます.

バックテストとは,過去のデータを用いて手法の妥当性をテストすることをいいます.

バックテストには,Pythonライブラリ『Backtesting.py』を用います.Backtesting.pyの使い方は,以下の記事をご覧ください.

【Python】Backtesting.pyで株取引をバックテストして戦略を最適化

また,ボリンジャーバンドについては以下の記事をご覧ください.

ボリンジャーバンドとは?テクニカル分析には欠かせない基礎指標です

TA-LibとBacktesting.pyのインストール

今回使用する主なライブラリは,TA-LibとBacktesting.pyです.

Backtesting.pyは,pipでインストールできます.

pip install backtesting

TA-Libのインストールは少し厄介です.以下の記事を参考にインストールしてください.

TA-Libをインストールする方法

今回使用するその他のライブラリは,基本的にはpipでインストールできると思いますので必要に応じてインストールしてください.

ボリンジャーバンドによる売買手法のバックテスト

データの取得

とりあえず,2018年1月1日~現在のApple(AAPL)の株価でバックテストを行います.

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をインストールして株価データを取得する方法【日本株・米国株】

株価時系列データを取得してCSVファイルに保存する方法と呼び出す方法

ボリンジャーバンドによる売買

まず,ボリンジャーバンドをTA-Libで計算する関数を作ります.

import talib as ta
 
def BB(close, n, nu, nd):
    upper, middle, lower = ta.BBANDS(close, timeperiod=n, nbdevup=nu, nbdevdn=nd, matype=0)
    return upper, lower

また,Backtesting.py用にボリンジャーバンドによる売買クラスを作ります.

今回行う戦略は単純で,+3σバンドを上回れば売り,-3σバンドを下回れば買いを入れます.ボリンジャーバンドを形成する移動平均は25日とします.

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

class BBsigma(Strategy):
    n = 25 #移動平均日数
    nu = 3 #何σか
    nd = 3 #何σか
 
    def init(self):
        self.upper, self.lower = self.I(BB, self.data.Close, self.n, self.nu, self.nd)
 
    def next(self): # チャートデータの行ごとに呼び出される
         #+3σより大きいなら売り
        if self.data.Close > self.upper:
            self.position.close()
        #-3σより小さいなら買い
        elif self.data.Close < self.lower:
            self.buy() # 買い

バックテストを実行

# バックテストを設定
bt = Backtest(
    data, # チャートデータ
    BBsigma, # 売買戦略
    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-21 00:00:00
Duration                   1205 days 00:00:00
Exposure Time [%]                     26.3538
Equity Final [$]                      1750.66
Equity Peak [$]                       1750.66
Return [%]                            75.0663
Buy & Hold Return [%]                 209.997
Return (Ann.) [%]                     18.5089
Volatility (Ann.) [%]                 26.2425
Sharpe Ratio                         0.705302
Sortino Ratio                         1.29385
Calmar Ratio                         0.744809
Max. Drawdown [%]                    -24.8505
Avg. Drawdown [%]                    -3.38444
Max. Drawdown Duration       64 days 00:00:00
Avg. Drawdown Duration       11 days 00:00:00
# Trades                                    4
Win Rate [%]                               50
Best Trade [%]                        54.6309
Worst Trade [%]                      -5.52182
Avg. Trade [%]                        15.4658
Max. Trade Duration         155 days 00:00:00
Avg. Trade Duration          78 days 00:00:00
Profit Factor                         8.65117
Expectancy [%]                        17.9514
SQN                                   1.19919
_strategy                             BBsigma
_equity_curve                             ...
_trades                      Size  EntryBa...
bokeh_plot (12).png
bokeh_plot (13).png
bokeh_plot (14).png
bokeh_plot (15).png

4回の取引で資産が1000 → 1750(+75%)となりました.

他の銘柄で試してみる

右肩上がりの銘柄では手法によらずうまくいってしまいそうなので,他の銘柄でもやってみます.

スチールダイナミクス(STLD)

import pandas_datareader.data as web
import datetime
 
start = datetime.date(2018,1,1)
end = datetime.date.today()
data = web.DataReader('STLD', 'yahoo', start, end)

実行結果

Equity Final [$] 1258.02

bokeh_plot (18).png

IBM(IBM)

import pandas_datareader.data as web
import datetime
 
start = datetime.date(2018,1,1)
end = datetime.date.today()
data = web.DataReader('IBM', 'yahoo', start, end)

実行結果

Equity Final [$] 1062.76

bokeh_plot (22).png

スチールダイナミクス,IBMでもプラスとなりました.

感想

Appleのようなマクロに上がっている株でも,スチールダイナミクスやIBMのような上がっていない株でも,ボリンジャーバンドによる売買で利益が出ることが確認できました.

しかし,利益は出やすいですがApple株でも+75%と,増加額は小さめです.

例えば,MACDによるApple株の売買のバックテストでは+112~250%程度のリターンが出力されました.

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

今回行ったボリンジャーバンドによる売買では,±3σを判定バンドとしたため,取引回数が少なかったことが原因であると考えられます.

そこで,判定バンドを±2σで行ってみたところ,以下のような結果が得られました.

Apple

Equity Final [$] 1361.76

Return [%] 36.1757

スチールダイナミクス

Equity Final [$] 754.746

Return [%] -24.5254

IBM

Equity Final [$] 1064.73

Return [%] 6.47287

なんと,Appleでのリターンは下がってしまい,スチールダイナミクスに至ってはマイナスになってしまいました.IBMでは±3σのときよりも良くなりましたが,微増です.

ボリンジャーバンドによる売買は安定的である一方,それだけでは大きく儲けを出せない感じですね.ボリンジャーバンドはマクロな視点で参考にするのが良さそうです.

まとめコード

 
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 BB(close, n, nu, nd):
    upper, middle, lower = ta.BBANDS(close, timeperiod=n, nbdevup=nu, nbdevdn=nd, matype=0)
    return upper, lower

class BBsigma(Strategy):
    n = 25 #移動平均日数
    nu = 3 #何σか
    nd = 3 #何σか
 
    def init(self):
        self.upper, self.lower = self.I(BB, self.data.Close, self.n, self.nu, self.nd)
 
    def next(self): # チャートデータの行ごとに呼び出される
         #+3σより大きいなら売り
        if self.data.Close > self.upper:
            self.position.close()
        #-3σより小さいなら買い
        elif self.data.Close < self.lower:
            self.buy() # 買い
 
# バックテストを設定
bt = Backtest(
    data, # チャートデータ
    BBsigma, # 売買戦略
    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() # 実行結果(グラフ)