Avoid ops market quote timeout
This commit is contained in:
@@ -205,3 +205,50 @@ def test_ops_market_opportunities_reuses_prewarmed_terminal_snapshot(monkeypatch
|
|||||||
assert captured["filters"]["limit"] == 180
|
assert captured["filters"]["limit"] == 180
|
||||||
assert captured["filters"]["min_edge_pct"] == 2
|
assert captured["filters"]["min_edge_pct"] == 2
|
||||||
assert captured["filters"]["min_liquidity"] == 500
|
assert captured["filters"]["min_liquidity"] == 500
|
||||||
|
|
||||||
|
|
||||||
|
def test_collect_events_and_prices_uses_gamma_hint_prices_without_clob_fanout():
|
||||||
|
class FakeScanner:
|
||||||
|
def fetch_event(self, _slug):
|
||||||
|
return {
|
||||||
|
"slug": "highest-temperature-in-shanghai-on-july-3-2026",
|
||||||
|
"markets": [
|
||||||
|
{
|
||||||
|
"question": "Will the highest temperature in Shanghai be 33°C on July 3?",
|
||||||
|
"outcomes": '["Yes", "No"]',
|
||||||
|
"clobTokenIds": '["yes-33", "no-33"]',
|
||||||
|
"outcomePrices": '["0.12", "0.88"]',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
def fetch_ask_price(self, _token_id):
|
||||||
|
raise AssertionError("CLOB should not be called when Gamma outcomePrices are present")
|
||||||
|
|
||||||
|
events, prices, status = market_opportunities._collect_events_and_prices(
|
||||||
|
[_row()],
|
||||||
|
FakeScanner(),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert status == "ready"
|
||||||
|
assert "shanghai" in events
|
||||||
|
assert prices == {"yes-33": 0.12, "no-33": 0.88}
|
||||||
|
|
||||||
|
|
||||||
|
def test_collect_events_and_prices_stops_on_time_budget():
|
||||||
|
class SlowScanner:
|
||||||
|
def fetch_event(self, _slug):
|
||||||
|
raise AssertionError("time budget should stop external event fetching")
|
||||||
|
|
||||||
|
def fetch_ask_price(self, _token_id):
|
||||||
|
raise AssertionError("time budget should stop external price fetching")
|
||||||
|
|
||||||
|
events, prices, status = market_opportunities._collect_events_and_prices(
|
||||||
|
[_row()],
|
||||||
|
SlowScanner(),
|
||||||
|
time_budget_sec=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert status == "partial"
|
||||||
|
assert events == {}
|
||||||
|
assert prices == {}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ GAMMA_API_BASE = "https://gamma-api.polymarket.com"
|
|||||||
CLOB_API_BASE = "https://clob.polymarket.com"
|
CLOB_API_BASE = "https://clob.polymarket.com"
|
||||||
_QUOTE_CACHE_TTL_SEC = 60
|
_QUOTE_CACHE_TTL_SEC = 60
|
||||||
_EVENT_CACHE_TTL_SEC = 180
|
_EVENT_CACHE_TTL_SEC = 180
|
||||||
|
_QUOTE_COLLECTION_TIME_BUDGET_SEC = 18.0
|
||||||
|
|
||||||
_CACHE_LOCK = threading.Lock()
|
_CACHE_LOCK = threading.Lock()
|
||||||
_EVENT_CACHE: Dict[str, Tuple[float, Optional[Dict[str, Any]]]] = {}
|
_EVENT_CACHE: Dict[str, Tuple[float, Optional[Dict[str, Any]]]] = {}
|
||||||
@@ -405,11 +406,21 @@ class PolymarketQuoteScanner:
|
|||||||
def _collect_events_and_prices(
|
def _collect_events_and_prices(
|
||||||
rows: List[Dict[str, Any]],
|
rows: List[Dict[str, Any]],
|
||||||
scanner: PolymarketQuoteScanner,
|
scanner: PolymarketQuoteScanner,
|
||||||
|
*,
|
||||||
|
time_budget_sec: float = _QUOTE_COLLECTION_TIME_BUDGET_SEC,
|
||||||
) -> Tuple[Dict[str, Optional[Dict[str, Any]]], Dict[str, Optional[float]], str]:
|
) -> Tuple[Dict[str, Optional[Dict[str, Any]]], Dict[str, Optional[float]], str]:
|
||||||
events_by_city: Dict[str, Optional[Dict[str, Any]]] = {}
|
events_by_city: Dict[str, Optional[Dict[str, Any]]] = {}
|
||||||
prices: Dict[str, Optional[float]] = {}
|
prices: Dict[str, Optional[float]] = {}
|
||||||
status = "ready"
|
status = "ready"
|
||||||
|
started_at = time.monotonic()
|
||||||
|
|
||||||
|
def budget_exhausted() -> bool:
|
||||||
|
return time.monotonic() - started_at >= max(0.0, float(time_budget_sec))
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
|
if budget_exhausted():
|
||||||
|
status = "partial"
|
||||||
|
break
|
||||||
city_key = _normalize_key(row.get("city"))
|
city_key = _normalize_key(row.get("city"))
|
||||||
if not city_key or city_key in events_by_city:
|
if not city_key or city_key in events_by_city:
|
||||||
continue
|
continue
|
||||||
@@ -433,9 +444,12 @@ def _collect_events_and_prices(
|
|||||||
if not token_id or token_id in prices:
|
if not token_id or token_id in prices:
|
||||||
continue
|
continue
|
||||||
hint_price = hint_prices.get(market_side)
|
hint_price = hint_prices.get(market_side)
|
||||||
if hint_price is not None and hint_price > 0.30:
|
if hint_price is not None:
|
||||||
prices[token_id] = hint_price
|
prices[token_id] = hint_price
|
||||||
continue
|
continue
|
||||||
|
if budget_exhausted():
|
||||||
|
status = "partial"
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
prices[token_id] = scanner.fetch_ask_price(token_id)
|
prices[token_id] = scanner.fetch_ask_price(token_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
Reference in New Issue
Block a user