Use Shenzhen market for Lau Fau Shan
This commit is contained in:
@@ -87,6 +87,17 @@ def _normalize_city_key(city: Any) -> str:
|
|||||||
return ALIASES.get(raw, raw)
|
return ALIASES.get(raw, raw)
|
||||||
|
|
||||||
|
|
||||||
|
MARKET_CITY_ALIASES: Dict[str, str] = {
|
||||||
|
# Lau Fau Shan has its own HKO observation / settlement layer, but
|
||||||
|
# Polymarket lists this temperature market under nearby Shenzhen.
|
||||||
|
"lau fau shan": "shenzhen",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_market_city_key(city_key: str) -> str:
|
||||||
|
return MARKET_CITY_ALIASES.get(city_key, city_key)
|
||||||
|
|
||||||
|
|
||||||
def _contains_token(haystack: str, token: str) -> bool:
|
def _contains_token(haystack: str, token: str) -> bool:
|
||||||
token = _normalize_text(token)
|
token = _normalize_text(token)
|
||||||
if not token:
|
if not token:
|
||||||
@@ -433,11 +444,14 @@ class PolymarketReadOnlyLayer:
|
|||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
date_str = _extract_iso_date(target_date) or str(target_date or "")
|
date_str = _extract_iso_date(target_date) or str(target_date or "")
|
||||||
city_key = _normalize_city_key(city)
|
city_key = _normalize_city_key(city)
|
||||||
|
market_city_key = _resolve_market_city_key(city_key)
|
||||||
requested_slug = str(forced_market_slug or "").strip().lower() or None
|
requested_slug = str(forced_market_slug or "").strip().lower() or None
|
||||||
|
|
||||||
scan: Dict[str, Any] = {
|
scan: Dict[str, Any] = {
|
||||||
"available": False,
|
"available": False,
|
||||||
"reason": None,
|
"reason": None,
|
||||||
|
"city_key": city_key or None,
|
||||||
|
"market_city_key": market_city_key or None,
|
||||||
"primary_market": None,
|
"primary_market": None,
|
||||||
"selected_date": date_str or None,
|
"selected_date": date_str or None,
|
||||||
"selected_condition_id": None,
|
"selected_condition_id": None,
|
||||||
@@ -474,9 +488,18 @@ class PolymarketReadOnlyLayer:
|
|||||||
self._debug_market_scan("unsupported_city", city=city, normalized=city_key)
|
self._debug_market_scan("unsupported_city", city=city, normalized=city_key)
|
||||||
return scan
|
return scan
|
||||||
|
|
||||||
|
if not market_city_key or market_city_key not in CITY_REGISTRY:
|
||||||
|
scan["reason"] = "Mapped market city is not supported by the Polymarket market layer."
|
||||||
|
self._debug_market_scan(
|
||||||
|
"unsupported_market_city",
|
||||||
|
city=city_key,
|
||||||
|
market_city=market_city_key,
|
||||||
|
)
|
||||||
|
return scan
|
||||||
|
|
||||||
if not date_str:
|
if not date_str:
|
||||||
scan["reason"] = "Missing target date for market discovery."
|
scan["reason"] = "Missing target date for market discovery."
|
||||||
self._debug_market_scan("missing_date", city=city_key)
|
self._debug_market_scan("missing_date", city=city_key, market_city=market_city_key)
|
||||||
return scan
|
return scan
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -484,15 +507,22 @@ class PolymarketReadOnlyLayer:
|
|||||||
if isinstance(temperature_bucket, dict):
|
if isinstance(temperature_bucket, dict):
|
||||||
preferred_temp = _safe_float(temperature_bucket.get("temp"))
|
preferred_temp = _safe_float(temperature_bucket.get("temp"))
|
||||||
market, reason = self._find_primary_market(
|
market, reason = self._find_primary_market(
|
||||||
city_key,
|
market_city_key,
|
||||||
date_str,
|
date_str,
|
||||||
forced_market_slug=requested_slug,
|
forced_market_slug=requested_slug,
|
||||||
preferred_temp=preferred_temp,
|
preferred_temp=preferred_temp,
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(f"Polymarket market discovery failed ({city_key}): {exc}")
|
logger.warning(
|
||||||
|
f"Polymarket market discovery failed ({city_key}->{market_city_key}): {exc}"
|
||||||
|
)
|
||||||
scan["reason"] = "Market discovery failed."
|
scan["reason"] = "Market discovery failed."
|
||||||
self._debug_market_scan("discovery_exception", city=city_key, error=str(exc))
|
self._debug_market_scan(
|
||||||
|
"discovery_exception",
|
||||||
|
city=city_key,
|
||||||
|
market_city=market_city_key,
|
||||||
|
error=str(exc),
|
||||||
|
)
|
||||||
return scan
|
return scan
|
||||||
|
|
||||||
if not market:
|
if not market:
|
||||||
@@ -500,6 +530,7 @@ class PolymarketReadOnlyLayer:
|
|||||||
self._debug_market_scan(
|
self._debug_market_scan(
|
||||||
"no_market",
|
"no_market",
|
||||||
city=city_key,
|
city=city_key,
|
||||||
|
market_city=market_city_key,
|
||||||
date=date_str,
|
date=date_str,
|
||||||
forced_slug=requested_slug,
|
forced_slug=requested_slug,
|
||||||
reason=scan["reason"],
|
reason=scan["reason"],
|
||||||
@@ -557,6 +588,7 @@ class PolymarketReadOnlyLayer:
|
|||||||
self._debug_market_scan(
|
self._debug_market_scan(
|
||||||
"not_tradable",
|
"not_tradable",
|
||||||
city=city_key,
|
city=city_key,
|
||||||
|
market_city=market_city_key,
|
||||||
date=date_str,
|
date=date_str,
|
||||||
slug=market_slug,
|
slug=market_slug,
|
||||||
trade_state=trade_state,
|
trade_state=trade_state,
|
||||||
@@ -575,6 +607,7 @@ class PolymarketReadOnlyLayer:
|
|||||||
self._debug_market_scan(
|
self._debug_market_scan(
|
||||||
"missing_tokens",
|
"missing_tokens",
|
||||||
city=city_key,
|
city=city_key,
|
||||||
|
market_city=market_city_key,
|
||||||
date=date_str,
|
date=date_str,
|
||||||
slug=market_slug,
|
slug=market_slug,
|
||||||
token_count=len(tokens),
|
token_count=len(tokens),
|
||||||
@@ -608,7 +641,7 @@ class PolymarketReadOnlyLayer:
|
|||||||
_safe_int(os.getenv("POLYMARKET_ALL_BUCKET_LIMIT", "8"), 8),
|
_safe_int(os.getenv("POLYMARKET_ALL_BUCKET_LIMIT", "8"), 8),
|
||||||
)
|
)
|
||||||
all_buckets = self._build_top_temperature_buckets(
|
all_buckets = self._build_top_temperature_buckets(
|
||||||
city_key=city_key,
|
city_key=market_city_key,
|
||||||
target_date=date_str,
|
target_date=date_str,
|
||||||
primary_market=market,
|
primary_market=market,
|
||||||
limit=all_bucket_limit,
|
limit=all_bucket_limit,
|
||||||
@@ -704,6 +737,7 @@ class PolymarketReadOnlyLayer:
|
|||||||
self._debug_market_scan(
|
self._debug_market_scan(
|
||||||
"scan_ready",
|
"scan_ready",
|
||||||
city=city_key,
|
city=city_key,
|
||||||
|
market_city=market_city_key,
|
||||||
date=date_str,
|
date=date_str,
|
||||||
selected_slug=market_slug,
|
selected_slug=market_slug,
|
||||||
market_price=scan.get("market_price"),
|
market_price=scan.get("market_price"),
|
||||||
|
|||||||
@@ -147,6 +147,59 @@ def test_trade_state_keeps_open_markets_tradable_after_gamma_end_date():
|
|||||||
assert state["ended_at_utc"] == "2020-01-01T00:00:00+00:00"
|
assert state["ended_at_utc"] == "2020-01-01T00:00:00+00:00"
|
||||||
|
|
||||||
|
|
||||||
|
def test_lau_fau_shan_uses_shenzhen_market_city():
|
||||||
|
layer = PolymarketReadOnlyLayer()
|
||||||
|
captured = {}
|
||||||
|
|
||||||
|
def _fake_find_primary_market(city_key, target_date, **_kwargs):
|
||||||
|
captured["primary_city_key"] = city_key
|
||||||
|
captured["target_date"] = target_date
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
"id": "market-1",
|
||||||
|
"question": "Will the highest temperature in Shenzhen be 30C or higher on April 23?",
|
||||||
|
"slug": "highest-temperature-in-shenzhen-on-april-23-2026-30c-or-higher",
|
||||||
|
"conditionId": "condition-1",
|
||||||
|
"active": True,
|
||||||
|
"closed": False,
|
||||||
|
"acceptingOrders": True,
|
||||||
|
"volumeNum": 1000,
|
||||||
|
"liquidityNum": 500,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
layer._find_primary_market = _fake_find_primary_market
|
||||||
|
layer._extract_market_tokens = lambda _market: [
|
||||||
|
{"outcome": "Yes", "token_id": "yes-token"},
|
||||||
|
{"outcome": "No", "token_id": "no-token"},
|
||||||
|
]
|
||||||
|
layer._get_token_market_data = lambda token_id: (
|
||||||
|
{"buy": 0.42, "sell": 0.40, "midpoint": 0.41}
|
||||||
|
if token_id == "yes-token"
|
||||||
|
else {"buy": 0.61, "sell": 0.59, "midpoint": 0.60}
|
||||||
|
)
|
||||||
|
|
||||||
|
def _fake_build_top_temperature_buckets(city_key, **_kwargs):
|
||||||
|
captured["bucket_city_key"] = city_key
|
||||||
|
return []
|
||||||
|
|
||||||
|
layer._build_top_temperature_buckets = _fake_build_top_temperature_buckets
|
||||||
|
|
||||||
|
scan = layer.build_market_scan(
|
||||||
|
city="Lau Fau Shan",
|
||||||
|
target_date="2026-04-23",
|
||||||
|
temperature_bucket={"temp": 30, "probability": 0.58},
|
||||||
|
model_probability=0.58,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert captured["primary_city_key"] == "shenzhen"
|
||||||
|
assert captured["bucket_city_key"] == "shenzhen"
|
||||||
|
assert scan["city_key"] == "lau fau shan"
|
||||||
|
assert scan["market_city_key"] == "shenzhen"
|
||||||
|
assert scan["selected_slug"] == "highest-temperature-in-shenzhen-on-april-23-2026-30c-or-higher"
|
||||||
|
|
||||||
|
|
||||||
def test_hydrate_bucket_prices_uses_executable_quotes_without_midpoint():
|
def test_hydrate_bucket_prices_uses_executable_quotes_without_midpoint():
|
||||||
layer = PolymarketReadOnlyLayer()
|
layer = PolymarketReadOnlyLayer()
|
||||||
buckets = [
|
buckets = [
|
||||||
|
|||||||
Reference in New Issue
Block a user