from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from sqlalchemy.exc import OperationalError import time from db import SessionLocal from models.bid import Bid as BidModel from models.auth import User from pydantic import BaseModel from datetime import datetime, timezone from zoneinfo import ZoneInfo from typing import List, Optional router = APIRouter() MARKET_TIMEZONES = { "ISONE": ZoneInfo("America/New_York"), "NYISO": ZoneInfo("America/New_York"), "MISO": ZoneInfo("America/Chicago"), } def get_db(): db = SessionLocal() try: yield db finally: db.close() class BidBase(BaseModel): timestamp: datetime quantity: float price: float user_id: ( int # In production the user_id should be obtained from the authenticated user ) market: str class BidCreate(BidBase): pass class BidResponse(BidBase): id: int status: str pnl: Optional[float] class Config: from_attributes = True @router.get("/", response_model=List[BidResponse]) def get_bids(db: Session = Depends(get_db)): return db.query(BidModel).all() @router.post("/", response_model=BidResponse) def submit_bid(bid: BidCreate, db: Session = Depends(get_db)): if bid.market not in MARKET_TIMEZONES: raise HTTPException( status_code=400, detail=f"Invalid market. Supported markets: {list(MARKET_TIMEZONES.keys())}", ) market_tz = MARKET_TIMEZONES[bid.market] now = datetime.now(market_tz) bid_timestamp_local = bid.timestamp.astimezone(market_tz) bid_day = bid_timestamp_local.date() today = now.date() print(f"Received bid for {bid_day} at {bid.timestamp} ({bid.market})") if bid.timestamp < now: raise HTTPException(status_code=400, detail="Cannot submit bids in the past.") cutoff_time = now.replace(hour=11, minute=0, second=0, microsecond=0) if bid_day == today: if now > cutoff_time: raise HTTPException( status_code=400, detail="Cannot submit bids for today after 11AM local time.", ) start_of_hour = bid.timestamp.replace(minute=0, second=0, microsecond=0) end_of_hour = start_of_hour.replace(minute=59, second=59, microsecond=999999) bid_count = ( db.query(BidModel) .filter( BidModel.timestamp >= start_of_hour, BidModel.timestamp <= end_of_hour, BidModel.market == bid.market, ) .count() ) if bid_count >= 10: raise HTTPException( status_code=400, detail="Cannot submit more than 10 bids for this hour in this market.", ) db_bid = BidModel( timestamp=bid.timestamp, quantity=bid.quantity, price=bid.price, user_id=bid.user_id, market=bid.market, status="Submitted", pnl=None, ) db.add(db_bid) max_retries = 3 for attempt in range(max_retries): try: db.commit() break except OperationalError as e: if "database is locked" in str(e).lower(): if attempt < max_retries - 1: time.sleep(2) continue else: raise HTTPException( status_code=500, detail="Database is busy. Please try again later.", ) else: raise db.refresh(db_bid) return db_bid