Pythonで株価バックテストを実装する方法【初心者向け・移動平均クロス戦略】

Uncategorized

※本記事には広告・アフィリエイトリンクが含まれます。収益はサイト運営・検証費用に充てています。

結論から言うと:

  • バックテストとは過去データを使って投資戦略の有効性を検証する手法で、実際の資金を使う前にリスクを把握できる仕組みです
  • 移動平均クロス戦略をPythonで実装するのに必要なコードは50行程度で書ける
  • 手数料・スリッページを考慮しないと過大評価になるため、実際の検証では必ず含める必要がある

バックテストとは(なぜ必要か)

バックテストとは、ある投資戦略を過去の価格データに当てはめて、仮に運用していたらどうなったかをシミュレーションする手法です。率直に言うと、バックテストなしで実際の資金を投じるのはリスクが高い。過去の成績が将来を保証するものではありませんが、少なくとも戦略の基本的な特性(勝率・ドローダウンなど)を把握できる仕組みです。

移動平均クロス戦略のルール

今回実装する戦略のルールは以下の通りです。

  • ゴールデンクロス(短期MA が長期MA を上抜け)→ 買いエントリー
  • デッドクロス(短期MA が長期MA を下抜け)→ 全量売り・ポジションクローズ
  • 設定:短期5日MA、長期25日MA

実装コード

import yfinance as yf
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

# データ取得
df = yf.download("7203.T", period="3y")
df = df[['Close']].copy()

# 移動平均計算
df['MA5'] = df['Close'].rolling(5).mean()
df['MA25'] = df['Close'].rolling(25).mean()

# シグナル生成
df['Signal'] = 0
df.loc[df['MA5'] > df['MA25'], 'Signal'] = 1  # ゴールデンクロス(ロング)
df.loc[df['MA5'] < df['MA25'], 'Signal'] = 0  # デッドクロス(現金)

# ポジション変化
df['Position'] = df['Signal'].shift(1)

# 日次リターン計算
df['Return'] = df['Close'].pct_change()
df['Strategy'] = df['Return'] * df['Position']

# 累積リターン
df['CumMarket'] = (1 + df['Return']).cumprod()
df['CumStrategy'] = (1 + df['Strategy']).cumprod()

print(df[['CumMarket', 'CumStrategy']].tail(5))

結果の評価(収益率・ドローダウン)

# 最終リターン
final_market = df['CumMarket'].iloc[-1] - 1
final_strategy = df['CumStrategy'].iloc[-1] - 1
print(f"市場リターン: {final_market:.2%}")
print(f"戦略リターン: {final_strategy:.2%}")

# 最大ドローダウン
rolling_max = df['CumStrategy'].cummax()
drawdown = (df['CumStrategy'] - rolling_max) / rolling_max
max_dd = drawdown.min()
print(f"最大ドローダウン: {max_dd:.2%}")

# グラフ
fig, ax = plt.subplots(figsize=(14, 6))
ax.plot(df.index, df['CumMarket'], label='Buy&Hold', color='gray')
ax.plot(df.index, df['CumStrategy'], label='MA Cross戦略', color='blue')
ax.set_title('バックテスト結果:累積リターン比較')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('backtest.png')

よくある落とし穴(将来データ混入・手数料)

バックテストで最も多い失敗が「先読みバイアス(Look-ahead Bias)」です。シグナル計算に当日の終値を使い、同日の終値で売買するコードは現実には不可能な仮定を含む仕組みです。df['Position'] = df['Signal'].shift(1)のように1日ずらすことで防げます。手数料については、SBI証券の場合は100万円以下の取引で約定代金の0.495%かかるため、頻繁に売買する戦略では手数料負けする可能性があります。

筆者の検証メモ

トヨタ(7203.T)の3年データで検証したところ、5日/25日MAクロスのシンプル戦略はBuy&Holdに対して大きな優位性を示さなかった。移動平均クロスは相場のトレンドが明確な時期は有効だが、レンジ相場では頻繁にシグナルが切り替わり手数料コストが嵩む傾向がある。

次のステップ

バックテストの結果をさらに深掘りするには、シャープレシオの計算、複数銘柄への適用、パラメーター最適化(グリッドサーチ)といった方向があります。詳しくは関連書籍もご参照ください。

おすすめ書籍まとめはこちら


【投資免責事項】本記事で紹介するコードおよび分析結果は情報提供を目的としたものであり、特定の銘柄への投資を推奨するものではありません。投資判断はご自身の責任で行ってください。

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