본문으로 건너뛰기

7,426 forecasts against Polymarket: exactly where the market beat our models

NeuPortal Research최신4분 분량글 언어: 영어

핵심 요약

On the 7,048 calls that have resolved, the market scores 0.161 and our models score 0.194. The market wins. This post is about where it wins, because the answer is more specific, and more useful, than "markets are efficient".

Since September our statistical models have made 7,426 probability calls on Polymarket questions. Each call is stored next to the market price at the moment it was made, and the file holding it is timestamped into Bitcoin with OpenTimestamps. When the market resolves, both numbers are scored with the Brier score (lower is better).

On the 7,048 calls that have resolved, the market scores 0.161 and our models score 0.194. The market wins. This post is about where it wins, because the answer is more specific, and more useful, than "markets are efficient".

Everything below can be reproduced from the public dataset in a few lines of pandas (code at the end).

Headline numbers

trackresolved callsour Briermarket Brier
weather6,1280.2040.166
crypto6670.0990.094
sports1650.2130.208
markets520.1960.147
politics230.1400.201
tech130.3500.309
all7,0480.1940.161

Weather is 87% of the sample, so it dominates the total. Without weather the gap is small (0.130 vs 0.123 on 920 calls). The only track where we are ahead is politics, on 23 calls, which proves nothing.

One more number worth keeping in mind: our call scored better than the market price on 50.5% of individual questions. We win about half the coin flips, and lose the total anyway. The losses are bigger than the wins, and the rest of this post is about why.

Finding 1: we were far too sure that unlikely things would not happen

Take every call where our model said 10% or less. There are 2,176 of them.

  • our average probability: 3.8%
  • the market's average price on the same questions: 16.5%
  • how often the event actually happened: 17.0%

The market was almost exactly right; we were off by a factor of four. Most of these are weather "band" questions (will the high temperature land in a given 2-degree range). Our models put a narrow, confident distribution on the temperature and gave neighbouring bands almost nothing. Reality was wider than the model.

The market's own calibration, by contrast, is close to the diagonal everywhere it has data:

market price bucketcallsavg pricehappened
0-10%1,6165.4%5.3%
10-20%1,20115.1%13.7%
20-30%1,15825.3%26.3%
30-40%1,20635.3%35.2%
40-50%93645.0%45.7%

And ours:

our probability bucketcallsavg ourshappened
0-10%2,1763.8%17.0%
10-20%1,21614.9%23.8%
30-40%1,03435.2%31.4%
40-50%71045.2%35.4%
50-60%35655.0%41.3%
60-70%13365.0%43.6%

So the error runs both ways: too low at the bottom, too high in the middle. That is the signature of a model that is overconfident about which band wins, not one that is biased in a single direction.

Finding 2: the more we disagreed with the market, the worse we did

Group the calls by the gap between our probability and the market price:

gap between us and the marketcallsour Briermarket Brierwe beat the price
up to 5 points2,0850.1160.11657%
5-10 points1,2900.1530.14954%
10-20 points1,8570.2120.18648%
20-30 points1,0700.2490.19347%
over 30 points7460.3600.19537%

When we roughly agreed with the price, we tied it. Every step of disagreement cost us, and the large disagreements, the ones that would have looked like "edge", were the worst calls in the set.

Finding 3: mixing our model into the price only made it worse

A natural rescue is to blend: take some weighted average of our probability and the market's. If the model carried any information the price did not, some blend should beat the price alone.

weight on our modelBrier of the blend
0% (market only)0.161
25%0.163
50%0.169
75%0.180
100% (model only)0.194

Every amount of our model hurt. On this sample, the honest conclusion is that the models added nothing the price did not already know.

What we are not claiming

  • This is one set of models, mostly on one kind of question. It says little about forecasting in general and nothing about other teams.
  • Our first batch (calls from 14 to 26 September, 1,613 of them) was sealed late, on 26 September, after 1,394 had already resolved. Those rows are flagged sealed_before_resolution = false and are not evidence of forecasting in advance. They do not flatter us: on the 5,654 resolved calls sealed before their outcome, the result is the same (0.196 vs 0.160). Since 26 September every call is sealed minutes after it is made.
  • Nothing here is a trading signal or advice. It is a record of misses, kept so it cannot be edited later.

Reproduce it

import pandas as pd
from datasets import load_dataset

f = load_dataset("neuportal/forecasts-vs-market", split="train").to_pandas()
f = f[f.status == "final"]

# headline
print(f[["brier_model", "brier_market"]].mean())

# finding 1: our 0-10% calls
low = f[f.our_p <= 0.10]
print(len(low), low.our_p.mean(), low.market_p_at_call.mean(), low.outcome.mean())

# finding 2: by disagreement
gap = (f.our_p - f.market_p_at_call).abs()
bins = pd.cut(gap, [0, .05, .1, .2, .3, 1], include_lowest=True)
print(f.groupby(bins)[["brier_model", "brier_market"]].mean())

# finding 3: blends
for w in [0, .25, .5, .75, 1]:
    p = w * f.our_p + (1 - w) * f.market_p_at_call
    print(w, ((p - f.outcome) ** 2).mean())

To check that a call existed when we say it did, open its proof_file in the proofs repository and drop the .ots file, then the .json, on opentimestamps.org. The Bitcoin block it reports is the latest moment that call could have been written.

Educational transparency data, not financial advice.

다음 글

공개적으로 채점하는 예측

모든 예측은 이벤트 전에 기록되고 이벤트 후에 채점됩니다. 적중이든 실패든 모두.

AI 터미널과 결제 게이트웨이의 작동 및 인증 절차를 위해 쿠키를 사용합니다. 사이트를 계속 이용하시면 쿠키 정책과 개인정보 처리방침에 동의하는 것으로 간주됩니다.

‘동의하고 계속하기’를 선택하면 광고 성과 측정에 사용되는 Google Ads 광고 쿠키도 허용됩니다. 이 쿠키는 마이페이지에서는 사용되지 않습니다.