from datetime import date from fastapi import APIRouter, Depends, Form, Request from fastapi.responses import HTMLResponse, RedirectResponse from sqlalchemy.orm import Session from app.database import get_db from app.models import OptionTrade, Contract, Product router = APIRouter(prefix="/options", tags=["options"]) OPTION_TYPES = [("C", "C 看涨"), ("P", "P 看跌")] OPTION_DIRECTIONS = [("buy", "买"), ("sell", "卖")] def get_product_contracts(db: Session) -> dict: result: dict[str, list[str]] = {} products = db.query(Product).order_by(Product.code).all() for p in products: codes = [c.code for c in p.contracts if c.is_active] if codes: result[p.code] = codes return result def today_str() -> str: return date.today().isoformat() @router.get("/", response_class=HTMLResponse) def option_page(request: Request, db: Session = Depends(get_db)): view = request.query_params.get("view", "") open_trades = ( db.query(OptionTrade).filter(OptionTrade.status == "open") .order_by(OptionTrade.open_date.desc()).all() ) closed_trades = ( db.query(OptionTrade).filter(OptionTrade.status == "closed") .order_by(OptionTrade.close_date.desc()).limit(50).all() ) template = request.app.state.templates.get_template("options.html") return HTMLResponse( template.render( request=request, active_nav="options", product_contracts=get_product_contracts(db), option_types=OPTION_TYPES, option_directions=OPTION_DIRECTIONS, open_trades=open_trades, closed_trades=closed_trades, today=today_str(), ) ) @router.post("/open") def open_trade( request: Request, contract_code: str = Form(...), option_type: str = Form(...), direction: str = Form(...), strike_price: float = Form(...), open_date: str = Form(...), open_price: float = Form(...), open_fee: float = Form(0.0), db: Session = Depends(get_db), ): code = contract_code.upper() contract = db.query(Contract).filter(Contract.code == code).first() product_code = contract.product.code if contract else code[:2] t = OptionTrade( product_code=product_code, contract_code=code, option_type=option_type, direction=direction, strike_price=strike_price, open_date=date.fromisoformat(open_date), open_price=open_price, open_fee=open_fee, status="open", ) db.add(t) db.commit() return RedirectResponse("/options/", status_code=303) @router.post("/{trade_id}/close") def close_trade( request: Request, trade_id: int, close_date: str = Form(...), close_price: float = Form(...), close_fee: float = Form(0.0), db: Session = Depends(get_db), ): t = db.query(OptionTrade).filter(OptionTrade.id == trade_id).first() if t and t.status == "open": t.close_date = date.fromisoformat(close_date) t.close_price = close_price t.close_fee = close_fee t.status = "closed" db.commit() return RedirectResponse("/options/", status_code=303) @router.post("/{trade_id}/delete") def delete_trade(trade_id: int, db: Session = Depends(get_db)): t = db.query(OptionTrade).filter(OptionTrade.id == trade_id).first() if t: db.delete(t) db.commit() return RedirectResponse("/options/", status_code=303)