0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

08_競馬の期待値(EV)の計算方法:AUCが高くてもROIが低い理由

0
Posted at

はじめに

競馬AIを作る人が最初にハマる罠のひとつが「AUCが高ければROIも高いはず」という思い込みです。

実際には、AUCが高くてもROIは低いことがあります。その理由と、ROIを改善するための「期待値(EV)」の計算方法を解説します。


AUCとROIの違い

**AUC(Area Under the Curve)**は、モデルが「当たる馬と外れる馬を正しい順序で並べられるか」を測る指標です。0.5がランダム、1.0が完璧な予測。

**ROI(Return on Investment)**は、実際に賭けたときの回収率です。投資100円に対して払戻が120円なら、ROI = 120%。

なぜ両者がズレるのか?

AUCは「順序の正確さ」を測りますが、実際に儲けるためには「正しい金額を正しいタイミングで賭ける」必要があります。

例えば、単勝1.1倍の馬を80%の確率で的中させるモデルがあるとします。

  • AUCは高い
  • 期待値 = 0.8 × 1.1 = 0.88 → ROIはマイナス

一方、単勝10倍の馬を15%の確率で的中させるモデルがあるとします。

  • AUCへの貢献は少ない
  • 期待値 = 0.15 × 10 = 1.5 → ROIはプラス

重要なのは「当たる確率 × オッズ」が1を超えるかどうかです。


期待値の計算式

期待値(EV) = 予測勝率 × 実際に支払われるオッズ

EVが1.0より大きいとき、理論的には長期的にプラス収支が期待できます。

ただし注意:競馬のオッズには控除率(JRAは約25%)が含まれているため、ランダムに買い続けるとROI 75%になります。EV > 1.0の馬だけを選ぶことで、市場平均を上回るROIを狙います。


Pythonでの期待値計算

import pandas as pd
import numpy as np


def calculate_expected_value(
    prob: float,
    odds: float
) -> float:
    """
    期待値(EV)を計算する。

    Args:
        prob: モデルの予測確率(0〜1)
        odds: オッズ(例:単勝5倍なら5.0)

    Returns:
        期待値(1.0以上で理論的にプラス)
    """
    return prob * odds


def calculate_ev_for_dataframe(
    df: pd.DataFrame,
    prob_col: str,
    odds_col: str
) -> pd.DataFrame:
    """
    DataFrameに期待値列を追加する。
    """
    df = df.copy()
    df['ev'] = df[prob_col] * df[odds_col]
    return df

単勝・複勝・馬連の期待値計算

単勝の期待値

def calculate_win_ev(win_prob: float, win_odds: float) -> float:
    """単勝の期待値"""
    return win_prob * win_odds


# 例:単勝5倍の馬をモデルが25%の確率と予測
ev = calculate_win_ev(0.25, 5.0)
print(f"単勝EV: {ev:.2f}")  # 1.25 → EV > 1.0なので買い候補

複勝の期待値

複勝は「3着以内に入る確率」を使います。

def calculate_place_ev(place_prob: float, place_odds: float) -> float:
    """複勝の期待値(3着以内確率 × 複勝オッズ)"""
    return place_prob * place_odds


# 例:複勝1.5倍の馬をモデルが60%の確率で3着以内と予測
ev = calculate_place_ev(0.60, 1.5)
print(f"複勝EV: {ev:.2f}")  # 0.90 → EV < 1.0なので見送り

馬連の期待値

馬連は「2頭の組み合わせの期待値」なので、少し複雑です。

def calculate_exacta_ev(
    prob_horse_a_top2: float,  # 馬Aが2着以内の確率
    prob_horse_b_top2: float,  # 馬Bが2着以内の確率
    exacta_odds: float,        # 馬連オッズ
    n_horses: int = 16         # 出走頭数(近似計算用)
) -> float:
    """
    馬連の期待値を近似計算する。
    正確な計算はモデルの同時確率が必要だが、
    近似として独立仮定を使う。
    """
    # 馬AとBの両方が2着以内に入る確率の近似
    # (厳密には独立ではないが、近似として使える)
    joint_prob = prob_horse_a_top2 * prob_horse_b_top2 * n_horses / (n_horses - 1)
    return joint_prob * exacta_odds

EV閾値の設計

「EVがいくら以上のときに買うか」という閾値(min-EV)の設計は、ROIに大きく影響します。

def analyze_ev_roi_by_threshold(
    df: pd.DataFrame,
    ev_col: str = 'ev',
    odds_col: str = 'odds',
    label_col: str = 'won',
    bet_amount: int = 100
) -> pd.DataFrame:
    """
    EV閾値ごとのROIを分析する。
    """
    thresholds = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 2.0]
    results = []

    for threshold in thresholds:
        filtered = df[df[ev_col] >= threshold]

        if len(filtered) == 0:
            continue

        investment = len(filtered) * bet_amount
        payout = (filtered[label_col] * filtered[odds_col] * bet_amount).sum()
        roi = payout / investment if investment > 0 else 0

        results.append({
            'min_ev': threshold,
            'n_bets': len(filtered),
            'investment': investment,
            'payout': int(payout),
            'roi': round(roi, 4)
        })

    return pd.DataFrame(results)


# 使い方
results = analyze_ev_roi_by_threshold(df)
print(results.to_string(index=False))

出力例:

 min_ev  n_bets  investment    payout    roi
    1.0  108339  10833900  10872390  1.0036
    1.1   50000   5000000   5200000  1.0400
    1.2   30000   3000000   3270000  1.0900
    1.3   15000   1500000   1720000  1.1467

私のシステムでは、EV 1.0〜1.1帯のROIが89〜93%と赤字だったため、min-EV 1.2以上という設定に変更しました。


AUCが高くてもROIが低い理由のまとめ

  1. AUCは確率の「順序」を評価する → 人気馬を正しく上位に置けばAUCは高い
  2. 人気馬のオッズは低い → EV = 確率 × オッズが1を超えにくい
  3. AIが人気馬を正確に予測するほど、人気馬のオッズが下がる → 市場との悪循環

解決策:

  • 高EV(穴馬)に絞って賭ける
  • min-EVの閾値を適切に設定する
  • モデルの確率とオッズを両方活用する設計にする

詳細な実装について

EV計算の完全実装(複数の券種対応)や、EVフィルタの設計思想は**Part1:設計編**で解説しています。また、実際のバックテストでのEV分布とROIの関係も詳細に分析しています。


0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?