0684f15859
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
150 lines
4.7 KiB
Python
150 lines
4.7 KiB
Python
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(),
|
|
weekdays=["周一","周二","周三","周四","周五","周六","周日"],
|
|
)
|
|
)
|
|
|
|
|
|
@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}/edit")
|
|
def edit_trade(
|
|
request: Request,
|
|
trade_id: int,
|
|
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),
|
|
close_date: str = Form(""),
|
|
close_price: float = Form(None),
|
|
close_fee: float = Form(None),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
t = db.query(OptionTrade).filter(OptionTrade.id == trade_id).first()
|
|
if t:
|
|
code = contract_code.upper()
|
|
contract = db.query(Contract).filter(Contract.code == code).first()
|
|
t.product_code = contract.product.code if contract else code[:2]
|
|
t.contract_code = code
|
|
t.option_type = option_type
|
|
t.direction = direction
|
|
t.strike_price = strike_price
|
|
t.open_date = date.fromisoformat(open_date)
|
|
t.open_price = open_price
|
|
t.open_fee = open_fee
|
|
if close_date and close_price is not None:
|
|
t.close_date = date.fromisoformat(close_date)
|
|
t.close_price = close_price
|
|
t.close_fee = close_fee or 0
|
|
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)
|