mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-08-18 16:37:24 +02:00
fix(infonet): harden market snapshot projection
This commit is contained in:
@@ -24,6 +24,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
from typing import Any, Iterable
|
from typing import Any, Iterable
|
||||||
|
|
||||||
|
|
||||||
@@ -32,6 +33,32 @@ def _payload(event: dict[str, Any]) -> dict[str, Any]:
|
|||||||
return p if isinstance(p, dict) else {}
|
return p if isinstance(p, dict) else {}
|
||||||
|
|
||||||
|
|
||||||
|
def _finite_float(value: Any) -> float | None:
|
||||||
|
try:
|
||||||
|
parsed = float(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None
|
||||||
|
return parsed if math.isfinite(parsed) else None
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_int(value: Any) -> int | None:
|
||||||
|
try:
|
||||||
|
return int(value)
|
||||||
|
except (TypeError, ValueError, OverflowError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _event_order_key(event: dict[str, Any]) -> tuple[int, float, int, int]:
|
||||||
|
timestamp = _finite_float(event.get("timestamp"))
|
||||||
|
sequence = _safe_int(event.get("sequence"))
|
||||||
|
return (
|
||||||
|
1 if timestamp is None else 0,
|
||||||
|
0.0 if timestamp is None else timestamp,
|
||||||
|
1 if sequence is None else 0,
|
||||||
|
0 if sequence is None else sequence,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _events_for_market(market_id: str, chain: Iterable[dict[str, Any]]) -> list[dict[str, Any]]:
|
def _events_for_market(market_id: str, chain: Iterable[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||||
out: list[dict[str, Any]] = []
|
out: list[dict[str, Any]] = []
|
||||||
for ev in chain:
|
for ev in chain:
|
||||||
@@ -39,7 +66,7 @@ def _events_for_market(market_id: str, chain: Iterable[dict[str, Any]]) -> list[
|
|||||||
continue
|
continue
|
||||||
if _payload(ev).get("market_id") == market_id:
|
if _payload(ev).get("market_id") == market_id:
|
||||||
out.append(ev)
|
out.append(ev)
|
||||||
out.sort(key=lambda e: (float(e.get("timestamp") or 0.0), int(e.get("sequence") or 0)))
|
out.sort(key=_event_order_key)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
@@ -61,6 +88,10 @@ def build_snapshot(
|
|||||||
to advance to EVIDENCE. Pass it explicitly so the function stays
|
to advance to EVIDENCE. Pass it explicitly so the function stays
|
||||||
pure and deterministic.
|
pure and deterministic.
|
||||||
"""
|
"""
|
||||||
|
frozen_at_value = _finite_float(frozen_at)
|
||||||
|
if frozen_at_value is None:
|
||||||
|
raise ValueError("frozen_at must be finite")
|
||||||
|
|
||||||
events = _events_for_market(market_id, chain)
|
events = _events_for_market(market_id, chain)
|
||||||
|
|
||||||
predictor_ids: list[str] = []
|
predictor_ids: list[str] = []
|
||||||
@@ -79,27 +110,29 @@ def build_snapshot(
|
|||||||
side = p.get("side")
|
side = p.get("side")
|
||||||
if side not in ("yes", "no"):
|
if side not in ("yes", "no"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
stake = p.get("stake_amount")
|
||||||
|
if stake is None:
|
||||||
|
weight = 1.0 # Free pick = 1.0 virtual stake (RULES §5.2).
|
||||||
|
staked_amount = 0.0
|
||||||
|
else:
|
||||||
|
parsed_stake = _finite_float(stake)
|
||||||
|
if parsed_stake is None or parsed_stake <= 0:
|
||||||
|
# Invalid paid predictions must not inflate participant
|
||||||
|
# counts or poison the frozen probability state.
|
||||||
|
continue
|
||||||
|
weight = parsed_stake
|
||||||
|
staked_amount = parsed_stake
|
||||||
|
|
||||||
if node not in seen_predictors:
|
if node not in seen_predictors:
|
||||||
seen_predictors.add(node)
|
seen_predictors.add(node)
|
||||||
predictor_ids.append(node)
|
predictor_ids.append(node)
|
||||||
stake = p.get("stake_amount")
|
|
||||||
if stake is not None:
|
total_stake += staked_amount
|
||||||
try:
|
if side == "yes":
|
||||||
a = float(stake)
|
yes_weight += weight
|
||||||
except (TypeError, ValueError):
|
|
||||||
a = 0.0
|
|
||||||
if a > 0:
|
|
||||||
total_stake += a
|
|
||||||
if side == "yes":
|
|
||||||
yes_weight += a
|
|
||||||
else:
|
|
||||||
no_weight += a
|
|
||||||
else:
|
else:
|
||||||
# Free pick = 1.0 virtual stake (RULES §5.2).
|
no_weight += weight
|
||||||
if side == "yes":
|
|
||||||
yes_weight += 1.0
|
|
||||||
else:
|
|
||||||
no_weight += 1.0
|
|
||||||
|
|
||||||
pool = yes_weight + no_weight
|
pool = yes_weight + no_weight
|
||||||
if pool > 0:
|
if pool > 0:
|
||||||
@@ -114,7 +147,7 @@ def build_snapshot(
|
|||||||
"frozen_total_stake": total_stake,
|
"frozen_total_stake": total_stake,
|
||||||
"frozen_predictor_ids": predictor_ids,
|
"frozen_predictor_ids": predictor_ids,
|
||||||
"frozen_probability_state": {"yes": yes_p, "no": no_p},
|
"frozen_probability_state": {"yes": yes_p, "no": no_p},
|
||||||
"frozen_at": float(frozen_at),
|
"frozen_at": frozen_at_value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user