帝王賞2023を最適化問題で解いたらどうだったか?
結果から言うと
安易な考えすぎて、解が出ませんでした。
最適化問題とは・・・?
こちらより引用させていただきました。
過程
2023年の帝王賞は12頭立て、内JRA勢が7頭
実力的にはJRA勢が揃いすぎた感じのメンバーで、
ほぼ間違いなく上位2頭にはJRAの馬が入りそう。
この7頭という頭数だったら、
絶対プラスになる買い方があるのではないか、
と思った次第です。
そこで最適化問題の登場です。
参考
こちらに丁度よいPythonのソースコードあったので参考にさせていただきました。
解いてみた
「JRA勢が上位1,2着に入る」という条件付きになりますが、必ずプラスになる買い方を見つけようという試みです。
単勝オッズと馬単オッズを使用します。
オッズや馬番などは公式から参考にしました。
コーディングはPythonで。取得したオッズデータ等をDataFrameに格納します(df)。
因みにオッズデータはこんな感じになってます。上の方に単勝オッズが並び、その後、馬単オッズが続いています。
First: 1位予想馬
Second: 2位予想馬(-1の場合は単勝)
Odds: オッズ
Pythonコード
最適解を出すメイン部分の関数になります。(上記参考サイトより)
from ortoolpy import addbinvars, lpSum, model_min, pd
def solve(df: pd.DataFrame, num: int, alpha: float) -> pd.DataFrame:
"""購買数を求める
:param df: 変数表
:param num: 購買数上限
:param alpha: リターン比
:return: 変数表(Val列が購買数)
"""
n = len(df[df.Second == -1]) # 単勝の行数
m = model_min(dfi=df) # 数理モデル
df["Mono"] = None # 単勝を買うかどうか
df.loc[:n-1, "Mono"] = addbinvars(n)
m += lpSum(df.Var) # 目的関数(総購買数)
m += lpSum(df.Var) <= num # 購買数上限
# 単勝
for row in df[:n].itertuples():
m += row.Odds * row.Var >= num * alpha * row.Mono
df.loc[df.First == row.First, "Mono"] = row.Mono
# 馬単
for row in df[n:].itertuples():
m += row.Odds * row.Var >= num * alpha * (1 - row.Mono)
# こんな感じで使うみたい
# m.solve(objs=['-XXX', 'YYY'])
m.solve()
# df['Val'] に結果が入る
try:
df["Prize"] = df.Odds * df.Val * 100
return df[df.Val > 0] if m.status==1 else None
except Exception as e:
print(f'[Warning] 解なし m.status = {m.status}, {e}')
return pd.DataFrame() # 解がなければ空を返す
res = solve(df, num=1000, alpha=1.05) # num: 百円単位
res
条件:100,000円を賭ける
回収率を105%に設定
計算結果
この条件でやってみましたが、解が出ませんでした。
ちょっと安易すぎたか。
条件変更
res = solve(df, num=500, alpha=0.88) # num: 百円単位
条件:50,000円を賭ける
回収率を88%に設定
この条件にしたら一応解は出ましたが、必ず12%は負ける賭け方なので、駄目ですね。Valがベット数で、Prizeが返ってきた額です。
50,000円賭けて、大体44,000〜 46,000円位のリターンになってますね。
表の見方
1行目:4番(メイショウハリオ)の単勝に11,300円賭けて44,070円の払い戻し
4行目:1番(テーオーケインズ)->4番(メイショウハリオ)の馬単に5,800円賭け て44,160円の払い戻し
今回の帝王賞は「解なし」という結果になりましたが、少頭数のレースを見つけて、またいつか検証してみようと思います。
以上になります。またお会いしましょう
この記事が気に入ったらサポートをしてみませんか?