足球博彩中怎么进行套利交易(二)
在上一节中,我们对球队名称标准化的问题做了总结.我承认这不是一个完美的方法,因为足球数据里会不断出现新的联赛,新的球队,新的杯赛名称,以及不同网站对同一支球队的不同翻译.
所以在真实系统里,我不认为存在一个一劳永逸的球队名称归一化方案.更现实的答案是:先建立稳定的标准化规则,再持续运行归一化脚本,把新出现的特殊情况不断修订进去.足球数据的标准化不是一次性工程,而是一个需要长期维护的基础设施.
这篇文章的内容建立在球队名称已经统一以后.也就是说,我们已经可以确认不同数据源里的“阿根廷 VS 佛得角”指向同一场比赛.接下来要解决的问题是:同一场比赛下,不同玩法,不同盘口,不同博彩公司之间,怎样判断它们能不能组成套利结构.
标准化以后的数据大致会长成这样:
id,starttime,league,event,dealer,lay,bettype,handicap,odd_home,odd_draw,odd_away,currency,Max_amount,Min_amount,other,timeStamp
15889684,2023-09-28 08:30,自由杯,弗鲁米嫩 VS 巴西国际,no,360,over,2,1.8,0.0,0.0,cny,0,0,,2023-09-27 13:53
15889685,2023-09-28 08:30,自由杯,弗鲁米嫩 VS 巴西国际,no,360,under,2,0.0,0.0,2.05,cny,0,0,,2023-09-27 13:53
每一行都是同一场比赛下的一条盘口信息.它告诉我们:比赛是谁对谁,数据来自哪个 dealer,玩法是什么,盘口线是多少,主胜/平/客胜或者大小球方向的赔率是多少.
但是到这里还不能直接计算套利.因为一条数据只是一个报价,它本身并不知道自己和另外一条数据在规则上是什么关系.我们还需要先建立博彩规则映射.
问:为什么要先建立足球博彩规则映射?
套利交易的核心不是“看起来赔率很高”,而是“无论比赛结果是什么,都有一部分下注能够覆盖这个结果”.
例如 moneyline 有主胜,平局,客胜三种结果.如果我能在不同公司分别拿到足够高的主胜,平局,客胜赔率,那么这三条下注腿可以覆盖 90 分钟内的所有胜平负结果.
但是足球博彩不只有 moneyline.常见玩法包括:
| 玩法 | 常见含义 |
|---|---|
| moneyline | 胜平负 |
| moneyline2in1 | 二选一胜平负变体 |
| asiahandicap | 亚洲让球 |
| over / under | 大小球 |
| total | 总进球数类玩法 |
| correct score | 精确比分 |
| BTTS | 双方是否进球 |
这些玩法并不总是在同一个维度里.例如全场 moneyline 不能和半场大小球混在一起计算,因为它们结算的比赛范围不同.全场 90 分钟规则下的结果,半场 45 分钟规则下的结果,加时赛规则下的结果,都应该被视为不同维度.
所以第一条原则是:只有同一个比赛维度下的玩法,才可以放进同一个套利模型中.
足球常规盘口通常按 90 分钟结算,包括伤停补时,不包括加时赛和点球大战.举一个规则例子:假设某场比赛 90 分钟比分是 1:1,加时赛以后变成 3:2.对于本文讨论的全场 moneyline,asiahandicap,over/under 等常规盘口,结算依据仍然是 90 分钟的 1:1,不是加时赛后的 3:2.
这件事看似简单,但非常重要.如果不同数据源或不同盘口的结算范围不一致,就算公式上看起来存在套利,真实执行时也可能不是同一个风险空间.
问:moneyline 套利为什么最容易理解?
moneyline 是最直观的例子.它把一场足球比赛的 90 分钟结果分成三类:
- 主队胜.
- 平局.
- 客队胜.
如果我们分别下注这三类结果,并且下注金额按赔率比例调整,就有可能做到三种结果下返还金额都大于总投入.
假设某场比赛不同博彩公司提供了如下赔率:
| 投注项 | 博彩公司 | 十进制赔率 |
|---|---|---|
| Moneyline Home 主胜 | Bookmaker A | 2.30 |
| Moneyline Draw 平局 | Bookmaker B | 3.70 |
| Moneyline Away 客胜 | Bookmaker C | 4.20 |
先做最基础的倒数和判断:
\[ \frac{1}{2.30}+\frac{1}{3.70}+\frac{1}{4.20}=0.9428<1 \]
由于三项赔率倒数之和小于 1,说明存在理论套利空间.
如果计划投入总资金 1000 元,下注金额不是三项平均分配,而是按赔率倒数比例分配:
| 投注项 | 赔率 | 投注金额 |
|---|---|---|
| Moneyline Home 主胜 | 2.30 | 460.63 元 |
| Moneyline Draw 平局 | 3.70 | 286.36 元 |
| Moneyline Away 客胜 | 4.20 | 253.01 元 |
| 合计 | 1000.00 元 |
最终返还如下:
| 90 分钟赛果 | 返还金额 | 净利润 |
|---|---|---|
| 主胜 | 460.63 × 2.30 = 1059.45 元 | 59.45 元 |
| 平局 | 286.36 × 3.70 = 1059.53 元 | 59.53 元 |
| 客胜 | 253.01 × 4.20 = 1062.64 元 | 62.64 元 |
这就是最基础的三项盘套利.核心原则是:套利不是让每一项下注金额相等,而是根据赔率调整资金比例,让不同赛果下的返还金额尽可能接近.
moneyline 之所以容易理解,是因为它只有三种互斥结果,并且每一条下注腿只有全赢或全输.但是足球博彩真正复杂的地方在于,很多盘口不是简单的赢和输.
问:为什么不能只用赔率倒数判断所有套利?
因为 asiahandicap 和 over/under 里存在走水,半赢,半输.
例如大小球 2.0:
| 总进球数 | Over 2.0 | Under 2.0 |
|---|---|---|
| 大于 2 球 | 全赢 | 全输 |
| 正好 2 球 | 走水 | 走水 |
| 小于 2 球 | 全输 | 全赢 |
再例如亚洲让球 -0.25:
| 赛果 | 下注主队 -0.25 |
|---|---|
| 主队赢球 | 全赢 |
| 平局 | 半输 |
| 主队输球 | 全输 |
这些盘口不能简单地套用 moneyline 的倒数和公式.因为某些比分状态下,一条下注腿可能不是全赢,也不是全输,而是半赢,半输或退回本金.
所以我们需要把每一种玩法都转换成一个更通用的表达:它在每一个可能比分下的结算结果是什么.
问:怎么用脚本计算出能够全包的玩法组合?
我的方法是建立一套基于穷举比分的多维度模型.
为了便于说明,先把足球比赛的进球数限制在 0,1,2.那么可能比分一共有 9 种:
| 比分状态 |
|---|
| 0:0 |
| 0:1 |
| 0:2 |
| 1:0 |
| 1:1 |
| 1:2 |
| 2:0 |
| 2:1 |
| 2:2 |
在这 9 种比分里:
moneyline_home 中奖的比分是:
1:0, 2:0, 2:1
moneyline_draw 中奖的比分是:
0:0, 1:1, 2:2
moneyline_away 中奖的比分是:
0:1, 0:2, 1:2
为了把 moneyline,亚盘,大小球放进同一个模型里,我把结算结果统一成五种代码:
| 代码 | 含义 |
|---|---|
| 0 | 全输 |
| 50 | 半输 |
| 100 | 走水 |
| 150 | 半赢 |
| 200 | 全赢 |
moneyline 只有全赢和全输,所以它的收益矩阵可以写成:
| Bet Type | 0:0 | 0:1 | 0:2 | 1:0 | 1:1 | 1:2 | 2:0 | 2:1 | 2:2 |
|---|---|---|---|---|---|---|---|---|---|
| ML Home | 0 | 0 | 0 | 200 | 0 | 0 | 200 | 200 | 0 |
| ML Draw | 200 | 0 | 0 | 0 | 200 | 0 | 0 | 0 | 200 |
| ML Away | 0 | 200 | 200 | 0 | 0 | 200 | 0 | 0 | 0 |
这个表格就是规则映射.它不是赔率表,而是玩法在每一种比分下的结算表.
如果某个比分状态下,ML Home 的代码是 200,意思是主胜下注全赢.如果代码是 0,意思是这条下注腿在这个比分下全输.
问:收益矩阵怎样扩展到更多玩法?
同样的方法可以扩展到亚洲让球,大小球,双方进球,精确比分等玩法.
例如在同样的 9 个比分状态下,加入一些常见玩法后,矩阵可以写成:
| Bet Type | 0:0 | 0:1 | 0:2 | 1:0 | 1:1 | 1:2 | 2:0 | 2:1 | 2:2 |
|---|---|---|---|---|---|---|---|---|---|
| ML Home | 0 | 0 | 0 | 200 | 0 | 0 | 200 | 200 | 0 |
| ML Draw | 200 | 0 | 0 | 0 | 200 | 0 | 0 | 0 | 200 |
| ML Away | 0 | 200 | 200 | 0 | 0 | 200 | 0 | 0 | 0 |
| AH -1 | 0 | 0 | 0 | 100 | 0 | 0 | 200 | 200 | 100 |
| AH -0.75 | 0 | 0 | 0 | 150 | 0 | 0 | 200 | 200 | 50 |
| AH -0.5 | 0 | 0 | 0 | 200 | 0 | 0 | 200 | 200 | 0 |
| AH -0.25 | 50 | 0 | 0 | 200 | 50 | 0 | 200 | 200 | 50 |
| AH +0 | 100 | 0 | 0 | 200 | 100 | 0 | 200 | 200 | 100 |
| AH +0.25 | 150 | 0 | 0 | 200 | 150 | 0 | 200 | 200 | 150 |
| AH +0.5 | 200 | 0 | 0 | 200 | 200 | 0 | 200 | 200 | 200 |
| O2.5 | 0 | 0 | 0 | 0 | 0 | 200 | 0 | 200 | 200 |
| U2.5 | 200 | 200 | 200 | 200 | 200 | 0 | 200 | 0 | 0 |
| BTTS Yes | 0 | 0 | 0 | 0 | 200 | 200 | 0 | 200 | 200 |
| BTTS No | 200 | 200 | 200 | 200 | 0 | 0 | 200 | 0 | 0 |
| Correct Score 0:0 | 200 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Correct Score 0:1 | 0 | 200 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Correct Score 0:2 | 0 | 0 | 200 | 0 | 0 | 0 | 0 | 0 | 0 |
| Correct Score 1:0 | 0 | 0 | 0 | 200 | 0 | 0 | 0 | 0 | 0 |
| Correct Score 1:1 | 0 | 0 | 0 | 0 | 200 | 0 | 0 | 0 | 0 |
| Correct Score 1:2 | 0 | 0 | 0 | 0 | 0 | 200 | 0 | 0 | 0 |
| Correct Score 2:0 | 0 | 0 | 0 | 0 | 0 | 0 | 200 | 0 | 0 |
| Correct Score 2:1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 200 | 0 |
| Correct Score 2:2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 200 |
这张表的意义是:每一种玩法都被转换成了一个收益向量.
例如:
\[ MLHome=(0,0,0,200,0,0,200,200,0) \]
\[ U2.5=(200,200,200,200,200,0,200,0,0) \]
如果几条下注腿的收益向量加起来,能在每一个比分状态下都形成覆盖,那么它们才有可能组成套利组合.
问:这张矩阵解决了什么问题?
它解决了三个问题.
第一,它把不同玩法放进了同一个坐标系.不管是 moneyline,asiahandicap,over/under,还是 correct score,最后都变成“在每个比分下返回什么代码”.
第二,它让脚本可以自动判断覆盖关系.如果一组下注腿在某些比分下全部为 0,说明这组下注腿没有覆盖全结果,不可能是安全套利.
第三,它为下一步计算下注比例打好了基础.矩阵里的 200,150,100,50,0 还不是最终金额,但它们可以根据真实赔率转换成返还倍数.比如全赢代码 200 会转换成赔率 \(O\),半赢代码 150 会转换成 \((O+1)/2\),走水代码 100 会转换成 1.
所以从这一刻开始,套利就不再只是一个赔率倒数问题,而是一个收益向量的线性组合问题.
下一篇继续写:当我们已经有了收益矩阵以后,怎样把真实赔率代入进去,解出每一条下注腿应该投入多少比例,并判断最差比分下是否仍然盈利.