Backtrader入門 – 本格バックテストフレームワークの使い方

Python実装・コード

Backtraderは本格的なバックテストが行えるPythonフレームワークです。複雑な戦略・複数銘柄・リスク管理まで対応しており、プロの個人投資家にも使われています。

Backtraderのインストール

pip install backtrader matplotlib

基本的なバックテストの構成

import backtrader as bt
import yfinance as yf
import pandas as pd
import datetime

# yfinanceからデータを取得してBacktrader用に変換
def get_data_for_bt(ticker, period="3y"):
    df = yf.download(ticker, period=period, progress=False, auto_adjust=True)
    df.index = pd.to_datetime(df.index)
    data = bt.feeds.PandasData(dataname=df)
    return data

シンプルな移動平均クロス戦略

class MACrossStrategy(bt.Strategy):
    params = (
        ("short_period", 25),
        ("long_period", 75),
        ("stake", 100),  # 取引単位(株数)
    )
    
    def __init__(self):
        # 移動平均線を定義
        self.ma_short = bt.ind.SMA(self.data.close, period=self.params.short_period)
        self.ma_long = bt.ind.SMA(self.data.close, period=self.params.long_period)
        # クロスシグナル
        self.crossover = bt.ind.CrossOver(self.ma_short, self.ma_long)
    
    def next(self):
        if not self.position:  # ポジションなし
            if self.crossover > 0:  # ゴールデンクロス
                self.buy(size=self.params.stake)
                print(f"BUY: {self.data.datetime.date(0)} @ {self.data.close[0]:.0f}")
        else:  # ポジションあり
            if self.crossover < 0:  # デッドクロス
                self.sell(size=self.params.stake)
                print(f"SELL: {self.data.datetime.date(0)} @ {self.data.close[0]:.0f}")

# バックテストを実行
def run_backtest(ticker, initial_cash=1_000_000):
    cerebro = bt.Cerebro()
    
    # データを追加
    data = get_data_for_bt(ticker)
    cerebro.adddata(data)
    
    # 戦略を追加
    cerebro.addstrategy(MACrossStrategy, short_period=25, long_period=75)
    
    # 初期資金を設定
    cerebro.broker.setcash(initial_cash)
    
    # 手数料を設定(0.1%)
    cerebro.broker.setcommission(commission=0.001)
    
    # 分析ツールを追加
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name="sharpe", riskfreerate=0.01)
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown")
    cerebro.addanalyzer(bt.analyzers.Returns, _name="returns")
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="trades")
    
    print(f"初期資金: {cerebro.broker.getvalue():,.0f}円")
    results = cerebro.run()
    print(f"最終資金: {cerebro.broker.getvalue():,.0f}円")
    
    # 結果を表示
    strat = results[0]
    sharpe = strat.analyzers.sharpe.get_analysis().get("sharperatio", 0)
    mdd = strat.analyzers.drawdown.get_analysis()["max"]["drawdown"]
    total_return = strat.analyzers.returns.get_analysis()["rtot"] * 100
    trades = strat.analyzers.trades.get_analysis()
    
    print(f"\n=== バックテスト結果 ===")
    print(f"総リターン: {total_return:.1f}%")
    print(f"シャープレシオ: {sharpe:.2f}" if sharpe else "シャープレシオ: N/A")
    print(f"最大ドローダウン: {mdd:.1f}%")
    if trades.get("total"):
        total = trades["total"]["total"]
        won = trades.get("won", {}).get("total", 0)
        print(f"取引回数: {total}, 勝率: {won/total:.1%}" if total > 0 else "")
    
    cerebro.plot(style="candlestick", volume=False)
    return results

run_backtest("7203.T")

RSI戦略の実装

class RSIStrategy(bt.Strategy):
    params = (
        ("rsi_period", 14),
        ("rsi_overbought", 70),
        ("rsi_oversold", 30),
        ("stake_pct", 0.95),  # 資金の95%を使用
    )
    
    def __init__(self):
        self.rsi = bt.ind.RSI(self.data.close, period=self.params.rsi_period)
    
    def next(self):
        cash = self.broker.getcash()
        price = self.data.close[0]
        size = int((cash * self.params.stake_pct) / price)
        
        if not self.position:
            if self.rsi[0] < self.params.rsi_oversold:
                if size > 0:
                    self.buy(size=size)
        else:
            if self.rsi[0] > self.params.rsi_overbought:
                self.close()

def compare_strategies(ticker):
    """複数戦略を比較"""
    strategies = [
        ("MA Cross", MACrossStrategy, {}),
        ("RSI", RSIStrategy, {}),
    ]
    
    for name, strategy, params in strategies:
        cerebro = bt.Cerebro()
        cerebro.adddata(get_data_for_bt(ticker))
        cerebro.addstrategy(strategy, **params)
        cerebro.broker.setcash(1_000_000)
        cerebro.broker.setcommission(0.001)
        cerebro.addanalyzer(bt.analyzers.Returns, _name="returns")
        results = cerebro.run()
        ret = results[0].analyzers.returns.get_analysis()["rtot"] * 100
        print(f"{name}: {ret:.1f}%")

compare_strategies("7203.T")

パラメーター最適化

def optimize_strategy(ticker):
    """パラメーターを最適化"""
    cerebro = bt.Cerebro()
    cerebro.adddata(get_data_for_bt(ticker))
    
    # 最適化:short_periodとlong_periodの全組み合わせをテスト
    cerebro.optstrategy(
        MACrossStrategy,
        short_period=range(10, 50, 10),
        long_period=range(50, 200, 25)
    )
    
    cerebro.broker.setcash(1_000_000)
    cerebro.broker.setcommission(0.001)
    cerebro.addanalyzer(bt.analyzers.Returns, _name="returns")
    
    results = cerebro.run()
    
    # 結果をDataFrameに整理
    opt_results = []
    for run in results:
        strat = run[0]
        ret = strat.analyzers.returns.get_analysis()["rtot"] * 100
        opt_results.append({
            "short": strat.params.short_period,
            "long": strat.params.long_period,
            "return(%)": round(ret, 1)
        })
    
    df = pd.DataFrame(opt_results).sort_values("return(%)", ascending=False)
    print("Top10パラメーター:")
    print(df.head(10))
    return df

optimized = optimize_strategy("7203.T")

まとめ

Backtraderは手数料・ポジション管理・複数銘柄に対応した本格的なバックテストフレームワークです。パラメーター最適化機能も充実しており、プロフェッショナルな戦略検証が可能です。

タイトルとURLをコピーしました