from datetime import datetime from fastapi import APIRouter, Depends, Header, HTTPException, Query from sqlalchemy.orm import Session from app.database import get_db from app.models.db_models import Device, OptionPosition from app.models.schemas import OptionPositionCreate, OptionPositionClose, OptionPositionResponse router = APIRouter(prefix="/positions", tags=["positions"]) def _get_device(x_device_token: str = Header(...), db: Session = Depends(get_db)) -> Device: device = db.query(Device).filter(Device.apns_token == x_device_token).first() if not device: raise HTTPException(status_code=404, detail="Device not registered.") return device @router.get("", response_model=list[OptionPositionResponse]) def get_positions( status: str | None = Query(None), device: Device = Depends(_get_device), db: Session = Depends(get_db), ): query = db.query(OptionPosition).filter(OptionPosition.device_id == device.id) if status: query = query.filter(OptionPosition.status == status) return query.order_by(OptionPosition.opened_at.desc()).all() @router.post("", response_model=OptionPositionResponse, status_code=201) def log_position( body: OptionPositionCreate, device: Device = Depends(_get_device), db: Session = Depends(get_db), ): position = OptionPosition( device_id=device.id, ticker=body.ticker, strategy=body.strategy, strike=body.strike, expiration=body.expiration, premium_received=body.premium_received, contracts=body.contracts, status="open", ) db.add(position) db.commit() db.refresh(position) return position @router.patch("/{position_id}", response_model=OptionPositionResponse) def close_position( position_id: int, body: OptionPositionClose, device: Device = Depends(_get_device), db: Session = Depends(get_db), ): position = ( db.query(OptionPosition) .filter(OptionPosition.id == position_id, OptionPosition.device_id == device.id) .first() ) if not position: raise HTTPException(status_code=404, detail="Position not found.") valid_statuses = ("closed", "rolled") if body.status not in valid_statuses: raise HTTPException(status_code=422, detail=f"status must be one of {valid_statuses}") position.status = body.status position.close_reason = body.close_reason position.closed_at = datetime.utcnow() db.commit() db.refresh(position) return position