見出し画像

【Python】バックテストを行う

「移動平均線がゴールデンクロスしたときに買う」など、一定の売買ルールに従って取引を行った場合の収支を、過去のデータを使って検証することをバックテストといいます。

バックテストを行うことで、移動平均線の日数やテクニカル指標の設定値などのパラメータについて、その有効性を確認したり最適値を求めたりすることもできます。

ここでは、トヨタの株価データを取得し、簡単なトレードルールに従った場合の収支をバックテストし、更に売買ルールのパラメータを最適化してみます。

なお、最低限のポイントのみの説明にするため、Pythonライブラリ、モジュール等のインストール方法については割愛させて頂きます。お使いのPC環境等に合わせてインストールしてもらえればと思います。


1.株価データを取得する

下記を参考にOHLCV(始値 / 高値 / 安値 / 終値 / 出来高)形式のトヨタ株(7203.JP)データを取得します。データの取得期間は、2022年10月1日から現在の日付までです。

import pandas_datareader.data as web
import datetime

start = '2022-10-01'
end = datetime.date.today()
code = '7203.JP' # トヨタ

data = web.DataReader(code, 'stooq', start, end)

# 日付を昇順に並び替える
data.sort_index(inplace=True)


2.ライブラリをインポートする

まず、backtesting ライブラリから、Strategyクラス、Backtestクラス、backtesting.libからcrossoverクラス、backtesting.testからSMAクラスをインポートします。

Strategyクラスは、トレード戦略を実装するための基本クラスです。トレードのエントリーとエグジットの条件などを定義します。独自の戦略を作成する場合は、Strategyクラスをサブクラス化してカスタム戦略を実装します。

Backtestクラスは、バックテストの実行を管理するための主要なクラスです。バックテストの期間、データ、戦略、手数料などのパラメータを設定し、実際のバックテストを実行します。

from backtesting import Strategy
from backtesting.lib import crossover
from backtesting.test import SMA
from backtesting import Backtest


3.売買ルールを作成する

バックテストを実行する際には、先にバックテストで使う売買のルールを定義します。そのために、売買のルールを管理する Strategy クラスを継承したクラスを作成します。ここでは、ゴールデンクロスで買い、デッドクロスで売るルールを定義しています。短期移動平均の設定日数は5日、長期移動平均の設定日数は25日としています。

class SmaCross(Strategy):
    ns = 5 # 短期移動平均日数
    nl = 25 # 長期移動平均日数

    def init(self):
        # 短期移動平均
        self.smaS = self.I(SMA, self.data['Close'], self.ns)
        # 長期起動平均
        self.smaL = self.I(SMA, self.data['Close'], self.nl)

    def next(self): # チャートデータの行ごとに呼び出される
        # smaS > smaL で買う
        if crossover(self.smaS, self.smaL):
            self.buy() # 買い
        # smaS < smaL で売る
        elif crossover(self.smaL, self.smaS):
            self.position.close() # 売り


4.バックテストを実行する

売買のルールを定義した後は、バックテストを行います。バックテストの実行は、Backtest クラスで行います。まずは Backtest クラスのインスタンスを生成し、run()メソッドで実行します。更に、plot()メソッドで実行結果をグラフ表示します。

# バックテストを設定。バックテストクラスを初期化してインスタンスを生成
bt = Backtest(
    data, # チャートデータ
    SmaCross, # 売買戦略
    # cash=1000, # 最初の所持金
    trade_on_close=True # True:現在の終値で取引,False:次の時間の始値で取引
)

# run()メソッドでバックテストを実行
result = bt.run()

# 実行結果のデータを表示
print(result)

# plot()メソッドを実行して実行結果をグラフで表示
bt.plot()


バックテストの結果のおもな項目

  • Equity Final [$]:最終的な資産

  • Equity Peak [$]:利益が最大時の資産

  • Return [%]:利益率

  • # Trades:トレードの回数

  • Win Rate [%]:勝率

  • Best Trade [%]:最も成績のよかったトレードの利益率

  • Worst Trade [%]:最も成績の悪かったトレードの利益率

  • Avg. Trade [%]:トレードの平均の利率

  • SQN [%]:トレード数の平方根×平均損益 / 標準偏差で計算されるシステムの品質スコア


5.パラメータを最適化する

バックテストの結果を使って、さらによい結果を出すためにパラメータの最適化を行います。ここでのパラメータの最適化とは、移動平均線の日数を最適にすることを指します。パラメータの最適化は、Backtest クラスの optimize メソッドで行います。

# optimize()メソッドを実行して最適化
result = bt.optimize(
    ns=range(5, 25, 5),  # 短期移動平均日数
    nl=range(5, 75, 5),  # 長期移動平均日数
    maximize= 'Return [%]',  #※maximizeはデフォルトではSQN(System Quality Number)です
    constraint=lambda r: r.ns < r.nl  # consraintオプションを付けることで短期SMAが長期SMAより長くなることを禁止できます
)

# 実行結果のデータを表示
print(result)

# plot()メソッドを実行して実行結果をグラフで表示
bt.plot()

maxmizeには、前項でバックテストを実行した結果の項目から最適化したい項目を指定します。指定しない場合は、SQNスコアが最適化されます。constraint では、売買のルールを定義したクラスが引数として渡されるので、lambda の形式で渡されたクラスのメンバを使って条件式を作成します。



6.任意の取引のルールでバックテストを行う

バックテストとパラメータの最適化は、ライブラリ内の SMA クラスを利用したものでした。ここでは、これまでに利用してきたテクニカル指標を使ったバックテストとパラメータの最適化を行っていきます。

6.1 RSIを用いた取引ルールでのバックテスト

①RSIを算出する関数を定義

def RSI(close, n1, n2):
    # RSIを算出
    rsiS = ta.RSI(close, timeperiod=n1)  # 短期 n1日
    rsiL = ta.RSI(close, timeperiod=n2)  # 長期 n2日
    return rsiS, rsiL

②Strategyクラスを継承したRSICrossクラスを定義

class RSICross(Strategy):
    ns = 14 # 短期
    nl = 28 # 長期

    def init(self):
        # rsiS / rsiL
        self.rsiS, self.rsiL = self.I(RSI, self.data['Close'], self.ns, self.nl)


    def next(self): # チャートデータの行ごとに呼び出される
        # rsiS > rsiL で買う
        if crossover(self.rsiS, self.rsiL):
            self.buy() # 買い
        # rsiS < rsiL で売る
        elif crossover(self.rsiL, self.rsiS):
            self.position.close() # 売り

③バックテストクラスのインスタンス化とバックテストの実行

# バックテストを設定。バックテストクラスを初期化してインスタンスを生成
bt = Backtest(
    data, # チャートデータ
    RSICross, # 売買戦略
    # cash=1000, # 最初の所持金
    trade_on_close=True # True:現在の終値で取引,False:次の時間の始値で取引
)

# run()メソッドでバックテストを実行
result = bt.run()

# 実行結果のデータを表示
print(result)

# plot()メソッドを実行して実行結果をグラフで表示
bt.plot()

④パラメータの最適化

# optimize()メソッドを実行して最適化
result = bt.optimize(
    ns=range(5, 25, 5),  # 短期
    nl=range(5, 75, 5),  # 長期
    maximize= 'Return [%]',  #※maximizeはデフォルトではSQN(System Quality Number)です
    constraint=lambda r: r.ns < r.nl  # consraintオプションを付けることで短期rsiが長期rsiより長くなることを禁止できます
)

# 実行結果のデータを表示
print(result)

# plot()メソッドを実行して実行結果をグラフで表示
bt.plot()


6.2 MACDを用いた取引ルールでのバックテスト

①MACDを算出する関数を定義

def MACD(close, n1, n2, n3):
    # MACD、シグナル、ヒストグラムを算出
    macd, macdsignal, _ = ta.MACD(close, fastperiod=n1, slowperiod=n2, signalperiod=n3)
    return macd, macdsignal

②Strategyクラスを継承したMADCCrossクラスを定義

class MACDCross(Strategy):
    n1 = 12 # fastperiod
    n2 = 26 # slowperiod
    n3 = 9 # signalperiod

    def init(self):
        # macd, macdsignal
        self.macd, self.macdsignal = self.I(MACD, self.data['Close'], self.n1, self.n2, self.n3)


    def next(self): # チャートデータの行ごとに呼び出される
        # macd > macdsignal で買う
        if crossover(self.macd, self.macdsignal):
            self.buy() # 買い
        # macd < macdsignal で売る
        elif crossover(self.macdsignal, self.macd):
            self.position.close() # 売り

③バックテストクラスのインスタンス化とバックテストの実行

# バックテストを設定。バックテストクラスを初期化してインスタンスを生成
bt = Backtest(
    data, # チャートデータ
    MACDCross, # 売買戦略
    # cash=1000, # 最初の所持金
    trade_on_close=True # True:現在の終値で取引,False:次の時間の始値で取引
)

# run()メソッドでバックテストを実行
result = bt.run()

# 実行結果のデータを表示
print(result)

# plot()メソッドを実行して実行結果をグラフで表示
bt.plot()

④パラメータの最適化

# optimize()メソッドを実行して最適化
result = bt.optimize(
    n1=range(5, 55, 5),  # fastperiod
    n2=range(10, 75, 5),  # slowperiod
    n3=range(10, 75, 5),  # signalperiod
    maximize= 'Return [%]',  #※maximizeはデフォルトではSQN(System Quality Number)です
    constraint=lambda r: r.n1 < r.n2  # consraintオプションを付けることでfastperiodがslowperiodより長くなることを禁止できます
)

# 実行結果のデータを表示
print(result)

# plot()メソッドを実行して実行結果をグラフで表示
bt.plot()


backtesting ライブラリを用いて、バックテストを実行することができました。optimize()メソッドを使えば、パラメータの最適化が容易に行えるので、
Pythonでのトレード戦略の開発と評価が可能な強力なツールになりそうです。


この記事が気に入ったらサポートをしてみませんか?