Type/to search
2
Follow
490
Followers
在套利和预测之间: 一次世界杯路径收敛策略的朴素实验
Discussions
Created 2026-06-18 09:09:14  Updated 2026-06-22 15:37:13
 0
 206

img

世界杯刚开幕,市场就给了我们一点小小的震撼教育:强队不是不会翻车,只是翻车姿势往往比我们想象得更有创意。

比如西班牙 0:0 佛得角。赛前如果只看实力、排名、阵容深度,大多数人的直觉都会很朴素:西班牙应该能拿下吧?但足球最有意思,也最折磨交易者的地方就在这里。你以为你买的是“强队胜”,实际上你买的是一整段 90 分钟的命运连续剧。前 15 分钟没进,赔率动一下;半小时还是 0:0,市场开始皱眉;到了 70 分钟还没动静,原本看起来稳稳的强队胜,突然就不那么稳了。

img

这时候会遇到一个很经典的矛盾:套利很好,预测很难。

真正的套利,理论上应该是无风险的。比如找到一组互斥且完备的合约,无论最后发生什么,组合都能兑付,并且买入成本低于兑付金额。听起来很美好,像交易员版本的午睡。但问题也在这里:这样的机会太少,出现时往往很快消失,容量也未必够。

预测则是另一回事。你可以判断强队大概率赢,可以分析阵容、状态、赛程、伤病,也可以看赔率隐含概率,但比赛不会因为我们分析得很认真就配合演出。足球尤其如此,一个早早的红牌、一个门柱、一次离谱的 VAR,都可能让原本漂亮的逻辑变成赛后复盘里的沉默三秒。

img

所以问题来了:如果纯套利太稀缺,纯预测又太随机,我们能不能站在两者中间?市场本身已经给了我们一些前提概率,比如强队胜的价格、0:0 的价格、0:1 的价格。这些价格不是凭空来的,它们反映了市场对不同路径的集体定价。那我们能不能利用这些前提概率,再加上一点自己的数学模型,构造一个不完美、但更有保护性的组合,去撬开一个“路径收敛”的窗口?

这就是这个想法的起点。

它不是传统的无风险套利,也不是单纯拍脑袋预测。它更像是一种折中:先承认市场给出的主概率有参考价值,再用低概率路径去保护主判断里最脆弱的部分,同时用一个简单模型检查价格是不是还能接受。

假设一场比赛里,巴西明显强于海地。市场给出的巴西胜价格是 0.89,说明巴西赢是一个很强的主路径。但我们不直接只买巴西胜,而是同时观察两个保护路径:0:0 和 0:1。因为对于一个强队胜的判断来说,最难受的早期剧本往往不是所有结果,而是比赛迟迟打不开,或者弱队偷一个。

于是初始组合变成:

text
巴西胜 Yes 0:0 Yes 0:1 Yes

假设价格是:

text
巴西胜 = 0.89 0:0 = 0.016 0:1 = 0.011

三条腿合计成本:

text
C = 0.89 + 0.016 + 0.011 = 0.917

这组东西并不是完备事件。它没有覆盖 1:1、0:2、2:2,也没有覆盖所有奇怪剧本。所以如果持有到终场,它当然可能亏,而且可能亏得很直接。但它有一个很重要的特点:它把市场已经给出的三个概率拼在一起,形成了一个围绕主胜的路径篮子。

如果最终巴西赢,组合兑付 1。如果最终 0:0 或 0:1,也兑付 1。也就是说,它买入的是:

img

text
巴西胜 ∪ 0:0 ∪ 0:1

只要这些路径之一发生,到期兑付就是 1。成本是 0.917,覆盖路径内有 0.083 的空间。但真正的关键不在终场,而在中途。

如果巴西早早进球,比分来到 1:0,那么 0:0 和 0:1 这两条保护腿基本归零,但巴西胜价格会上升。只要此时巴西胜的可卖价格超过初始总成本,再加上一点目标利润,就可以平掉主胜,把这次组合提前收敛。

公式很简单:

text
止盈条件 = 巴西胜 bid >= 初始总成本 C + 目标利润

比如目标利润设为 0.02:

text
触发价 = 0.917 + 0.02 = 0.937

如果 1:0 之后巴西胜 bid 到了 0.95,那么:

text
锁定利润 = 0.95 - 0.917 = 0.033

这时候我们并不是靠终局开奖赚钱,而是靠比赛路径进入了一个有利分支,把组合提前卖掉。这个过程有点像市场给你开了一扇小窗,你别站在窗边写诗,先把窗户钻过去再说。

但这里还有一个问题:三条腿看起来便宜,就一定值得买吗?不一定。市场价格只是第一层信息。我们还需要一个自己的参考尺子,哪怕它很朴素。

这里我用了最基础的泊松进球模型。

足球比分可以粗略看成两个队在 90 分钟内各自进球的随机过程。假设主队 90 分钟预期进球数是 λ_home,客队预期进球数是 λ_away,那么主队进 i 个球、客队进 j 个球的概率可以写成:

text
P(i, j) = Pois(i; λ_home) × Pois(j; λ_away)

其中:

text
Pois(k; λ) = e^(-λ) × λ^k / k!

img

它当然很粗糙。足球不是两个独立的抽奖机器,进球后战术会变,红牌会变,心态也会变。但它的好处是简单、透明,至少能让我们不要完全凭感觉下手。

在赛前,泊松模型可以先给我们一个初始参考。我们可以手动给出 λ_homeλ_away,也可以让程序从市场的精确比分盘口里反推。比如 0:0、0:1、1:0、1:1、2:0、2:1、3:0 这些比分盘口,每一个都相当于市场给出的一个概率点。程序要找一组 λ_homeλ_away,让泊松模型算出来的比分概率,尽量接近这些市场价格。

换句话说,市场在说:“我觉得这些比分大概值这些钱。” 泊松模型在旁边小声说:“那我试着拟合一下,看看这套价格背后隐含的是怎样的进球强度。”

策略里可以配置:

python
CALIBRATE_LAMBDA_FROM_MARKET = True MODEL_SCORE_SAMPLES = "0-0,0-1,1-0,1-1,2-0,2-1,3-0"

这些比分只用于建模,不参与下单。真正交易的腿仍然是:

text
目标队胜 0:0 0:1

代码里用网格搜索拟合 λ。它不复杂,但很直观:

python
def fit_lambdas_from_score_markets(quotes, model_score_legs, event_state=None): samples = [] live_score = event_state.get("score_tuple") if event_state else None minute = event_state.get("elapsed") if event_state else None is_live = bool(live_score and minute not in [None, ""]) for leg in model_score_legs: q = quotes.get(leg["name"]) p_market = quote_probability(q) if p_market is None: continue target_h, target_a = parse_score_text(leg["score"]) if is_live: current_h, current_a = live_score if current_h > target_h or current_a > target_a: continue samples.append((target_h - current_h, target_a - current_a, p_market, leg["score"])) else: samples.append((target_h, target_a, p_market, leg["score"])) if len(samples) < 2: return None best = None for ih in range(5, 501, 5): lh = ih / 100.0 for ia in range(5, 501, 5): la = ia / 100.0 err = 0.0 for add_h, add_a, p_market, _score in samples: p_model = poisson_pmf(add_h, lh) * poisson_pmf(add_a, la) err += (p_model - p_market) ** 2 if best is None or err < best["err"]: best = {"lambda_home": lh, "lambda_away": la, "err": err, "samples": samples} if is_live: ratio = max(0.01, max(0.0, 90.0 - float(minute)) / 90.0) best["lambda_home"] = best["lambda_home"] / ratio best["lambda_away"] = best["lambda_away"] / ratio best["source"] = "live_score_markets" else: best["source"] = "pre_match_score_markets" return best

赛前,这个 λ 只是初始校准。因为比赛还没开始,没有实时路径可更新,比分默认还是 0:0,剩余时间就是 90 分钟。这个阶段我们用它做入场过滤:

text
模型覆盖概率 = P(目标队胜) + P(0:0) + P(0:1)

然后要求:

text
模型覆盖概率 - 市场成本 >= 安全边际

只有当市场价格足够便宜,并且模型也认为这个篮子有一点优势时,才允许开仓。

真正有意思的是比赛开始之后。

比赛一旦开始,泊松模型就不再是静态的。假设比赛到了第 30 分钟,当前比分还是 0:0,那么剩余时间只有 60 分钟,未来的进球强度要按剩余时间缩小:

text
λ_home_remaining = λ_home × (90 - t) / 90 λ_away_remaining = λ_away × (90 - t) / 90

如果当前比分已经是 1:0,那 0:0 和 0:1 就不可能再发生。此时模型要从当前比分出发,只计算剩余时间里还会进多少球。终场 2:0、2:1、3:0、1:1 这些仍然可能发生的比分,才有资格参与新的估计。

这就是实时泊松更新的关键:不是每分钟机械地把赛前 λ 缩小,而是结合当前比分、剩余时间和仍然可能发生的精确比分盘口,重新估计接下来这场比赛还可能怎么走。

最终策略变成三层判断。

第一层是市场成本:

text
win_ask + 0:0_ask + 0:1_ask <= 最大允许成本

第二层是泊松过滤:

text
模型覆盖概率 - 市场成本 >= 安全边际

第三层是路径收敛:

text
当前组合 bid 价值 >= 初始成本 + 目标利润

只有第一层和第二层同时满足,才考虑入场。入场之后,不再靠模型幻想终局,而是用真实盘口 bid 判断能不能平仓。模型负责提高开仓质量,盘口负责决定能不能兑现。

合约查找也尽量做得简单。Polymarket 的世界杯市场 slug 很有规律。比如一场比赛的 event slug 是:

python
EVENT_SLUG = "fifwc-aut-jor-2026-06-17"

如果我们保护 Jordan 胜,对应的胜利合约后缀是:

python
WIN_SUFFIX = "jor"

那么交易所需的三个合约可以直接拼出来:

python
def yes_symbol(slug): return slug + "_USDC.Yes" def build_legs(): legs = [ { "name": "win", "slug": EVENT_SLUG + "-" + WIN_SUFFIX, "symbol": yes_symbol(EVENT_SLUG + "-" + WIN_SUFFIX), "kind": "win", } ] for score in parse_scores(PROTECT_SCORES): legs.append( { "name": "score_" + score.replace("-", "_"), "slug": EVENT_SLUG + "-exact-score-" + score, "symbol": yes_symbol(EVENT_SLUG + "-exact-score-" + score), "kind": "score", "score": score, } ) return legs

这里尽量不要用球队名去模糊搜索。比如搜 Jordan,很容易搜出 Michael B. Jordan、Jordan Pickford、Jordan Spieth,最后足球策略还没开始,娱乐圈、英格兰门将和高尔夫先坐一桌了。用 event slug 拼合约,干净很多。

比赛中还需要实时比分。第一版可以直接从 Polymarket Gamma event 里取:

python
def get_event_state(): data = get_json(GAMMA_BASE + "/events", slug=EVENT_SLUG) e = data[0] return { "title": e.get("title"), "score": e.get("score"), "score_tuple": parse_score(e.get("score")), "elapsed": e.get("elapsed"), "period": e.get("period"), "live": bool(e.get("live")), "ended": bool(e.get("ended")), "start_time": e.get("startTime"), }

组合当前可卖价值用真实盘口 bid:

python
def basket_bid_value(legs, quotes): total = 0.0 for leg in legs: pos = positions.get(leg["name"], {}) amount = float(pos.get("amount", 0)) if amount <= 0: continue q = quotes.get(leg["name"]) if not q or q["bid"] is None: continue total += amount * q["bid"] return total

止盈判断:

python
cost = current_position_cost() value = basket_bid_value(legs, quotes) target = cost + TARGET_PROFIT * SHARES if value >= target: close_all()

这就是最小版本。它没有假装自己已经解决了足球预测,也没有宣称可以稳定套利。它只是尝试把“套利”和“预测”之间那条灰色地带拿出来研究一下。

套利的美好在于确定性,但确定性很稀缺。预测的诱惑在于空间大,但随机性也大。这个策略做的事情,是把市场给出的预测价格当作材料,用一个高概率主判断,加上两个低概率保护路径,再用泊松模型做一层粗过滤,尝试构造一个可以在比赛过程中提前收敛的窗口。

风险必须说清楚。这个组合不是完备事件,不是无风险套利。它适合强弱差距较大、主路径足够清晰、保护路径价格足够低的比赛。如果比赛本身五五开,或者保护腿已经很贵,硬做就没有意义。更麻烦的是,它没有覆盖所有不利路径,比如 1:1、0:2、2:2,都可能让组合出现明显亏损。所以必须设置仓位上限、最大亏损、止损规则,不能因为“看起来像套利”就把它当成套利。

泊松模型也不是魔法。它只是一个非常朴素的数学底座,帮我们从“我感觉这个价格不错”,往前走一步,变成“至少用一个透明模型检查过这个价格”。它会错,市场也会错,我们自己更会错。交易里最诚实的部分,大概就是承认这些都会错,然后尽量让错误不要一次把桌子掀了。

这次尝试很浅。它更像是在问一个问题:当纯套利机会越来越少,纯预测又太难时,我们能不能借助市场已经给出的概率,构造一些更有结构的交易?不是幻想消灭风险,而是把风险拆开,看清楚它从哪几条路过来。

也许这不是答案,但它是一个挺值得继续挖的小洞。预测负责告诉我们哪条主路可能更宽,套利思想提醒我们不要只看方向,还要看组合结构。两者之间,或许还有很多可以探索的空间。

策略源码: Polymarket 足球路径收敛策略

Comment
All comments (0)
No data
No data
  • 1
Forums
PINE Language
Get the app
iPhone Download
© 2015 - ∞ INVENTOR PTE LTD (SG)