Skip to content

beatinaniwa/furlong

Repository files navigation

Furlong

JRA中央競馬の予測プロジェクト。netkeiba からレース結果を収集し、特徴量を構築してLightGBM/CatBoostで複勝(3着以内)予測モデルを学習・評価・バックテストするパイプラインを提供します。2段階モデルにより、オッズ非依存の実力予測(Stage 1)と市場確率との乖離によるバリュー検出(Stage 2)を行います。複勝に加え、三連複(3着以内3頭の組み合わせ)のHarvilleモデルによる確率計算・バックテスト・予測にも対応。未来のレース(出馬表)をスクレイピングし、DBの過去戦績と組み合わせてリアルタイム予測も可能です。

セットアップ

# Python 3.14 が必要
uv python install 3.14

# 依存関係インストール(LightGBMにはlibompが必要)
brew install libomp  # macOS
uv sync

プロジェクト構成

src/furlong/
    db/
        models.py          # データモデル(Race, RaceResult, Enum定義)
        store.py           # SQLite永続化(RaceStore)
    scraper/
        client.py          # HTTPクライアント(リトライ・間隔制御)
        race_list.py       # レースID収集
        race_result.py     # レース結果HTMLパーサー
        upcoming.py        # 出馬表パーサー + スクレイピング orchestration
        odds.py            # 単勝・複勝オッズJSON取得
        horse.py           # 馬ページパーサー(血統: sire_id/damsire_id抽出)
        parsing_utils.py   # 共通パースユーティリティ
    features/
        categories.py      # 距離・頭数カテゴリ分類
        race_class.py      # レースクラス分類(新馬〜重賞・障害)
        horse.py           # 馬の実力系特徴量(27カラム: 成績・適性・Bayesian平滑化・欠損フラグ)
        jockey.py          # 騎手系特徴量(9カラム: 180d成績・距離別・Bayesian平滑化)
        trainer.py         # 調教師系特徴量(9カラム: 180d成績・馬場別・休養明け成績)
        bloodline.py       # 血統系特徴量(8カラム: 父・母父の馬場/距離/重馬場適性)
        race.py            # レース条件系特徴量(13カラム: 基本情報・コースID・クラス序数)
        relative.py        # レース内相対特徴量(6カラム: 能力順位・ギャップ・場力)
        track_bias.py      # 馬場バイアス特徴量(1カラム)
        pace.py            # ペース・脚質特徴量(7カラム: 脚質確率・意図・エントロピー)
        pace_race.py       # レース内ペース特徴量(4カラム: 前傾馬数・前圧力・適性スコア)
        market.py          # 市場残差特徴量(5カラム: 残差・サプライズ率・フラグ・期待複勝率)
        builder.py         # FeatureBuilder: 全特徴量の組み立て
    model/
        models.py          # TrainedModel: LightGBM/CatBoostラッパー
        train.py           # TrainingPipeline: Binary + LambdaRank + Stage1学習・評価
        evaluate.py        # AUC-ROC, Top-N, キャリブレーション
        walk_forward.py    # ウォークフォワード検証(Stage 1対応)
        sanity.py          # 評価健全性テスト(ランダム予測ベースライン)
        predict.py         # 予測: 指定日のベット推奨出力(マルチ戦略対応)
        predict_trio.py    # 三連複予測: 確率計算・EV計算・DB保存
        trio.py            # 三連複確率計算(Harville + 独立法)
        strategy.py        # 戦略探索: フィルタ×VS閾値最適化パイプライン
        review.py          # ペーパートレード成績レビュー
    backtest/
        simulator.py       # BacktestSimulator: 賭け戦略 + value_scoreスイープ
        trio_simulator.py  # TrioBacktestSimulator: 三連複バックテスト
        roi_analysis.py    # ROIAnalyzer: レース条件別ROI分析
        condition_filter.py # ConditionFilter: 条件フィルタ付き戦略 + スイープ
        pnl_simulation.py  # P&Lシミュレーション(月別損益計算)
        betting.py         # payout計算共通関数
    config.py              # StrategyConfig + MultiStrategyConfig: TOML設定管理
    main.py                # CLIエントリポイント
experiments/               # 実験ログ(バックテスト結果・知見の記録)

使い方

Phase 1: データ収集

# 2025年のレースデータを収集(デフォルト: 1月〜12月)
uv run furlong scrape

# 期間を指定して収集
uv run furlong scrape --start-date 2025-01-01 --end-date 2025-03-31

# リクエスト間隔を変更(デフォルト: 2秒、最低: 1秒)
uv run furlong scrape --interval 3.0

# DBファイルパスを指定
uv run furlong scrape --db data/furlong.db

# 一時的エラーで失敗したレースを再取得
uv run furlong scrape --retry-failures

# 後方互換: サブコマンド省略も可能
uv run furlong --start-date 2025-01-01 --end-date 2025-03-31

途中で中断(Ctrl+C)しても、再実行すれば未取得分から自動的に再開します。

複勝データバックフィル

既存レースの複勝払戻(place_payout)と複勝オッズ(place_odds)をバックフィルします。

# 全レースをバックフィル
uv run furlong backfill-place

# 件数制限(テスト用)
uv run furlong backfill-place --limit 10

# 失敗レースを再試行
uv run furlong backfill-place --retry-failures

# リクエスト間隔を変更(デフォルト: 2秒)
uv run furlong backfill-place --interval 3.0

1レースあたり2リクエスト(結果ページHTML + オッズAPI/HTML)。処理済み判定はplace_oddsがNOT NULLの行の存在で行い、中断後の再開に対応しています。

調教師IDバックフィル

既存レース結果に調教師ID(trainer_id)をバックフィルします。

# 全レースをバックフィル
uv run furlong backfill-trainer-id

# 件数制限(テスト用)
uv run furlong backfill-trainer-id --limit 10

# 失敗レースを再試行
uv run furlong backfill-trainer-id --retry-failures

# リクエスト間隔を変更(デフォルト: 1秒)
uv run furlong backfill-trainer-id --interval 2.0

レース結果HTMLから調教師リンクのみを軽量抽出し、trainer_idカラムを更新します。100件ごとにバッチcommit、中断後の再開に対応。

血統データ収集

馬ページから血統情報(父: sire_id、母父: damsire_id)を収集します。

# 全馬の血統を収集
uv run furlong scrape-bloodline

# 件数制限(テスト用)
uv run furlong scrape-bloodline --limit 10

# 失敗馬を再試行
uv run furlong scrape-bloodline --retry-failures

# リクエスト間隔を変更(デフォルト: 1秒)
uv run furlong scrape-bloodline --interval 2.0

取得済みスキップ(idempotent)、失敗馬はfailed_horsesテーブルに記録(最大3回リトライ)。100件ごとにバッチcommit。

Phase 2: 特徴量構築

SQLiteに蓄積されたデータから、機械学習用の特徴量マトリクスを生成します。

from furlong.features.builder import FeatureBuilder

builder = FeatureBuilder("furlong.db")
df = builder.build()           # 100列(モデル入力用)
df_meta = builder.build_with_metadata()  # 114列(学習・バックテスト用)

特徴量一覧(98モデル特徴量)

# カラム名 区分 説明
1 race_id キー レースID
2 horse_number キー/特徴量 馬番
3 is_top3 目的変数 複勝圏内(3着以内)なら1。7頭以下は2着以内
基本情報
4 age 馬(基本) 馬齢(sex_ageから抽出)
5 sex_ord 馬(基本) 性別序数(牡=0, 牝=1, セ=2)
6 weight_carried 馬(基本) 斤量
7 race_no レース(基本) レース番号
8 race_class_ord レース(基本) クラス序数(新馬=0〜重賞=6)
9 track_name_code レース(基本) 競馬場コード(札幌=0〜小倉=9)
10 course_id_code レース(基本) コースID(会場×馬場×距離の決定論的エンコーディング)
馬: 直近成績
11-14 avg_finish_last3/5, avg_last3f_last3/5 馬(直近成績) 直近3/5走の平均着順・上がり3F
馬: 正規化成績
15-16 finish_pct_last3/5 馬(正規化) 頭数正規化着順割合(直近3/5走平均)
17 finish_pct_best_last5 馬(正規化) 直近5走のベスト着順割合
18 finish_pct_std_last5 馬(正規化) 直近5走の着順割合の標準偏差
19 top3_rate_last5 馬(正規化) 直近5走中の複勝率
20-21 last3f_rank_pct_last3/5 馬(正規化) 頭数正規化上がり順位割合(直近3/5走)
22 speed_slope_last3 馬(トレンド) 直近3走のスピード指数線形回帰傾き
馬: 適性
23 avg_finish_same_dist_cat 馬(距離適性) 同距離カテゴリ平均着順
24-25 win_rate_same_surface, place_rate_same_surface 馬(馬場適性) 同馬場勝率・複勝率
26-27 win_rate_same_surface_smoothed/count 馬(馬場適性) Bayesian平滑化同馬場勝率・サンプル数
28-29 place_rate_same_surface_smoothed/count 馬(馬場適性) Bayesian平滑化同馬場複勝率・サンプル数
馬: その他
30 days_since_last_race 馬(間隔) 前走間隔日数
31 horse_weight_diff 馬(体重) 馬体重前走比
32 career_count 馬(経験) 過去出走回数
33-34 avg_speed_index_last3/5 馬(スピード) 直近3/5走平均スピード指数
35-36 avg_last3f_rank_last3/5 馬(上がり順位) 直近3/5走平均上がり3F順位
37 distance_change 馬(距離変化) 前走との距離差(m)
馬: 欠損フラグ
38 is_debut 馬(フラグ) デビュー戦フラグ(career_count=0)
39 is_few_starts 馬(フラグ) 少走馬フラグ(career_count≤2)
40 is_first_surface 馬(フラグ) 初馬場フラグ
41 is_first_distance_bucket 馬(フラグ) 初距離カテゴリフラグ
騎手
42-43 jockey_win_rate_180d, jockey_place_rate_180d 騎手(直近) 直近180日勝率・複勝率
44 jockey_win_rate_dist_cat 騎手(距離) 距離カテゴリ別勝率
45-46 jockey_win_rate_180d_smoothed/count 騎手(Bayesian) 180日Bayesian平滑化勝率・件数
47-48 jockey_place_rate_180d_smoothed/count 騎手(Bayesian) 180日Bayesian平滑化複勝率・件数
49-50 jockey_win_rate_dist_cat_smoothed/count 騎手(Bayesian) 距離別Bayesian平滑化勝率・件数
レース条件
51 bracket_number レース条件 枠番
52 horse_count レース条件 出走頭数
53 distance レース条件 距離(m)
54 surface_turf レース条件 芝=1, ダート=0
55-58 track_goodtrack_bad レース条件 馬場状態(one-hot)
市場
59 popularity 市場 人気順(Float64)
60 implied_prob 市場 レース内正規化複勝オッズ確率(1/place_odds)
馬場バイアス
61 track_bias_norm_bracket_top3 馬場バイアス 同日先行レースの内外バイアス(※Stage 1では除外)
レース内相対
62 ability_rank_in_field 相対(能力) レース内能力順位(降順rank)
63 ability_rank_pct 相対(能力) 能力順位の頭数正規化
64 ability_gap_to_best 相対(ギャップ) 最強馬との能力差
65 ability_gap_to_p3 相対(ギャップ) 3番手との能力差
66 field_strength_mean 相対(場力) 自馬除外の場力平均
67 field_dispersion 相対(場力) 場力の分散(IQR)
ペース・脚質 (Phase 1)
68 style_p_nige 脚質(確率) 逃げ確率(直近5走EWMA, halflife=3)
69 style_p_senko 脚質(確率) 先行確率
70 style_p_sashi 脚質(確率) 差し確率
71 style_p_oikomi 脚質(確率) 追込確率
72 front_intent 脚質(意図) 前へ行く意図(1.0×p_nige + 0.6×p_senko)
73 closing_intent 脚質(意図) 追込意図(0.7×p_sashi + 1.0×p_oikomi)
74 style_entropy 脚質(自在性) 脚質の自在性(-Σ p_i×ln(p_i))
75 n_front_runners ペース(レース) レース内前傾馬数(front_intent > 0.55の馬数)
76 sum_front_intent ペース(レース) レース内front_intent合計
77 race_front_pressure ペース(レース) 前圧力(sum_front_intent / 出走頭数)
78 pace_fit_score ペース(適性) ペース適性スコア(ペースシナリオ別複勝率の加重期待値)
市場残差 (Phase 1)
79 market_resid_last5 市場残差 EWMA市場残差(直近5走, halflife=3)
80 positive_surprise_rate 市場残差 直近5走の期待上振れ率
81 crowd_underbet_flag 市場残差 過小評価フラグ(resid > 0.05, Int8)
82 crowd_overbet_flag 市場残差 過大評価フラグ(resid < -0.05, Int8)
83 expected_top3_current 市場残差 現レースオッズからの期待複勝率(※Stage 1では除外)
調教師 (Phase 2)
84-85 trainer_win_rate_180d, trainer_place_rate_180d 調教師(直近) 直近180日勝率・複勝率
86 trainer_rides_180d 調教師(直近) 直近180日出走数
87-88 trainer_win_rate_180d_smoothed, trainer_place_rate_180d_smoothed 調教師(Bayesian) 180日Bayesian平滑化勝率・複勝率
89 trainer_surface_place_rate 調教師(馬場) 同馬場複勝率(全期間)
90 trainer_surface_place_rate_smoothed 調教師(Bayesian) 同馬場Bayesian平滑化複勝率
91 trainer_layoff_place_rate 調教師(休養明) 休養明け馬(60日超)の複勝率
92 trainer_layoff_place_rate_smoothed 調教師(Bayesian) 休養明けBayesian平滑化複勝率
血統 (Phase 2)
93-94 sire_surface_place_rate, sire_surface_place_rate_smoothed 血統(父) 父の産駒の同馬場複勝率・Bayesian平滑化
95-96 sire_distance_place_rate, sire_distance_place_rate_smoothed 血統(父) 父の産駒の同距離カテゴリ複勝率・Bayesian平滑化
97-98 sire_wet_place_rate, sire_wet_place_rate_smoothed 血統(父) 父の産駒の重馬場(重/不良)複勝率・Bayesian平滑化
99-100 damsire_surface_place_rate, damsire_surface_place_rate_smoothed 血統(母父) 母父の産駒の同馬場複勝率・Bayesian平滑化

メタデータ列(build_with_metadata() のみ): race_date, win_odds, place_odds, place_payout, finish_position, jockey_name, trainer_name, trainer_id, sire_id, damsire_id, race_class_category, distance_category, surface, horse_count_category

Bayesian平滑化

馬場適性率・騎手成績率にBayesian平滑化を適用: (n × 実績率 + 5 × 事前率) / (n + 5)。事前率は勝率=0.08、複勝率=0.25。サンプル数が少ない馬・騎手の過学習を抑制します。

レース内相対特徴量

ability_core = 0.4×speed_index + 0.3×finish_pct + 0.3×(1-last3f_rank_pct) を基に、レース内での順位・ギャップ・場の強さを計算。能力コアがnullの馬(デビュー馬等)は全相対特徴量がnull。

ペース・脚質特徴量 (Phase 1)

passing_positions(通過順位、例: "05-05-04-03")を解析し、各馬の脚質傾向とレース展開予測を特徴量化します。

  • 脚質分類: 通過順位の正規化位置(early_pct)と直線での順位変動(stretch_gain)から4分類(逃げ/先行/差し/追込)
  • 脚質確率: 直近5走のEWMA(halflife=3)で各脚質の確率を算出。front_intent/closing_intentは線形結合
  • ペースラベル: レース全体のペースを判定(high/mid/slow)。前半組の崩れ具合と後方組の追い上げで分類
  • pace_fit_score: 予測されるペースシナリオ(race_front_pressure由来)下での各馬の複勝率期待値。ハイペースで好走する追込馬が高スコアに
  • 入力フォールバック: passing_positionsがDBに無い場合は全ペース特徴量がnull

市場残差特徴量 (Phase 1)

オッズから計算される「市場の期待」に対する実績の乖離(残差)を特徴量化します。

  • expected_top3: オッズ帯×頭数帯×クラス帯の累積日次集計から算出(当日データ除外でリーケージ防止)。階層フォールバック(primary → オッズ×頭数 → オッズのみ → global prior 0.25)
  • market_resid: is_top3 - expected_top3。正=期待以上の成績、負=期待以下
  • crowd_underbet/overbet_flag: EWMA残差が±0.05を超える馬にフラグ
  • expected_top3_current: 現レースのオッズから算出したリアルタイム期待複勝率(Stage 2のみ使用)
  • 入力フォールバック: win_oddsがDBに無い場合はhistory系4列がnull。target側にwin_oddsがあればexpected_top3_currentはglobal prior(0.25)を返却

調教師特徴量 (Phase 2)

trainer_idを使って調教師の成績傾向を特徴量化します。騎手特徴量と同パターン(180日窓・Bayesian平滑化)。

  • 180日成績: 直近180日の勝率・複勝率・出走数(日次集約、過去日のみ)
  • 馬場別成績: 同馬場(芝/ダート)での複勝率(全期間)
  • 休養明け成績: 60日超の間隔が空いた馬の複勝率(全期間)
  • Bayesian平滑化: 全率にN=5、prior=0.08(勝率)/0.25(複勝率)で適用

血統特徴量 (Phase 2)

horse_bloodlineテーブルのsire_iddamsire_idを使い、産駒の過去成績から血統適性を特徴量化します。

  • 父の馬場適性: 同馬場(芝/ダート)での産駒複勝率
  • 父の距離適性: 同距離カテゴリでの産駒複勝率
  • 父の重馬場適性: 重・不良馬場での産駒複勝率
  • 母父の馬場適性: 同馬場での産駒複勝率
  • 全率にBayesian平滑化(N=5, prior=0.25)を適用

スピード指数

speed_index = (base_time - finish_time) / std_time(正=速い=良い)。ベースラインは同距離・同馬場・同馬場状態の累積日別集計から計算(O(N log N))。サンプル不足時は馬場状態を除外した(距離, 馬場)フォールバックを使用。

馬場バイアス

同日同会場の先行レース(race_number < 自レース)の3着以内馬の正規化枠番平均。低値=内枠有利、高値=外枠有利。芝・ダートは独立して計算。

目的変数 is_top3

  • 一次ソース: place_payout > 0(バックフィル済み複勝払戻に基づく実データ)
  • フォールバックplace_payoutがNULLの場合のみ): finish_position <= 3horse_count <= 7 なら <= 2
  • フォールバック使用件数はログ出力で監視可能

リーケージ防止

  • 馬・騎手特徴量: past_date < target_date(厳密小なり)。同日レース不可視
  • ペース・脚質: 過去レースのpassing_positionsのみ使用。当該レースの通過順位は不使用
  • market_resid: 各past raceのis_top3とexpected_top3から算出。全て過去データのみ
  • expected_top3 lookup: 累積日次集計 + shift(1)で当該日のデータを除外(speed index baselineと同パターン)
  • 馬場バイアス: 同日の先行レース結果を使用(実運用で利用可能な情報)

距離カテゴリ

カテゴリ 距離
sprint 〜1400m
mile 1401〜1600m
middle 1601〜2200m
long 2201m〜

Phase 2-2: モデル学習・評価・バックテスト

# 基本パイプライン(Binary分類のみ)
uv run furlong train

# LambdaRankモデルも学習
uv run furlong train --with-ranker

# ウォークフォワード検証も実行
uv run furlong train --with-walk-forward

# 2段階モデル(LightGBM vs CatBoost + value_score検証)
uv run furlong train --stage1

# 全機能有効化(Binary + LambdaRank + ウォークフォワード + Stage 1)
uv run furlong train --full

# DBやモデル出力先を指定
uv run furlong train --db furlong.db --output-dir models/

パイプライン処理

  1. 特徴量生成: FeatureBuilderで100列のDataFrameを構築
  2. 時系列分割: max(race_date) - 90日 をカットオフとしてtrain/valに分割
  3. Binary LightGBM学習: 学習前にtrain末尾を校正用ホールドアウトとして分離(データ十分時)、binary classification(複勝予測、is_unbalance=True)、early stopping 50
  4. 確率校正: 校正ホールドアウト上で none / isotonic / sigmoid を比較し、Brier→LogLoss最小の方式を選択して検証確率へ適用
  5. 評価: AUC-ROC、Top-N的中率、キャリブレーション
  6. バックテスト: 7種の賭け戦略でROIをシミュレーション
  7. LambdaRank学習--with-ranker時): NDCG@1/3/5で最適化、Top-N固定ベット評価
  8. ウォークフォワード検証--with-walk-forward時): 4期間の時系列交差検証
  9. Stage 1: 2段階モデル--stage1時):
    • LightGBM/CatBoostでオッズ非依存の実力予測(popularity, implied_prob, track_bias_norm_bracket_top3, expected_top3_currentを除外)
    • value_score(= pred_prob / implied_prob)スイープで最適閾値検出
    • レース条件別(クラス/頭数/馬場/距離)ROI分析
    • profitable条件の自動検出と条件フィルタ比較
    • ウォークフォワードStage 1比較(--with-walk-forward併用時)

バックテスト戦略

戦略 ベット対象 的中条件 払戻
複勝 各レース予測確率1位に100円 is_top3 == 1 place_payout × bet_amount / 100
期待値 pred_prob × place_odds > 閾値の馬に各100円 is_top3 == 1 place_payout × bet_amount / 100
バリュー モデル順位が市場順位より高い馬(rank_divergence ≥ 閾値) is_top3 == 1 place_payout × bet_amount / 100
Top-N固定 rankerスコア上位N頭に各100円 is_top3 == 1 place_payout × bet_amount / 100
Value Score value_score > 閾値 かつ pred_prob > min_prob is_top3 == 1 place_payout × bet_amount / 100

モデル構成

モデル 目的関数 評価指標 ベット戦略
Binary (LGBMClassifier) binary_logloss AUC-ROC, Top-N, EV sweep 複勝/期待値/バリュー
LambdaRank (LGBMRanker) lambdarank NDCG@1/3/5, Top-N Top-N固定ベット
Stage 1 LightGBM binary_logloss AUC-ROC (オッズ非依存) Value Score(複勝)
Stage 1 CatBoost Logloss AUC (カテゴリ特徴量対応) Value Score(複勝)

出力ファイル

ファイル 内容
models/model.txt Binary LightGBM native形式
models/model.pkl Binary pickle形式
models/model_calibration.pkl Binary確率校正アーティファクト(方式 + calibrator)
models/training_meta.json 特徴量順序・パラメータ・評価指標(auc_roc_raw/auc_roc_eval、校正方式とBrier/LogLoss比較を含む)
models/model_ranker.txt LambdaRank native形式(--with-ranker時)
models/model_ranker.pkl LambdaRank pickle形式
models/training_meta_ranker.json Rankerメタデータ
reports/backtest_balance.csv 各戦略の収支推移
models/model_stage1_lgb.pkl Stage 1 LightGBM(--stage1時)
models/model_stage1_cb.pkl Stage 1 CatBoost(--stage1時)
reports/stage1_comparison.csv LightGBM vs CatBoost比較(--stage1時)
reports/value_score_sweep.csv Value Scoreスイープ結果(--stage1時)
reports/roi_by_condition.csv レース条件別ROI(--stage1時)
reports/walk_forward_stage1.csv WF Stage 1結果(--stage1 --with-walk-forward時)
models/training_meta_stage1.json Stage 1学習メタデータ(--stage1時)
config.toml 最適戦略設定(strategyコマンドで生成)
reports/filter_sweep.csv フィルタ×VS閾値スイープ結果(strategyコマンドで生成)

Phase 3: 戦略探索・最適化

# 条件フィルタ×VS閾値の最適化 → ウォークフォワード検証 → config.toml生成
uv run furlong strategy

# 特定プロファイルを更新(デフォルト: default)
uv run furlong strategy --profile aggressive

# ウォークフォワード検証をスキップ(高速)
uv run furlong strategy --skip-wf

# DB・モデル・出力先を指定
uv run furlong strategy --db furlong.db --model models/model_stage1_lgb.pkl --output config.toml

処理フロー:

  1. 特徴量生成 → Stage 1モデルで検証データ予測
  2. 4つの候補戦略フィルタ(少頭数、少頭数×ダート、少頭数×OP、ダート×中距離)× VS閾値(1.5〜3.0)のスイープ
  3. 戦略選定: bets/race 1〜3 を満たす候補から、収縮ROI(shrinkage_factor=100)+ 最大DD制約(max_drawdown=5000)で選定(候補なし時は段階的に制約緩和)
  4. Nestedウォークフォワード検証(outer=4期間、inner=4期間)で内側最適化/外側OOSを実施。Nestedで有効期間がない場合は単純WFにフォールバック
  5. P&Lシミュレーション(100円/1000円固定ベット)
  6. config.toml に最適戦略を保存(マルチ戦略フォーマット)

補足:

  • 戦略コマンドのWFでは、初期フィルタ最適化に使った検証期間(training_meta_stage1.jsonval_date_start 以降)を除外して評価し、in-sample化を回避します。

config.toml(マルチ戦略フォーマット)

[model]
type = "lightgbm"
path = "models/model_stage1_lgb.pkl"

[database]
path = "furlong.db"

[strategy.conservative]
vs_threshold = 3.0
min_prob = 0.15

[strategy.aggressive]
vs_threshold = 7.0
min_prob = 0.15

[strategy.aggressive.conditions]
horse_count_category = ["~8頭", "9~12頭"]
surface = [""]

複数の戦略プロファイルを定義し、predict コマンドで切り替えて使用できます。旧形式([strategy] に直接パラメータを記述)も自動検出して互換ロードされます。

評価健全性テスト

# ランダム予測でバックテスト(VS>3.0、10回反復)
uv run furlong test-sanity

# VS閾値・反復回数を指定
uv run furlong test-sanity --vs-threshold 2.0 --iterations 20

# DB指定
uv run furlong test-sanity --db furlong.db

各馬にランダムな確率(0〜1の一様乱数)を付与してValue Scoreバックテストを実行します。ランダム予測でROIがプラスであれば「⚠ 評価系にバイアスあり」と警告表示し、評価パイプラインの見直しが必要なことを示します。

週次ワークフロー

毎週の予測→結果取込→振り返りを効率化するコマンド群です。

# 金曜夜: 今週末(土日)の予測を実行・保存
uv run furlong predict-weekend --save

# 月曜夜: 直近の土日のレース結果を取込
uv run furlong update-results --weekly

# 月曜夜: 直近の土日の成績レビュー
uv run furlong review --weekly

predict-weekend

今週の土曜・日曜の日付を自動算出し、両日分の予測を実行します。

# 表示のみ(保存しない)
uv run furlong predict-weekend

# 予測をDBに保存(既に保存済みの日はスキップ)
uv run furlong predict-weekend --save

# 保存済みでも強制再実行
uv run furlong predict-weekend --save --force

# 戦略・設定ファイル指定
uv run furlong predict-weekend --save --strategy all --config config.toml

--save 指定時、既にDBに同日・同戦略の予測が保存されていればスキップして「スキップ: 2026-03-07(保存済み)」と表示します。--force でスキップを無効化できます。

update-results

指定日のレース結果をnetkeibaからスクレイピングしてDBに保存します。既にDB内にあるレースはスキップし、永続的なエラー(404等)のレースも除外します。取込後は予測の自動照合を実行します。

# 特定の日付の結果を取込
uv run furlong update-results --date 2026-02-28

# 当日の結果を取込
uv run furlong update-results --date today

# 直近の土日の結果を一括取込
uv run furlong update-results --weekly

# リクエスト間隔を変更(デフォルト: 2秒、最低: 1秒)
uv run furlong update-results --weekly --interval 3.0

predict --date today

predict コマンドの --datetoday を指定すると当日日付に自動変換されます。

uv run furlong predict --date today --strategy all

Phase 3: 予測

# 過去の日付(DB内に結果がある場合)
uv run furlong predict --date 2025-12-01

# 未来の日付 → 出馬表を自動スクレイピングして予測
uv run furlong predict --date 2026-03-01 --strategy all

# 予測をDBに保存(ペーパートレード用)
uv run furlong predict --date 2026-03-01 --strategy all --save

# 特定の戦略プロファイルを指定
uv run furlong predict --date 2026-03-01 --strategy conservative

# config.tomlの戦略設定を使用
uv run furlong predict --date 2026-03-01 --config config.toml

# モデル・DBを明示指定
uv run furlong predict --date 2026-03-01 --model models/model_stage1_lgb.pkl --db furlong.db

出力: 戦略ヘッダー付きで、レースごとにグループ化されたRichテーブル。会場名・レース番号・レース名・馬番・馬名・予測確率・複勝オッズ・Value Score・備考を表示します。VS値は10.0でキャップされます。min_career_count(デフォルト3)未満の出走歴の馬はベット推奨から除外されます。--strategy all時、同一設定の戦略は表示上統合されます(DB保存は元の戦略名ごと)。

自動フロー分岐: DBに対象日の完了済みレース結果があれば既存DBフロー、なければ出馬表をスクレイピングして予測します。出馬表フローでは:

  • race.netkeiba.com から出馬表とオッズを取得
  • DBの過去戦績を使って馬・騎手の特徴量を計算
  • 障害レースは自動除外、取消・除外馬はエントリから除外
  • オッズ未発売の馬は value_score=NaN となり戦略フィルタで除外

--save フラグを付けると予測結果をDBの predictions テーブルに保存し、レース結果との自動照合を行います。--strategy 未指定時は、default プロファイルまたは唯一のプロファイルが使用されます(複数プロファイル存在時はエラー)。

Phase 4: ペーパートレードレビュー

# 特定日の成績レビュー(ベット詳細表示付き)
uv run furlong review --date 2025-03-01

# 期間指定の成績レビュー
uv run furlong review --from 2025-01-01 --to 2025-03-31

# 全期間の成績レビュー
uv run furlong review

# 直近の土日の成績レビュー
uv run furlong review --weekly

# 強制再照合(結果データ更新後など)
uv run furlong review --date 2025-03-01 --force-reconcile

レビューコマンドは保存済みの予測をレース結果と照合し、戦略別の成績を表示します:

  • 戦略別集計: ベット数、的中数、複勝率、投資額、回収額、ROI、返金件数
  • 未照合の予測件数(結果データ未スクレイピング or 複勝払戻未取得のケース)
  • 単日指定時は個別ベットの詳細(着順、結果、複勝オッズ、払戻)も表示

三連複 (Trio) ベッティング

is_top3モデルの確率出力を活用し、三連複(3着以内3頭の組み合わせ、順不同)の期待値ベッティングを行います。

三連複データ収集

# 既存レースの三連複払戻データをバックフィル
uv run furlong backfill-trio

# 件数制限
uv run furlong backfill-trio --limit 100

# リクエスト間隔を変更(デフォルト: 2秒)
uv run furlong backfill-trio --interval 3.0

scrape / update-results コマンドでも三連複払戻は自動的にパース・保存されます。

三連複バックテスト

# Top-N戦略でバックテスト
uv run furlong backtest-trio --top-n 5 10 20 50

# 確率計算手法を指定(harville / independent)
uv run furlong backtest-trio --method harville

# アンカー戦略も実行
uv run furlong backtest-trio --with-anchor

# モデル・DBを指定
uv run furlong backtest-trio --model models/model_stage1_lgb.pkl --db furlong.db

バックテスト戦略:

戦略 ベット対象 説明
top-N 各レースでHarville確率上位N組み合わせ N=5,10,20,50
anchor-N Top-1馬を軸に相手上位N頭からのC(N,2)組み合わせ N=3,5,7
random-N ランダムN組み合わせ(ベースライン) 100回反復平均

三連複予測

# 指定日の三連複予測
uv run furlong predict-trio --date 2026-03-08 --top-n 10

# オッズも取得してEV計算
uv run furlong predict-trio --date 2026-03-08 --with-odds

# 予測をDBに保存
uv run furlong predict-trio --date 2026-03-08 --save

# 確率計算手法を指定
uv run furlong predict-trio --date 2026-03-08 --method independent

三連複予測レビュー

# 全期間の三連複成績レビュー
uv run furlong review-trio

# 期間指定
uv run furlong review-trio --from 2026-01-01 --to 2026-03-31

# 直近の土日
uv run furlong review-trio --weekly

# 強制再照合
uv run furlong review-trio --force-reconcile

確率計算手法

  • Harville法: 各馬のstrength score (s = p / (1-p)) を使い、条件付き確率の6順列の合計で三連複確率を算出。P(trio {a,b,c}) = Σ P(1st=x, 2nd=y, 3rd=z) (x,y,zはa,b,cの全順列)
  • 独立法: P(i,j,k) = p_i × p_j × p_k / Z (Z = 全組み合わせの積の和で正規化)
  • 16頭立て: 16C3=560組み合わせ × 6順列 = 3,360回の計算

DBスキーマ

races

カラム 説明
race_id TEXT PK 12桁のnetkeiba ID
race_date TEXT 開催日
venue TEXT 競馬場名(中山、東京 等)
race_number INTEGER レース番号(1-12)
race_name TEXT レース名
surface TEXT 芝 / ダ / 障
distance INTEGER 距離(m)
weather TEXT 天候
track_condition TEXT 馬場状態

race_results

カラム 説明
race_id TEXT 外部キー
horse_number INTEGER 馬番
finish_position INTEGER 着順(取消等はNULL)
horse_name TEXT 馬名
horse_id TEXT netkeiba馬ID
jockey_name TEXT 騎手名
finish_time_seconds REAL タイム(秒)
win_odds REAL 単勝オッズ
place_odds REAL 複勝オッズ(min/max平均)
place_payout INTEGER 複勝払戻(100円単位)
horse_weight INTEGER 馬体重
prize_money REAL 賞金(万円)
trainer_id TEXT 調教師ID(netkeiba)

※ 主要カラムのみ記載。全カラムは store.py を参照。

horse_bloodline

カラム 説明
horse_id TEXT PK netkeiba馬ID
sire_id TEXT 父馬ID
damsire_id TEXT 母父馬ID
created_at TEXT 作成日時

predictions(ペーパートレード用)

カラム 説明
strategy TEXT 戦略プロファイル名
race_id TEXT レースID
race_date TEXT レース日
horse_number INTEGER 馬番
pred_prob REAL 予測確率
value_score REAL バリュースコア
place_odds REAL 複勝オッズ(予測時)
bet_amount INTEGER ベット額(デフォルト: 100)
finish_position INTEGER 着順(照合後)
is_place INTEGER 複勝的中フラグ(照合後)
settled_place_odds REAL 確定複勝オッズ(照合後)
payout INTEGER 払戻額(照合後: place_payout × bet_amount / 100)
reconciled_at TEXT 照合日時

UNIQUE制約: (strategy, race_id, horse_number)predict --save で保存、review で照合・集計。複勝圏内入着かつplace_payout未取得の場合は未照合(reconciled_at IS NULL)のまま保留されます。

trio_results(三連複払戻)

カラム 説明
race_id TEXT レースID
horse1 INTEGER 馬番1(常に horse1 < horse2 < horse3)
horse2 INTEGER 馬番2
horse3 INTEGER 馬番3
payout INTEGER 払戻金額(100円単位)

PRIMARY KEY: (race_id, horse1, horse2, horse3)。同着時は複数行。

trio_predictions(三連複予測)

カラム 説明
strategy TEXT 戦略名(例: trio-top10-harville)
race_id TEXT レースID
race_date TEXT レース日
horse1-3 INTEGER 馬番(ソート済み)
prob_harville REAL Harville法確率
prob_independent REAL 独立法確率
trio_odds REAL 三連複オッズ
ev_harville REAL 期待値
is_hit INTEGER 的中フラグ(照合後)
actual_payout INTEGER 払戻額(照合後)

UNIQUE制約: (strategy, race_id, horse1, horse2, horse3)

開発

今後の実装タスク管理は TODO.md を参照してください。

# テスト実行
uv run pytest tests/ -v

# リント
uv run ruff check src/

# Phase 0 特徴量検証(65特徴量ベースライン)
uv run python scripts/verify_phase0.py

# Phase 1 特徴量検証(+16特徴量のアブレーション比較)
uv run python scripts/verify_phase1.py

# Phase 2 特徴量検証(+17特徴量: 調教師9 + 血統8、キャリブレーション比較)
uv run python scripts/verify_phase2.py

推定実行時間(データ収集)

対象 リクエスト数 所要時間(デフォルト間隔)
レース結果 1ヶ月分 〜300 〜10分(2秒間隔)
レース結果 1年分 〜3,500 〜2時間(2秒間隔)
調教師IDバックフィル 〜10,900 〜3時間(1秒間隔)
血統データ収集 〜22,500 〜6時間(1秒間隔)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages