BoatraceCSV

Operations

GitHub Actions ワークフロー、設定ファイル、運用上のメモをまとめています。Cloud Run Jobs の詳細は infrastructure.md を参照してください。


Environment Setup for GitHub Actions

  1. Repository secrets (configured in GitHub):
    • GITHUB_TOKEN (provided automatically)
    • Optional: GIT_USER_EMAIL (defaults to “action@github.com”)
    • Optional: GIT_USER_NAME (defaults to “GitHub Action”)
  2. GitHub Pages configuration:
    • Settings → Pages → Source: Deploy from a branch
    • Branch: main
    • Folder: / (root)

Workflows

予想者(Predictor)の運用

scripts/boatrace/predictors/registry.py で active な予想者を宣言し、infra/run-*.sh の ACTIVE_PREDICTORS 配列を同期して更新します。build_index.py --all-active / build_weights.py --all-active が registry を参照して全 active 予想者をループします。詳細は data/estimate.md を参照。


Configuration

Edit .boatrace/config.json to customize:

{
  "rate_limit_interval_seconds": 3,
  "max_retries": 3,
  "initial_backoff_seconds": 5,
  "max_backoff_seconds": 30,
  "request_timeout_seconds": 30,
  "log_level": "INFO",
  "log_file": "logs/boatrace-{DATE}.json"
}

Performance


Data Source

Official Boatrace Races Server: http://www1.mbrace.or.jp/od2/


License

MIT License

穴予想 v9_suji の運用(2026-08-12〜2026-08-22、退役)

2026-08-22 退役。B案 v10_kimarite と穴予想スロットが重複し、回収率では 両者の差を示せないため(本番 1,511 レースで共通 2.70 点 / 5 点、回収率 70.7% vs 68.8%)、確率モデルを持ち主判定(3連単 log-loss)に載る B案を残した。 検定と体験指標の記録は scripts/boatrace/predictors/registry.py 冒頭コメント。

止めたのは日次の買い目だけ。月次のテーブル再生成は続いている:

ジョブ 処理 状態
monthly-weights build_suji_table.py 継続。kimarite_table.csv を v10_kimarite が読む
daily-sync build_suji_picks.py --mode daily 削除
preview-realtime build_suji_picks.write_day(..., realtime) active_predictors() のゲートで自動停止(コードは残置)
gcs_publisher CsvUploadSpec("suji", ...) 削除

build_suji_picks.py / build_suji_table.py は消さないこと — build_kimarite_picks.py が両方から import している。

穴予想の買い目は boatracecsv 側で確定させて CSV に出す(fun-site は表示と集計のみ)。 そのため他の予想者と違い、index に加えて 2 系統のファイルが要る。

ジョブ 追加された処理 出力
monthly-weights(毎月 1 日 06:00 JST) scripts/build_suji_table.py data/estimate/suji/tables/{suji_table,kimarite_table}.csv
daily-sync(07:30 JST) scripts/build_suji_picks.py --mode daily data/estimate/suji/YYYY/MM/DD.csv(状態=daily)
preview-realtime(締切 5 分前) build_suji_picks.write_day(..., realtime) を内部呼び出し 同上(状態=realtime を upsert)

依存順序: build_suji_picks.py は build_index.py(強さpt)と build_suji_table.py(スジ表)の両方の出力を読む。どちらかが欠けている場合は 作り方を示すメッセージ付きで落ちる(素の FileNotFoundError にはしない)。

sparse-checkout: 静的テーブルは data/estimate/suji/tables、日次の買い目は data/estimate/suji/${TODAY_YM} と別パスで指定している。同じ階層に置くと cone-mode が日次ファイルの全履歴まで checkout してしまうため。

ロールバック: registry.py / fun-site predictors.ts の v9_suji を status: retired にすると active_predictors() から外れ、preview-realtime / build_index / GCS ミラー / 集計すべての対象から自動的に落ちる。 infra/run*.sh の ACTIVE_PREDICTORS も同期すること。

穴予想 v10_kimarite の運用(2026-08-13〜)

B案。買い目を CSV で配る点は A案と同じだが、荒れ度メーターと同じ Stage1 モデルを土台にするので依存が 1 段深い。

依存順序(これを崩すと買い目が 0 行になる):

build_index.py --predictor v10_kimarite   … 強さpt
build_kimarite_probs.py                   … Stage1 の 32 クラス確率
        ↓
build_kimarite_picks.py                   … 合成 → ブレンド → 上位 5 点

Stage1 の確率が無いレースは 無言でスキップする(買い目を出さない)。 index が無い場合は作り方を示して落ちる。

sparse-checkout: 買い目は data/estimate/kimarite/picks/${TODAY_YM} を 独立したパスとして追加している(data/estimate/kimarite/${TODAY_YM} と別)。

退役判定: data/estimate/kimarite/tables/logloss.csv の 95%CI下限 が 0 を下回ったら退役する。回収率では判定しない(A案との差 +4.2pt を有意に するには 8.2 ヶ月かかる)。詳細は docs/design/ana_prediction.md §13.3。

この CSV は monthly-weights が毎月 1 日に再生成する。初回の生成は 2026-09-01 の実行(sparse-checkout / git add の取りこぼしを 2026-08-22 に修正したため、 それ以前はリポジトリに存在しなかった)。手元で先に見たい場合は python scripts/build_kimarite_logloss.py。

荒れ度メーターの運用(2026-08-12〜)

予想者に紐づかない独立の指標なので、ACTIVE_PREDICTORS とは無関係に毎回動く。

ジョブ 処理 出力
monthly-weights build_kimarite.py(全履歴で再学習) data/estimate/kimarite/tables/cell_coef_*.csv
monthly-weights build_kimarite_pairs.py(Stage2 再生成) data/estimate/kimarite/tables/pair_table.csv
monthly-weights build_kimarite_calibration.py data/estimate/kimarite/tables/calibration.csv
monthly-weights build_kimarite_logloss.py(B案の主判定) data/estimate/kimarite/tables/logloss.csv
daily-sync build_kimarite_probs.py --mode daily data/estimate/kimarite/YYYY/MM/DD.csv
daily-sync build_kimarite_picks.py --mode daily(v10_kimarite の買い目) data/estimate/kimarite/picks/YYYY/MM/DD.csv
preview-realtime write_day(..., realtime) を内部呼び出し(probs → picks の順) 同上(realtime を upsert)

「全履歴で再学習」は cone の広さ次第: build_kimarite.py は data/results/realtime/ の全日をループするが、その日の race_cards が cone に無いと無言でスキップする。2026-08-22 まで monthly-weights の sparse-checkout は race_cards / previews/{tkz,sui} を 8 ヶ月ぶんしか 持っていなかったため、実際の学習母数は直近 8 ヶ月だった(43,595 → 30,160 レース = 31% 欠落。しかも毎月 1 ヶ月ずつ拡大)。現在は 3 ファミリとも全履歴を cone に入れてある。ジョブログの races=NNNNN (YYYY-MM-DD 〜 YYYY-MM-DD) の左端が data/results/realtime/ の最古日と一致しているかで再発を検知できる。詳細は infrastructure.md。

学習と推論を分けている理由: 推論を sklearn 非依存にしておくと、 毎回の直前バッチでモデルを読み込む必要がなく、係数 CSV さえあれば動く。 係数のクラス構成が boatrace.kimarite.CELLS とズレていたら 黙って動かず落ちる(古い係数で推論して静かに壊れるのを防ぐ)。