cd7f6668af
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
165 lines
5.4 KiB
Python
165 lines
5.4 KiB
Python
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 Product, Contract, DailyBar, PositionRanking
|
|
from app.collector import sync_active_contracts, sync_one_contract, sync_position_rankings
|
|
|
|
router = APIRouter(prefix="/admin", tags=["admin"])
|
|
|
|
EXCHANGES = ["CZCE", "DCE", "SHFE", "CFFEX", "INE"]
|
|
|
|
|
|
@router.get("/", response_class=HTMLResponse)
|
|
def admin_page(request: Request, db: Session = Depends(get_db)):
|
|
products = db.query(Product).order_by(Product.code).all()
|
|
|
|
product_data = []
|
|
for p in products:
|
|
contracts = (
|
|
db.query(Contract)
|
|
.filter(Contract.product_id == p.id)
|
|
.order_by(Contract.code)
|
|
.all()
|
|
)
|
|
contract_list = []
|
|
for c in contracts:
|
|
bar_count = (
|
|
db.query(DailyBar)
|
|
.filter(DailyBar.contract == c.code)
|
|
.count()
|
|
)
|
|
contract_list.append({
|
|
"id": c.id, "code": c.code, "name": c.name,
|
|
"is_active": c.is_active, "bar_count": bar_count,
|
|
})
|
|
|
|
product_data.append({
|
|
"id": p.id,
|
|
"code": p.code,
|
|
"name": p.name,
|
|
"exchange": p.exchange,
|
|
"contracts": contract_list,
|
|
})
|
|
|
|
total_contracts = sum(len(pd["contracts"]) for pd in product_data)
|
|
|
|
template = request.app.state.templates.get_template("admin.html")
|
|
return HTMLResponse(
|
|
template.render(
|
|
request=request,
|
|
active_nav="admin",
|
|
products=product_data,
|
|
total_contracts=total_contracts,
|
|
exchanges=EXCHANGES,
|
|
)
|
|
)
|
|
|
|
|
|
@router.post("/product")
|
|
def create_product(
|
|
request: Request,
|
|
code: str = Form(...),
|
|
name: str = Form(...),
|
|
exchange: str = Form(...),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
existing = db.query(Product).filter(Product.code == code.upper()).first()
|
|
if not existing:
|
|
p = Product(code=code.upper(), name=name, exchange=exchange)
|
|
db.add(p)
|
|
db.commit()
|
|
return RedirectResponse("/admin/?tab=product", status_code=303)
|
|
|
|
|
|
@router.post("/contract")
|
|
def create_contract(
|
|
request: Request,
|
|
product_id: int = Form(...),
|
|
month: str = Form(...),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
product = db.query(Product).filter(Product.id == product_id).first()
|
|
if not product:
|
|
return RedirectResponse("/admin/?tab=contract", status_code=303)
|
|
|
|
code = (product.code + month).upper()
|
|
name = month.upper()
|
|
|
|
existing = db.query(Contract).filter(Contract.code == code).first()
|
|
if not existing:
|
|
c = Contract(
|
|
product_id=product_id,
|
|
code=code,
|
|
name=name,
|
|
is_active=True,
|
|
)
|
|
db.add(c)
|
|
db.commit()
|
|
return RedirectResponse("/admin/?tab=contract", status_code=303)
|
|
|
|
|
|
@router.post("/contract/{contract_id}/toggle")
|
|
def toggle_contract(contract_id: int, db: Session = Depends(get_db)):
|
|
c = db.query(Contract).filter(Contract.id == contract_id).first()
|
|
if c:
|
|
c.is_active = not c.is_active
|
|
db.commit()
|
|
return RedirectResponse("/admin/?tab=contract", status_code=303)
|
|
|
|
|
|
@router.post("/contract/{contract_id}/delete")
|
|
def delete_contract(contract_id: int, db: Session = Depends(get_db)):
|
|
c = db.query(Contract).filter(Contract.id == contract_id).first()
|
|
if c:
|
|
db.query(DailyBar).filter(DailyBar.contract == c.code).delete()
|
|
db.query(PositionRanking).filter(PositionRanking.contract_code == c.code).delete()
|
|
db.delete(c)
|
|
db.commit()
|
|
return RedirectResponse("/admin/?tab=contract", status_code=303)
|
|
|
|
|
|
@router.post("/product/{product_id}/delete")
|
|
def delete_product(product_id: int, db: Session = Depends(get_db)):
|
|
p = db.query(Product).filter(Product.id == product_id).first()
|
|
if p:
|
|
for c in p.contracts:
|
|
db.query(DailyBar).filter(DailyBar.contract == c.code).delete()
|
|
db.query(PositionRanking).filter(PositionRanking.contract_code == c.code).delete()
|
|
db.delete(p)
|
|
db.commit()
|
|
return RedirectResponse("/admin/?tab=product", status_code=303)
|
|
|
|
|
|
@router.post("/sync")
|
|
def sync_all(request: Request):
|
|
results = sync_active_contracts()
|
|
total = sum(results.values())
|
|
print(f"[sync] Synced {total} bars across {len(results)} contracts: {results}")
|
|
return RedirectResponse(f"/admin/?tab=sync&synced={total}", status_code=303)
|
|
|
|
|
|
@router.post("/sync/{contract_code}")
|
|
def sync_single(contract_code: str):
|
|
count = sync_one_contract(contract_code.upper())
|
|
return RedirectResponse(f"/admin/?tab=sync&synced={count}", status_code=303)
|
|
|
|
|
|
@router.post("/sync-positions")
|
|
def sync_positions(request: Request):
|
|
results = sync_position_rankings()
|
|
total = sum(results.values())
|
|
print(f"[sync] Position rankings: {total} rows across {len(results)} contracts")
|
|
return RedirectResponse(f"/admin/?tab=sync&pos_synced={total}", status_code=303)
|
|
|
|
|
|
@router.post("/sync/product/{product_id}")
|
|
def sync_product(product_id: int, request: Request, db: Session = Depends(get_db)):
|
|
contracts = db.query(Contract).filter(
|
|
Contract.product_id == product_id, Contract.is_active == True
|
|
).all()
|
|
total = 0
|
|
for c in contracts:
|
|
total += sync_one_contract(c.code)
|
|
return RedirectResponse(f"/admin/?tab=sync&synced={total}", status_code=303)
|