Skip to content

Commit c564b86

Browse files
refactor(winrate): structured conf_key, improve ConfirmationStore.clear logging, dedupe log fixes, time_to_end instrumentation
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7b3752b commit c564b86

2 files changed

Lines changed: 48 additions & 17 deletions

File tree

src/utils/winrate_upgrade.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import os
33
import time
4+
import logging
45
from threading import Lock
56
from typing import Any, Dict, Optional, Tuple
67

@@ -128,17 +129,23 @@ def expire_all_older_than(self, ttl: int) -> int:
128129

129130
def clear(self, key: str) -> bool:
130131
"""Remove a pending confirmation key if present. Returns True if removed."""
132+
logger = logging.getLogger("webhook_server_fastapi")
131133
with self.lock:
134+
exists_before = key in self._data
135+
logger.info(f"confirmation_clear_attempt: key={key} exists_before={exists_before}")
132136
if key in self._data:
133137
try:
134138
del self._data[key]
135139
try:
136140
self._save()
137141
except Exception:
138142
pass
143+
logger.info(f"confirmation_cleared: key={key} removed=True")
139144
return True
140145
except Exception:
146+
logger.exception(f"confirmation_cleared: key={key} removed=False (exception)")
141147
return False
148+
logger.info(f"confirmation_cleared: key={key} removed=False")
142149
return False
143150

144151

@@ -191,5 +198,22 @@ def compute_time_to_market_end(market: Optional[Dict[str, Any]]) -> Tuple[Option
191198
seconds = int((end - now).total_seconds())
192199
return seconds, "ok"
193200
except Exception:
201+
# Log end_time_unavailable once per market to avoid spam
202+
logger = logging.getLogger("webhook_server_fastapi")
203+
try:
204+
market_id = None
205+
attempted_fields = []
206+
if isinstance(market, dict):
207+
market_id = market.get("id") or market.get("market_id") or market.get("slug")
208+
attempted_fields = [k for k in ("end_time", "close_time", "end") if k in market]
209+
key = f"end_unavailable:{market_id or 'unknown'}"
210+
# use module-level cache
211+
if not hasattr(compute_time_to_market_end, "_logged_keys"):
212+
compute_time_to_market_end._logged_keys = set()
213+
if key not in compute_time_to_market_end._logged_keys:
214+
logger.info(f"end_time_unavailable: market_id/token={market_id} attempted_fields={attempted_fields}")
215+
compute_time_to_market_end._logged_keys.add(key)
216+
except Exception:
217+
logger.exception("error logging end_time_unavailable")
194218
return None, "end_time_unavailable"
195219

webhook_server_fastapi.py

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3469,24 +3469,20 @@ def _check_signal_id_duplicate(signal_id: str, signal: str, request_id: str) ->
34693469
if cache_age < _signal_id_cache_ttl_seconds:
34703470
# Duplicate found within TTL
34713471
_duplicate_signals_count += 1
3472+
# If confirmation flow is enabled, allow duplicate to proceed so confirmation logic can run
3473+
if getattr(settings, "WINRATE_UPGRADE_ENABLED", False) and getattr(settings, "REQUIRE_CONFIRMATION", False):
3474+
# Refresh cache timestamp and allow processing so confirmation handling can run
3475+
_signal_id_cache[cache_key] = current_time
3476+
logger.info(
3477+
f"[{request_id}] DUPLICATE_BUT_ALLOWED_FOR_CONFIRMATION: signal_id={signal_id}, signal={signal_upper}, cache_key={cache_key} "
3478+
f"(age={cache_age:.1f}s, TTL={_signal_id_cache_ttl_seconds}s)"
3479+
)
3480+
return False, "duplicate_allowed_for_confirmation"
3481+
# Default duplicate behavior: skip processing
34723482
logger.info(
3473-
f"[{request_id}] DUPLICATE SIGNAL: signal_id={signal_id}, signal={signal_upper}, cache_key={cache_key} "
3483+
f"[{request_id}] DUPLICATE_SIGNAL_SKIPPED: signal_id={signal_id}, signal={signal_upper}, cache_key={cache_key} "
34743484
f"(age={cache_age:.1f}s, TTL={_signal_id_cache_ttl_seconds}s) - SKIPPED"
34753485
)
3476-
# If confirmation flow is enabled, allow duplicate to proceed if it's awaiting confirmation
3477-
try:
3478-
if getattr(settings, "WINRATE_UPGRADE_ENABLED", False) and getattr(settings, "REQUIRE_CONFIRMATION", False):
3479-
conf_key = f"{signal_id}|{signal_upper}"
3480-
if hasattr(_confirmation_store, "_data"):
3481-
keys_preview = list(_confirmation_store._data.keys())[:20]
3482-
logger.debug(f"[{request_id}] confirmation_store keys_preview={keys_preview}")
3483-
if conf_key in _confirmation_store._data:
3484-
logger.info(f"[{request_id}] DUPLICATE SIGNAL but confirmation pending: allowing for confirmation (key={conf_key})")
3485-
# refresh cache timestamp and allow processing so confirmation handling can run
3486-
_signal_id_cache[cache_key] = current_time
3487-
return False, "duplicate_allowed_for_confirmation"
3488-
except Exception:
3489-
pass
34903486
return True, "duplicate"
34913487
else:
34923488
# Expired entry, remove it
@@ -3554,9 +3550,20 @@ def webhook(payload: WebhookPayload):
35543550
except Exception:
35553551
market = None
35563552
up_token, down_token = resolve_up_down_tokens(market) if market else (None, None)
3557-
token_or_market = up_token or slug or "unknown_market"
3553+
# Build structured confirmation key:
3554+
# Prefer market id, else token id, include direction and signal_id to avoid collisions.
35583555
sig_id = payload.signal_id or signal_id_for_logging or "no-signal-id"
3559-
conf_key = f"{token_or_market}:{sig_for_dedupe}:{sig_id}"
3556+
market_id = None
3557+
if market and isinstance(market, dict):
3558+
market_id = market.get("id")
3559+
token_id = up_token or down_token or None
3560+
if market_id:
3561+
conf_key = f\"pm:market:{market_id}:{sig_for_dedupe}:{sig_id}\"
3562+
elif token_id:
3563+
conf_key = f\"pm:token:{token_id}:{sig_for_dedupe}:{sig_id}\"
3564+
else:
3565+
# Fallback to slug-based key (least preferred)
3566+
conf_key = f\"pm:slug:{slug}:{sig_for_dedupe}:{sig_id}\"
35603567

35613568
# Use new high-level handle API (returns pending/expired/confirmed)
35623569
result = _confirmation_store.handle(

0 commit comments

Comments
 (0)