trading-platform-data-service/src/routers/sync.py
rckrdmrd 01683a850f feat: Initial Data Service implementation
- FastAPI service for market data synchronization from Polygon.io
- Async PostgreSQL integration with SQLAlchemy
- API endpoints for tickers, OHLCV data, and sync operations
- Rate-limited Polygon.io API client
- Background task support for historical data sync
- Support for 6 assets: XAUUSD, EURUSD, BTCUSD, GBPUSD, USDJPY, AUDUSD

Endpoints:
- GET /api/v1/tickers - List all tickers
- GET /api/v1/tickers/{symbol}/ohlcv - Get OHLCV data
- GET /api/v1/tickers/{symbol}/latest - Get latest price
- POST /api/v1/sync/historical - Start historical sync
- POST /api/v1/sync/ticker/{symbol} - Sync single ticker

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 13:28:18 -06:00

134 lines
3.9 KiB
Python

"""
Sync API Router - Endpoints for data synchronization.
"""
from fastapi import APIRouter, BackgroundTasks, HTTPException
from datetime import datetime
from typing import List, Optional
from src.schemas import SyncRequest, SyncResponse, SyncStatus
from src.services.sync_service import SyncService
router = APIRouter(prefix="/api/v1/sync", tags=["sync"])
# Singleton sync service
sync_service = SyncService()
@router.get("/status", response_model=SyncResponse)
async def get_sync_status():
"""Get current synchronization status."""
status = sync_service.get_status()
return SyncResponse(
status=SyncStatus(status.get("status", "idle")),
message=status.get("message", ""),
symbols_processed=status.get("symbols_processed", []),
records_inserted=status.get("records_inserted", 0),
errors=status.get("errors", []),
started_at=status.get("started_at"),
completed_at=status.get("completed_at")
)
@router.post("/historical", response_model=SyncResponse)
async def sync_historical(
request: SyncRequest,
background_tasks: BackgroundTasks
):
"""
Start historical data synchronization.
This endpoint starts a background task to sync historical data
from Polygon.io to the database.
"""
# Check if sync is already running
current_status = sync_service.get_status()
if current_status.get("status") == "running":
raise HTTPException(
status_code=409,
detail="Sync already in progress"
)
# Start sync in background
background_tasks.add_task(
sync_service.sync_all,
symbols=request.symbols,
start_date=request.start_date,
end_date=request.end_date,
timeframe=request.timeframe
)
return SyncResponse(
status=SyncStatus.pending,
message="Historical sync started in background",
symbols_processed=[],
records_inserted=0,
errors=[],
started_at=datetime.now(),
completed_at=None
)
@router.post("/latest", response_model=SyncResponse)
async def sync_latest(
symbols: Optional[List[str]] = None,
background_tasks: BackgroundTasks = None
):
"""
Sync latest data for all or specified tickers.
This fetches the most recent data since the last sync.
"""
# Check if sync is already running
current_status = sync_service.get_status()
if current_status.get("status") == "running":
raise HTTPException(
status_code=409,
detail="Sync already in progress"
)
# Start sync in background
background_tasks.add_task(
sync_service.sync_all,
symbols=symbols,
start_date=None, # Will auto-detect from last timestamp
end_date=None,
timeframe="5m"
)
return SyncResponse(
status=SyncStatus.pending,
message="Latest data sync started in background",
symbols_processed=[],
records_inserted=0,
errors=[],
started_at=datetime.now(),
completed_at=None
)
@router.post("/ticker/{symbol}", response_model=SyncResponse)
async def sync_single_ticker(
symbol: str,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None,
timeframe: str = "5m"
):
"""Sync data for a single ticker (synchronous)."""
# This runs synchronously for single ticker
result = await sync_service.sync_all(
symbols=[symbol.upper()],
start_date=start_date,
end_date=end_date,
timeframe=timeframe
)
return SyncResponse(
status=SyncStatus(result.get("status", "completed")),
message=result.get("message", ""),
symbols_processed=result.get("symbols_processed", []),
records_inserted=result.get("records_inserted", 0),
errors=result.get("errors", []),
started_at=result.get("started_at"),
completed_at=result.get("completed_at")
)