PythonのOptunaでバックテストパラメータを自動最適化する方法【日本株】

子供が生まれてからというもの、バックテストのパラメータを手動で試す時間が完全に消えた。以前は移動平均の期間を5日、10日、20日…と変えながら「この組み合わせが一番リターン出るかな」って延々と試していたんだけど、今は深夜にパソコンの前に座る余裕すらない。そんなとき知ったのがOptuna。「これ、育児パパのために作られたやつじゃないか」って思ったのが正直なところ。

なぜOptunaをバックテストに使うのか

Optunaは、Preferred Networks(日本の機械学習企業)が開発したハイパーパラメータ自動最適化フレームワーク。機械学習モデルの調整によく使われるけど、バックテストとの相性がめちゃくちゃいい。

何がいいかというと、「グリッドサーチ(総当たり)」じゃなく、TPE(Tree-structured Parzen Estimator)という賢い探索アルゴリズムで「次はどのパラメータを試すか」を自動で判断してくれること。1000通りのパラメータを全部試すんじゃなく、100回くらいの試行でかなりいい答えに近づける。時間のない社会人には最高。

ちなみに過最適化(オーバーフィッティング)は別問題として残るので、Optunaで出た「最強パラメータ」をそのまま信じるのは危険。あくまで「手動探索の代替」として使うのが正しい。これ、最初に僕がやらかしたやつ。。。

インストールと環境準備

pip install optuna yfinance pandas numpy

yfinanceで日本株データを取得する。銘柄コードの末尾に「.T」をつけるのがポイント(例:トヨタなら「7203.T」)。

バックテスト関数を作る

まず、Optunaに渡す「目的関数」を作る。この関数がパラメータを受け取って、バックテストの結果(シャープレシオなど)を返す。

import optuna
import yfinance as yf
import pandas as pd
import numpy as np

# 日本株データの取得(例:トヨタ自動車)
ticker = "7203.T"
df = yf.download(ticker, start="2022-01-01", end="2025-12-31")
df = df[["Close"]].dropna()

def backtest(short_window, long_window):
    """移動平均クロス戦略のバックテスト"""
    if short_window >= long_window:
        return -999  # 無効なパラメータ

    df["short_ma"] = df["Close"].rolling(short_window).mean()
    df["long_ma"] = df["Close"].rolling(long_window).mean()
    df = df.dropna()

    # シグナル生成(ゴールデンクロスで買い、デッドクロスで売り)
    df["signal"] = 0
    df.loc[df["short_ma"] > df["long_ma"], "signal"] = 1
    df["position"] = df["signal"].shift(1)

    # リターン計算
    df["returns"] = df["Close"].pct_change()
    df["strategy_returns"] = df["returns"] * df["position"]

    # シャープレシオ(年換算)
    mean_return = df["strategy_returns"].mean()
    std_return = df["strategy_returns"].std()
    if std_return == 0:
        return -999
    sharpe = (mean_return / std_return) * np.sqrt(252)
    return sharpe

def objective(trial):
    """Optunaの目的関数"""
    short_window = trial.suggest_int("short_window", 5, 50)
    long_window = trial.suggest_int("long_window", 20, 200)
    return backtest(short_window, long_window)

# 最適化の実行
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=100, show_progress_bar=True)

print("最適パラメータ:")
print(f"  短期移動平均: {study.best_params['short_window']}日")
print(f"  長期移動平均: {study.best_params['long_window']}日")
print(f"  シャープレシオ: {study.best_value:.3f}")

実行すると100回の試行が自動で走る。手動で試すより圧倒的に速い。

結果の可視化

Optunaには便利な可視化機能もある。

import optuna.visualization as vis

# パラメータの重要度
fig = vis.plot_param_importances(study)
fig.show()

# 試行履歴
fig2 = vis.plot_optimization_history(study)
fig2.show()

# パラメータのコンター図(どの組み合わせがいいか視覚化)
fig3 = vis.plot_contour(study, params=["short_window", "long_window"])
fig3.show()

コンター図を見ると「このあたりのパラメータが良さそう」という領域がヒートマップで表示される。「感覚でいじってた」が「データで見える」になる瞬間。

実際に試した結果(トヨタ、3年分)

トヨタ(7203.T)で試したところ、最適パラメータは短期12日・長期89日あたりで落ち着いた。シャープレシオは0.6前後。「これ、バイ&ホールドより良いのか?」って確認したら、バイ&ホールドのほうが若干高かった。。。まあ、製造メーカーはトレンドフォローより長期保有のほうが合ってるのかもしれない。

複数銘柄で試すなら、ループで銘柄ごとにstudyを作って結果をまとめるとよい。

tickers = ["7203.T", "6501.T", "6752.T"]  # トヨタ、日立、パナソニック
results = {}

for ticker in tickers:
    df = yf.download(ticker, start="2022-01-01", end="2025-12-31")[["Close"]].dropna()

    def make_objective(data):
        def objective(trial):
            short = trial.suggest_int("short_window", 5, 50)
            long_ = trial.suggest_int("long_window", 20, 200)
            return backtest_with_data(data, short, long_)
        return objective

    study = optuna.create_study(direction="maximize")
    optuna.logging.set_verbosity(optuna.logging.WARNING)
    study.optimize(make_objective(df), n_trials=100)

    results[ticker] = {
        "best_sharpe": study.best_value,
        "params": study.best_params
    }

print(pd.DataFrame(results).T)

注意点:過最適化に気をつける

Optunaで出た「最適パラメータ」は、あくまでバックテスト期間に最もフィットしたもの。将来も同じ動きをするとは限らない。対策としては:

ウォークフォワード最適化:訓練期間とテスト期間を分けて繰り返しテストする。② アウトオブサンプルテスト:最適化に使っていないデータで検証する。③ シンプルに保つ:パラメータが多いほど過最適化のリスクが高い。

僕は最初「Optunaで最強のパラメータ見つけた!」って喜んで本番運用したら、2週間でドローダウン15%になったことがある。。。ウォークフォワードは必須。

まとめ

Optunaを使えば、バックテストのパラメータ最適化を完全自動化できる。手動で試行錯誤していた時間を、育児や仕事に回せるのが最高。ただし、過最適化には要注意。

個人的には、Optunaで探索した後にウォークフォワードで検証する流れが今のマイベストプラクティスになってる。次はOptunaとバックテストフレームワーク「vectorbt」を組み合わせて、もっと高速に最適化できるか試してみたい。

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