Files
finance-talk/ft-app/app/routers/positions.py
T
2026-07-26 10:34:25 +08:00

123 lines
3.5 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 Round, Position, Contract
router = APIRouter(prefix="/positions", tags=["positions"])
def get_active_contracts(db: Session) -> list[str]:
contracts = (
db.query(Contract.code)
.filter(Contract.is_active == True)
.order_by(Contract.code)
.all()
)
return [c[0] for c in contracts]
@router.get("/", response_class=HTMLResponse)
def positions_page(request: Request, db: Session = Depends(get_db)):
active_rounds = (
db.query(Round)
.filter(Round.status == "active")
.order_by(Round.started_at.desc())
.all()
)
template = request.app.state.templates.get_template("positions.html")
return HTMLResponse(
template.render(
request=request,
active_nav="positions",
contracts=get_active_contracts(db),
active_rounds=active_rounds,
)
)
@router.post("/round")
def create_round(
request: Request,
contract_code: str = Form(...),
daily_hands: int = Form(1),
db: Session = Depends(get_db),
):
code = contract_code.upper()
existing = db.query(Round).filter(
Round.status == "active", Round.contract_code == code
).first()
if existing:
return RedirectResponse(f"/positions/?error={code} 已有活跃轮次", status_code=303)
r = Round(
contract_code=code,
daily_hands=daily_hands,
status="active",
started_at=date.today(),
)
db.add(r)
db.commit()
return RedirectResponse("/positions/", status_code=303)
@router.post("/{round_id}/add")
def add_position(
request: Request,
round_id: int,
open_price: int = Form(...),
amp_threshold: int = Form(...),
hands: int = Form(1),
db: Session = Depends(get_db),
):
r = db.query(Round).filter(Round.id == round_id).first()
if not r or r.status != "active":
return RedirectResponse("/positions/?error=轮次不存在或已结束", status_code=303)
lock_price = open_price + amp_threshold
p = Position(
round_id=r.id,
open_price=open_price,
amp_threshold=amp_threshold,
lock_price=lock_price,
hands=hands,
opened_at=date.today(),
)
db.add(p)
db.commit()
return RedirectResponse("/positions/", status_code=303)
@router.post("/{position_id}/lock")
def lock_position(position_id: int, db: Session = Depends(get_db)):
p = db.query(Position).filter(Position.id == position_id).first()
if p and p.locked_count < p.hands:
p.locked_count += 1
db.commit()
return RedirectResponse("/positions/", status_code=303)
@router.post("/{position_id}/unlock")
def unlock_position(position_id: int, db: Session = Depends(get_db)):
p = db.query(Position).filter(Position.id == position_id).first()
if p and p.locked_count > 0:
p.locked_count -= 1
db.commit()
return RedirectResponse("/positions/", status_code=303)
@router.post("/{round_id}/close")
def close_round(
request: Request,
round_id: int,
result: str = Form(...),
db: Session = Depends(get_db),
):
r = db.query(Round).filter(Round.id == round_id).first()
if r and r.status == "active":
r.status = result
db.commit()
return RedirectResponse("/positions/", status_code=303)