市场怎么判定输赢
加密涨跌市场按 Chainlink 的流判定,不按盘口。用哪条流是市场自己的属性,而归档里已经带齐了你自己重算一遍所需要的全部东西。
两个必须知道的口径
结算规则变过,别按日期推
2026-08-07(UTC) 之前用瞬时 Chainlink 流判定,之后改用 TWAP 流:当时 5 分钟市场用 30 秒回看、15 分钟用 60 秒;2026-08-14(UTC) 之后 5 分钟市场也改成了 60 秒,现在两种周期都判定在 twap60s 上。所以该用哪条流,要看这个市场自己的 raw.cryptoMarketConfig(twapEnabled 与 twapLookbackSeconds=30 或 60),不要按日期推——同一天里不同配置的市场是并存的。twap30s 仍在逐日归档,只是不再是任何市场的判定线。
帧是按收到的样子存的
乱序也照存,我们不在归档期做重排。要按时间顺序处理,请在查询时自己排序
自己重算一遍结果
这一步不需要你信我们的话——strike、窗口、判定用的那条流,全都在你已经下载的文件里。
| 1 | 读市场那一行:start_sec、end_sec、strike_value,以及 raw.cryptoMarketConfig.twapLookbackSeconds。 |
| 2 | 按那个回看秒数挑流——60 对应 twap60s,30 对应 twap30s——取它在窗口收尾时刻的值。 |
| 3 | 与 strike_value 比较。Polymarket 侧两者都是 1e18 定点,所以两边都转成整数再比,不要比浮点、也不要按字符串比:value 是为了查询方便,只要 relay 发布过,该用来判定的是 full_accuracy_value。 |
| 4 | 拿同一行的 outcome_prices 核对自己算的结果。["1","0"] 是 Up,["0","1"] 是 Down。 |
上面四步写成代码
读你已经下载的那两个文件,逐个市场打印结论。它按时间戳取收尾那一条,而不是按它在文件里的位置——帧是按到达顺序归档的。
import gzip, json, csv
MARKETS = "BTC-5m-markets.jsonl.gz" # one line per market, as downloaded
STREAMS = { # each market names the one it settles on
60: "BTCUSD-twap60s-prices.csv.gz",
30: "BTCUSD-twap30s-prices.csv.gz",
0: "BTCUSD-prices.csv.gz", # before TWAP, the instantaneous feed
}
_loaded = {}
def stream(lookback):
if lookback not in _loaded:
with gzip.open(STREAMS[lookback], "rt") as f:
_loaded[lookback] = list(csv.DictReader(f))
return _loaded[lookback]
for line in gzip.open(MARKETS, "rt"):
m = json.loads(line)
# strike_value is null when it could not be established honestly
if not m.get("resolved") or m.get("strike_value") is None:
continue
# Read the stream off the MARKET, never off the date: configs coexist on
# one day. A MISSING config is a record we could not read — not evidence
# of the pre-TWAP era — so it is skipped. Only an explicit null or 0 says
# "this one settled on the instantaneous stream".
raw = m.get("raw")
cfg = raw.get("cryptoMarketConfig") if isinstance(raw, dict) else None
if not isinstance(cfg, dict) or "twapLookbackSeconds" not in cfg:
continue
lookback = cfg["twapLookbackSeconds"]
if lookback is None:
lookback = 0
if lookback not in STREAMS:
continue # unknown config: refuse rather than guess
open_ms, close_ms = m["start_sec"] * 1000, m["end_sec"] * 1000
window = [t for t in stream(lookback)
if open_ms <= int(t["feed_ts_ms"]) <= close_ms]
if not window:
continue
# by timestamp, not by file order: frames are archived as received
last = max(window, key=lambda t: int(t["feed_ts_ms"]))
# full_accuracy_value is 1e18 fixed point ONLY when the relay published it;
# otherwise the collector wrote str(value) and the archive cannot tell you
fixed = last["full_accuracy_value"]
if not fixed.isdigit():
continue # a decimal fallback, not fixed point
settled = int(fixed)
if abs(settled / 1e18 - float(last["value"])) > 1e-6:
continue # an integer, but not at the 1e18 scale
# only a COMPLETE binary pair is an outcome; anything else is bad metadata
prices = m.get("outcome_prices")
if prices not in (["1", "0"], ["0", "1"]):
continue
# the market resolves Up when the close is >= the strike, not > it
mine = "UP" if settled >= int(m["strike_value"]) else "DOWN"
theirs = "UP" if prices[0] == "1" else "DOWN"
print(m["slug"], mine, "matches" if mine == theirs else "DIFFERS")Predict.fun 的判定方式不一样
Predict.fun 的市场行带 start_price 与 end_price 两个普通十进制数,结果由两者比较得出。判断有没有结算要看 end_price,不要看 status:status 是上游最后一次报的状态,而市场一旦拿到 end_price 就退出重读队列,所以一个已经结算的市场,status 可能还停在 OPEN。end_price 与 start_price 相等是平局,那是第三种结果,不是任何一侧获胜。
相关: Polymarket 盘口数据 · Chainlink 结算价数据 · Predict.fun 历史数据 · 与其他服务商的对比