Update: sync local state to remote
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"""Resolution tracker - checks if markets with signals have resolved and updates correctness."""
|
||||
import asyncio
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from src.db.database import SignalDatabase
|
||||
from src.services.market_fetcher import MarketFetcher
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResolutionTracker:
|
||||
"""Tracks market resolutions and computes signal correctness."""
|
||||
|
||||
def __init__(self, db: SignalDatabase):
|
||||
self.db = db
|
||||
self.market_fetcher = MarketFetcher()
|
||||
|
||||
def _determine_resolved_outcome(self, market) -> Optional[str]:
|
||||
"""
|
||||
Determine the resolved outcome from a market.
|
||||
|
||||
A market is considered resolved if closed==True and one outcome price >= 0.99.
|
||||
|
||||
Returns:
|
||||
The winning outcome string (e.g. "Yes" or "No"), or None if not resolved.
|
||||
"""
|
||||
if not market.closed:
|
||||
return None
|
||||
|
||||
if not market.outcomes or not market.outcome_prices:
|
||||
return None
|
||||
|
||||
for outcome, price in zip(market.outcomes, market.outcome_prices):
|
||||
if price >= 0.99:
|
||||
return outcome
|
||||
|
||||
return None
|
||||
|
||||
def _is_past_end_date(self, market) -> bool:
|
||||
"""Check if a market's end_date has passed."""
|
||||
if not market.end_date:
|
||||
return True # No end date, always check
|
||||
try:
|
||||
end_dt = datetime.fromisoformat(market.end_date.replace("Z", "+00:00"))
|
||||
return datetime.utcnow().replace(tzinfo=end_dt.tzinfo) >= end_dt
|
||||
except (ValueError, TypeError):
|
||||
return True
|
||||
|
||||
async def check_all(self) -> dict:
|
||||
"""
|
||||
Check all unresolved markets for resolution.
|
||||
|
||||
Returns:
|
||||
Summary dict with counts.
|
||||
"""
|
||||
unresolved_ids = self.db.get_unresolved_market_ids()
|
||||
if not unresolved_ids:
|
||||
logger.debug("No unresolved markets to check")
|
||||
return {"checked": 0, "resolved": 0, "signals_updated": 0}
|
||||
|
||||
logger.info(f"Checking {len(unresolved_ids)} unresolved markets for resolution")
|
||||
|
||||
checked = 0
|
||||
resolved = 0
|
||||
signals_updated = 0
|
||||
|
||||
for market_id in unresolved_ids:
|
||||
try:
|
||||
market = self.market_fetcher.get_market_by_id(market_id)
|
||||
if not market:
|
||||
logger.debug(f"Market {market_id} not found on API")
|
||||
checked += 1
|
||||
await asyncio.sleep(0.5)
|
||||
continue
|
||||
|
||||
# Optimization: skip markets whose end_date hasn't passed yet
|
||||
if not self._is_past_end_date(market):
|
||||
checked += 1
|
||||
await asyncio.sleep(0.5)
|
||||
continue
|
||||
|
||||
outcome = self._determine_resolved_outcome(market)
|
||||
if outcome:
|
||||
updated = self.db.mark_market_resolved(
|
||||
market_id=market_id,
|
||||
resolved_outcome=outcome,
|
||||
resolved_at=datetime.utcnow(),
|
||||
)
|
||||
resolved += 1
|
||||
signals_updated += updated
|
||||
logger.info(
|
||||
f"Market resolved: {market.question[:50]}... "
|
||||
f"outcome={outcome}, {updated} signals updated"
|
||||
)
|
||||
|
||||
checked += 1
|
||||
await asyncio.sleep(0.5) # Rate limiting
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking market {market_id}: {e}")
|
||||
checked += 1
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
result = {
|
||||
"checked": checked,
|
||||
"resolved": resolved,
|
||||
"signals_updated": signals_updated,
|
||||
}
|
||||
logger.info(
|
||||
f"Resolution check complete: {checked} checked, "
|
||||
f"{resolved} resolved, {signals_updated} signals updated"
|
||||
)
|
||||
return result
|
||||
Reference in New Issue
Block a user