feat(ingestor): enrich market metadata with gamma-api volume/liquidity (#107)
The CLOB API does not expose 24h volume or order-book liquidity, so the size_anomaly detector currently has no real ratio to compare a trade against and falls back to the niche-base 0.2 confidence floor. That makes the volume_impact / book_impact thresholds essentially dead code. This change adds a small client for the public gamma-api markets endpoint and merges its volume24hr / liquidityNum snapshot into MarketMetadata during the existing periodic sync. The detector can now compute real volume and book impact ratios. Notes on the gamma client: - gamma-api enforces a server-side max of 100 markets per page and caps `offset` around 10000. The client paginates with bounded concurrency, sorted by `volume24hr desc`, so the most-traded markets (which is where size anomalies actually matter) are always covered. Markets beyond that window have negligible recent volume and the niche path handles them fine without a ratio. - Failures are swallowed: a degraded gamma endpoint must not stop CLOB metadata from being cached, since size_anomaly + the niche path remain functional with `daily_volume=None`. MarketMetadata gains three optional Decimal fields (daily_volume, weekly_volume, liquidity); to_dict / from_dict round-trip is preserved and older cache entries without these keys deserialize cleanly. Tests: 12 new tests for GammaClient (parsing, single-page, short-page stop, offset-cap clean stop, retry, malformed responses); existing metadata_sync tests updated to inject a mocked GammaClient so they don't hit the real network. Co-authored-by: schrodinger01 <schrodinger01@users.noreply.github.com>
This commit is contained in:
co-authored by
schrodinger01
parent
5afbb35ee9
commit
e003e4ba47
@@ -399,6 +399,12 @@ class MarketMetadata:
|
||||
# Derived metadata
|
||||
category: str = "other"
|
||||
|
||||
# Liquidity/volume snapshot (from gamma-api). All optional — older
|
||||
# cache entries and CLOB-only sync results may not have these.
|
||||
daily_volume: Decimal | None = None
|
||||
weekly_volume: Decimal | None = None
|
||||
liquidity: Decimal | None = None
|
||||
|
||||
# Cache metadata
|
||||
last_updated: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||
|
||||
@@ -446,6 +452,9 @@ class MarketMetadata:
|
||||
"active": self.active,
|
||||
"closed": self.closed,
|
||||
"category": self.category,
|
||||
"daily_volume": str(self.daily_volume) if self.daily_volume is not None else None,
|
||||
"weekly_volume": str(self.weekly_volume) if self.weekly_volume is not None else None,
|
||||
"liquidity": str(self.liquidity) if self.liquidity is not None else None,
|
||||
"last_updated": self.last_updated.isoformat(),
|
||||
}
|
||||
|
||||
@@ -477,6 +486,15 @@ class MarketMetadata:
|
||||
else:
|
||||
last_updated = datetime.now(UTC)
|
||||
|
||||
def _opt_dec(key: str) -> Decimal | None:
|
||||
raw = data.get(key)
|
||||
if raw is None or raw == "":
|
||||
return None
|
||||
try:
|
||||
return Decimal(str(raw))
|
||||
except (ValueError, ArithmeticError):
|
||||
return None
|
||||
|
||||
return cls(
|
||||
condition_id=str(data["condition_id"]),
|
||||
question=str(data.get("question", "")),
|
||||
@@ -486,5 +504,8 @@ class MarketMetadata:
|
||||
active=bool(data.get("active", True)),
|
||||
closed=bool(data.get("closed", False)),
|
||||
category=str(data.get("category", "other")),
|
||||
daily_volume=_opt_dec("daily_volume"),
|
||||
weekly_volume=_opt_dec("weekly_volume"),
|
||||
liquidity=_opt_dec("liquidity"),
|
||||
last_updated=last_updated,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user