本ブログでは主として株式投資に役立つデータ分析を扱っていますが、株価データをもとに株式の売買戦略を決めるにあたっては、その戦略のパフォーマンスの評価が重要になります。過去の株価データを用いてその売買戦略を適用した結果どのような損益になるかを評価する手法がバックテストです。
Pythonでバックテストを行えるツールはいくつかあるようですが、最も有名なものは"Backtesting.py"ライブラリでしょう。これに関しては海外のみならず日本語の記事も非常に多くあります。ここでは"Backtesting.py"ライブラリを用いた基本的なバックテストの方法について述べます。
まず必要なライブラリをインポートします。
import pandas as pd import yfinance as yf import talib as ta from datetime import datetime, timedelta import numpy as np from pandas_datareader import data as pdr from backtesting import Backtest, Strategy from backtesting.lib import crossover from backtesting.test import SMA, GOOG, EURUSD import warnings #警告をコントロールするライブラリを追加 warnings.filterwarnings("ignore") #警告を非表示に設定
次に、日本の株価データを取得します。
# Define the stock symbol and timeframe symbol = '9432.T' # 9432はNTT end_date = datetime(2023, 9, 30, 0, 0, 0) start_date = end_date - timedelta(days=1800) # 5 years before today yf.pdr_override() # yahooサイトからデータをダウンロード stock_data = pdr.get_data_yahoo(symbol, start_date, end_date)
次に必要になるのは取引戦略のクラスの作成です。これはBacktesting.pyに備わっているStrategyというクラスを継承する形で記述します。クラスの中には指標を定義する関数init()と、売買戦略を定義する関数next()を記述します。ここでは指標の計算にTA-Libライブラリを用いていますが、自分で関数を定義することもできます。ここでの売買戦略は、長短移動平均のゴールデンクロスで買い、買いポジションがある時のデッドクロスで売りです。空売りは行わないので、売りについてはsell()でなくposition_close()を用いています。
class TalibCross(Strategy): tp1 = 10 tp2 = 20 def init(self): close = self.data.Close self.sma1 = self.I(ta.SMA, close, self.tp1) self.sma2 = self.I(ta.SMA, close, self.tp2) def next(self): if crossover(self.sma1, self.sma2): self.buy() elif crossover(self.sma2, self.sma1): self.position.close()
次に、バックテスト実行のためのBacktestインスタンスを生成します。
bt = Backtest(stock_data, TalibCross, cash=1000000)
そして、run()で生成したインスタンスを実行し、結果を出力します(出力結果は一部のみ)。
output = bt.run()
print(output)
plot()メソッドでインタラクティブなチャートが表示されます。
bt.plot()
もう一つの重要な機能が「最適化」です。移動平均の窓の長さや(0-100という値をとる指標について)しきい値といったパラメータの値を動かして、目的関数を最適化するパラメータ値を探ります。以下では2つの移動平均のウィンドウの長さを動かして、出力される指標の一つである'Sharpe Ratio'を最大にするパラメータ値を探索しています。constraint=で探索時の制約条件を設けています。
output = bt.optimize( tp1 = range(4, 20, 1), tp2 = range(14, 30, 1), constraint=lambda p: p.tp1 < p.tp2, maximize = 'Sharpe Ratio') print(output)
以下では最適化されたパラメータの値を確認しています。
print(output._strategy)
バックテストによるチューニングが将来の儲けには必ずしも結びつくわけではありませんが、売買戦略を評価するための強力なツールであるとはいえます。