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

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages