import aiohttp
import asyncio
from datetime import datetime
from typing import Optional
import structlog
from src.config import get_settings
logger = structlog.get_logger()
settings = get_settings()
class CMEDataFetcher:
"""Fetches CME prediction market data with retry logic."""
def __init__(self):
self.url = settings.CME_DATA_URL
self.retry_attempts = settings.CME_RETRY_ATTEMPTS
self.timeout = aiohttp.ClientTimeout(total=60)
async def fetch_csv(self) -> Optional[bytes]:
"""Fetch CSV data from CME with retry logic."""
for attempt in range(self.retry_attempts):
try:
async with aiohttp.ClientSession(timeout=self.timeout) as session:
async with session.get(self.url) as response:
if response.status == 200:
data = await response.read()
logger.info(
"cme_data_fetched",
size=len(data),
attempt=attempt + 1,
)
return data
else:
logger.warning(
"cme_fetch_failed",
status=response.status,
attempt=attempt + 1,
)
except asyncio.TimeoutError:
logger.error("cme_fetch_timeout", attempt=attempt + 1)
except Exception as e:
logger.error("cme_fetch_error", error=str(e), attempt=attempt + 1)
if attempt < self.retry_attempts - 1:
await asyncio.sleep(2 ** attempt) # Exponential backoff
logger.error("cme_fetch_failed_all_attempts")
return None
async def save_to_file(self, data: bytes, filepath: str) -> bool:
"""Save fetched data to file."""
try:
with open(filepath, 'wb') as f:
f.write(data)
logger.info("cme_data_saved", filepath=filepath)
return True
except Exception as e:
logger.error("cme_save_error", error=str(e), filepath=filepath)
return False