投資のためのデータサイエンス

個人の投資活動に役立つデータ分析にまつわる話題を綴ります。

Backtraderライブラリによるバックテスト

Backtraderについては、多くの記事で用いられているものの、こみいっている感じがしたため避けていましたが、今回取り上げることとしました。
BacktraderはPythonでバックテスト・パラメータ最適化・作図などが行えるツールです。戦略の指定は、ライブラリに備わっているクラスを継承する形で行います。このようにオブジェクト指向のツールなので、私のような古株にはとっつきにくい部分があります。また、Jupyter Notebookのような対話型IDEでの利用にはそぐわないという説明も書かれています。そこで今回はVSCodePython仮想環境を作ってそこで動かすという方法をとりました。
まずは、ライブラリのインポート、yfinanceによる株価データの読み込みとBacktraderへの受け渡しです。

import backtrader as bt
import pandas as pd
import numpy as np
from datetime import date, datetime, timedelta
import matplotlib.pyplot as plt
import yfinance as yf
from IPython.display import Image
import warnings
warnings.simplefilter(action="ignore", category=FutureWarning)
# 評価する銘柄
ticker = '6146.T'
# 期間の指定
end_date = datetime.today()
start_date = end_date - timedelta(days=365)

# 株価データ取得
data = yf.download(ticker, start=start_date, end=end_date, interval='1d', multi_level_index=False)
data.index = pd.to_datetime(data.index)
data.columns = ['Open', 'High', 'Low', 'Close', 'Volume']
datafeed = bt.feeds.PandasData(dataname=data)

次に、クラスの継承による戦略の定義です。ここでは移動平均のクロス、MACDのクロスの2つの戦略を定義しています。

class SmaCross(bt.Strategy):
    params = (('short_period', 10), ('long_period', 30),)

    def __init__(self):
        self.sma_short = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.short_period)
        self.sma_long = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.long_period)
        self.crossover = bt.indicators.CrossOver(self.sma_short, self.sma_long)

    def next(self):
        if not self.position:
            if self.crossover > 0:
                self.buy()
                print(f'Buy at {self.datas[0].datetime.date(0)}')
        else:
            if self.crossover < 0:
                self.sell()
                print(f'Sell at {self.datas[0].datetime.date(0)}')

class MACDCrossStrategy(bt.Strategy):
    def __init__(self):
        self.macd = bt.ind.MACD(self.data)
        self.cross = bt.ind.CrossOver(self.macd.macd, self.macd.signal)  # クロス判定

    def next(self):
        if not self.position:  # ポジションを持っていないとき
            if self.cross > 0:  # ゴールデンクロス
                self.buy()
        else:  # ポジションを持っているとき
            if self.cross < 0:  # デッドクロス
                self.sell()

以下はメインプログラムに相当する部分です。戦略はMACDの方を用いています。

# Cerebro実行
cerebro = bt.Cerebro()
cerebro.adddata(datafeed)
cerebro.addstrategy(MACDCrossStrategy)
#cerebro.addstrategy(SmaCross)
cerebro.broker.setcash(1_000_000)
cerebro.broker.setcommission(commission=0.001)

print(f'Starting Portfolio Value: {cerebro.broker.getvalue():,.2f}')
cerebro.run()
print(f'Final Portfolio Value: {cerebro.broker.getvalue():,.2f}')

# グラフ出力
chart_file = 'result.png'
cerebro.plot(style='candlestick')[0][0].savefig(chart_file, dpi=300, bbox_inches='tight')
Image(open(chart_file, 'rb').read())

(以下はターミナル出力)
Starting Portfolio Value: 1,000,000.00
Final Portfolio Value: 1,004,351.25


Backtraderには他にもさまざまな機能が利用可能なようです。今後取り上げていきたいと思います。