JV-Link 時系列オッズの取込と直前トレンドの可視化
JV-Link 時系列オッズの取込と直前トレンドの可視化
JV-Link には「時系列オッズ」という取込モードがあり、レース直前のオッズ推移を分単位で取得できます。終盤の急変動を捉えると、人気だけでは見えない「裏人気」「直前で買われた馬」が判ります。この記事では取込から可視化までの実例を紹介します。
時系列オッズで何が見えるか
通常のオッズは確定値だけが残りますが、時系列オッズは発走 90 分前くらいから 1〜5 分間隔で記録が入り続けます。
- 直前に買われた馬: 終盤 10 分でオッズが急降下した馬は「情報を持った人が買った」可能性
- 逃げ消し / 飛びそう感: 朝は人気だったのに終盤に売られた馬
- 市場のばらつき: 安定したオッズか、流動的か
これらは LightGBM 等の特徴量にも、人間の最終判断材料にも使えます。
取込の基本
JvLink To Importer では取込モードで「時系列オッズ」を有効化すると、レース日の発走前に自動で取り込まれます。データは概ね以下のような形で蓄積されます。
| カラム | 説明 |
|---|---|
| race_id | レース識別子 |
| horse_id | 馬識別子 |
| recorded_at | 記録時刻 (タイムスタンプ) |
| win_odds | 単勝オッズ |
| place_odds_low / place_odds_high | 複勝オッズ範囲 |
PostgreSQL なら recorded_at を TIMESTAMPTZ にしておくとタイムゾーン処理が楽です。
SQL: 各馬の最終10分のオッズ変動を集計
WITH last_window AS (
SELECT
race_id,
horse_id,
recorded_at,
win_odds,
-- 発走時刻からの相対時刻 (分)
EXTRACT(EPOCH FROM (post_time - recorded_at)) / 60 AS minutes_before
FROM time_series_odds tso
JOIN races r USING (race_id)
WHERE EXTRACT(EPOCH FROM (post_time - recorded_at)) / 60 BETWEEN 0 AND 60
)
SELECT
race_id,
horse_id,
-- 発走 60 分前のオッズ
FIRST_VALUE(win_odds) OVER w AS odds_60min,
-- 発走 5 分前のオッズ
LAST_VALUE(win_odds) FILTER (WHERE minutes_before <= 5) OVER w AS odds_5min,
-- 60 分→5分 の変動率
(LAST_VALUE(win_odds) FILTER (WHERE minutes_before <= 5) OVER w
- FIRST_VALUE(win_odds) OVER w)
/ FIRST_VALUE(win_odds) OVER w AS odds_change_ratio
FROM last_window
WINDOW w AS (PARTITION BY race_id, horse_id ORDER BY recorded_at);
odds_change_ratio がマイナス(オッズが下がった)かつ絶対値が大きい馬が「直前で買われた馬」候補です。
Python で可視化
pandas + matplotlib で1レースの全馬オッズ推移を一気に描画します。
import pandas as pd
import matplotlib.pyplot as plt
# race_id を絞ってロード
df = pd.read_sql(f"""
SELECT recorded_at, horse_id, horse_name, win_odds
FROM time_series_odds
JOIN horses USING (horse_id)
WHERE race_id = '{race_id}'
ORDER BY horse_id, recorded_at
""", conn)
fig, ax = plt.subplots(figsize=(12, 6))
for horse_id, group in df.groupby('horse_id'):
name = group['horse_name'].iloc[0]
ax.plot(group['recorded_at'], group['win_odds'], label=name, alpha=0.7)
ax.set_yscale('log') # オッズは対数軸が見やすい
ax.invert_yaxis() # 上から人気順 (低オッズ) に
ax.set_xlabel('時刻')
ax.set_ylabel('単勝オッズ')
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
plt.savefig('odds_trend.png')
対数軸 + 反転で「人気馬は上、人気薄は下」の見た目になり、最終 5〜10 分の変動が一目で見えます。
集計値を予想モデルに使う
時系列オッズから得られる特徴量で予測精度に効きやすいのは:
- odds_change_ratio (60min → 5min): 直前で買われた指標
- odds_volatility: 標準偏差。動きが大きい馬は不安定
- rank_change: 60 分前と 5 分前のオッズ順位の変化
- late_consensus: 発走 5 分前のオッズが終始安定していた → 市場の確信
注意点
- 時系列オッズの記録間隔は環境(取り込み頻度の設定)に依存します。1 分に 1 回入る環境を作るには取込スケジュールを密にする必要があります。
- データ量はレース数 × 馬数 × 記録回数で爆発しがち。インデックス設計(
(race_id, horse_id, recorded_at))と古いレースの集約パーティション化を早めに検討してください。 - 取得失敗(通信不良)で穴あきになる箇所があるので、可視化や集計時に欠損を補完するロジックを入れておくと安心です。
まとめ
時系列オッズは「発走前のマーケットがどう動いたか」を後から検証できる貴重なデータです。確定オッズだけ見ていては気付けない馬が浮き上がってくるので、可視化だけでも価値があります。慣れたら集計値を機械学習の特徴量に投入してみてください。
この記事をシェア