移除博弈分析功能

This commit is contained in:
2026-07-24 23:05:54 +08:00
parent 492ca9d82e
commit c0e338eb23
6 changed files with 3 additions and 433 deletions
-26
View File
@@ -1,26 +0,0 @@
"""博弈分析计算引擎"""
def net_position(long_pos: int, short_pos: int) -> int:
"""净持仓 = 多单 - 空单。正=净多, 负=净空"""
return long_pos - short_pos
def net_pnl(net_pos: int, avg_cost: float, current_price: float) -> float:
"""净盈亏 = 净持仓 × (现价 - 成本均价) × 20"""
return net_pos * (current_price - avg_cost) * 20
def cost_delta(old_cost: float, new_cost: float) -> float:
"""成本变化 = 新均价 - 旧均价"""
return round(new_cost - old_cost, 2)
def format_pnl(pnl_yuan: float) -> str:
"""格式化盈亏为亿/万"""
yi = abs(pnl_yuan) / 1e8
if yi >= 0.01:
sign = "+" if pnl_yuan >= 0 else "-"
return f"{sign}{yi:.2f}亿"
wan = abs(pnl_yuan) / 1e4
return f"{'+' if pnl_yuan >= 0 else '-'}{wan:.1f}"
+1 -2
View File
@@ -7,7 +7,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
from app.database import engine, Base, SessionLocal from app.database import engine, Base, SessionLocal
from app.models import User from app.models import User
from app.seed import seed from app.seed import seed
from app.routers import contracts, analysis, admin, auth from app.routers import contracts, admin, auth
TEMPLATES_DIR = Path(__file__).parent / "templates" TEMPLATES_DIR = Path(__file__).parent / "templates"
@@ -55,7 +55,6 @@ app.add_middleware(AuthMiddleware)
app.include_router(auth.router) app.include_router(auth.router)
app.include_router(contracts.router) app.include_router(contracts.router)
app.include_router(analysis.router)
app.include_router(admin.router) app.include_router(admin.router)
-125
View File
@@ -1,125 +0,0 @@
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from sqlalchemy.orm import Session
from app.database import get_db
from app.models import PositionSnapshot, Contract, DailyBar
from app.engine.game_theory import net_position, net_pnl, format_pnl
router = APIRouter(prefix="/analysis", tags=["analysis"])
INSTITUTIONS = ["中信期货", "高盛期货", "国泰君安期货", "华泰期货", "东证期货", "银河期货"]
@router.get("/", response_class=HTMLResponse)
def analysis_page(request: Request, db: Session = Depends(get_db)):
# Get active contracts for selector
active_contracts = (
db.query(Contract.code)
.filter(Contract.is_active == True)
.order_by(Contract.code)
.all()
)
contract_list = [c[0] for c in active_contracts]
# Default to first contract, or use query param
selected = request.query_params.get("contract", contract_list[0] if contract_list else None)
if not selected:
template = request.app.state.templates.get_template("analysis.html")
return HTMLResponse(
template.render(request=request, active_nav="analysis", rows=[], latest_date=None)
)
# Get latest date for selected contract
latest_snap = (
db.query(PositionSnapshot.date)
.filter(PositionSnapshot.contract_code == selected)
.order_by(PositionSnapshot.date.desc())
.first()
)
if not latest_snap:
template = request.app.state.templates.get_template("analysis.html")
return HTMLResponse(
template.render(
request=request, active_nav="analysis",
rows=[], latest_date=None, contracts=contract_list, selected=selected,
)
)
latest_date = latest_snap[0]
# Get latest close for current_price reference
latest_bar = (
db.query(DailyBar)
.filter(DailyBar.contract == selected)
.order_by(DailyBar.date.desc())
.first()
)
current_price = int(latest_bar.close) if latest_bar else 0
rows = []
total_net_short = 0
pnl_sum = 0.0
for inst in INSTITUTIONS:
long = (
db.query(PositionSnapshot)
.filter(
PositionSnapshot.contract_code == selected,
PositionSnapshot.institution == inst,
PositionSnapshot.direction == "long",
PositionSnapshot.date == latest_date,
)
.first()
)
short = (
db.query(PositionSnapshot)
.filter(
PositionSnapshot.contract_code == selected,
PositionSnapshot.institution == inst,
PositionSnapshot.direction == "short",
PositionSnapshot.date == latest_date,
)
.first()
)
long_pos = long.position if long else 0
short_pos = short.position if short else 0
long_cost = long.avg_cost if long else 0
short_cost = short.avg_cost if short else 0
np = net_position(long_pos, short_pos)
pnl = 0.0
if np < 0:
pnl = net_pnl(np, short_cost, current_price)
elif np > 0:
pnl = net_pnl(np, long_cost, current_price)
if np < 0:
total_net_short += abs(np)
pnl_sum += pnl
rows.append({
"institution": inst,
"long_pos": f"{long_pos / 10000:.1f}" if long_pos else "",
"short_pos": f"{short_pos / 10000:.1f}" if short_pos else "",
"net_pos": f"{'' if np > 0 else ''} {abs(np) / 10000:.1f}",
"pnl": format_pnl(pnl),
"pnl_raw": pnl,
})
template = request.app.state.templates.get_template("analysis.html")
return HTMLResponse(
template.render(
request=request,
active_nav="analysis",
rows=rows,
latest_date=latest_date.strftime("%Y-%m-%d"),
current_price=current_price,
total_net_short=f"{total_net_short / 10000:.1f}",
total_pnl=format_pnl(pnl_sum),
contracts=contract_list,
selected=selected,
)
)
+2 -202
View File
@@ -1,7 +1,7 @@
"""Seed database from existing data files. Run once manually or on first start.""" """Seed database from existing data files. Run once manually or on first start."""
from datetime import date from datetime import date
from app.database import engine, Base, SessionLocal from app.database import engine, Base, SessionLocal
from app.models import DailyBar, PositionSnapshot, Product, Contract, User from app.models import DailyBar, Product, Contract, User
from app.engine.lock_strategy import compute_amp_5d from app.engine.lock_strategy import compute_amp_5d
# --- Seed OHLCV data --- # --- Seed OHLCV data ---
@@ -27,176 +27,6 @@ SEED_BARS: list[dict] = [
{"date": "2026-07-24", "open": 900, "close": 908, "high": 912, "low": 891}, {"date": "2026-07-24", "open": 900, "close": 908, "high": 912, "low": 891},
] ]
# --- Position snapshots (from info.txt) ---
POSITION_DATA_SHORT: list[dict] = [
("中信期货", "short", "2026-07-22", 193030, -22032, 995.26),
("中信期货", "short", "2026-07-21", 215062, -1029, 995.26),
("中信期货", "short", "2026-07-20", 216091, -18178, 995.26),
("中信期货", "short", "2026-07-17", 234269, -31, 995.26),
("中信期货", "short", "2026-07-16", 234300, 35258, 995.26),
("中信期货", "short", "2026-07-15", 199042, 5294, 1005.40),
("中信期货", "short", "2026-07-14", 193748, 4117, 1006.94),
("中信期货", "short", "2026-07-13", 189631, 18219, 1008.16),
("中信期货", "short", "2026-07-10", 171412, -17319, 1013.49),
("中信期货", "short", "2026-07-09", 188731, 9860, 1013.49),
("中信期货", "short", "2026-07-08", 178871, -19747, 1016.71),
("中信期货", "short", "2026-07-07", 198618, 25262, 1016.71),
("中信期货", "short", "2026-07-06", 173356, 2998, 1025.41),
("中信期货", "short", "2026-07-03", 170358, -11691, 1026.30),
("中信期货", "short", "2026-07-02", 182049, 1284, 1026.30),
("中信期货", "long", "2026-07-22", 63971, -10850, 988.16),
("中信期货", "long", "2026-07-21", 74821, 2587, 988.16),
("中信期货", "long", "2026-07-20", 72234, -84, 991.28),
("中信期货", "long", "2026-07-17", 72318, 6928, 991.28),
("中信期货", "long", "2026-07-16", 65390, 6950, 999.15),
("中信期货", "long", "2026-07-15", 58440, -1227, 1006.42),
("中信期货", "long", "2026-07-14", 59667, 887, 1006.42),
("中信期货", "long", "2026-07-13", 58780, -4908, 1007.26),
("中信期货", "long", "2026-07-10", 63688, 5795, 1007.26),
("中信期货", "long", "2026-07-09", 57893, -3193, 1011.59),
("中信期货", "long", "2026-07-08", 61086, 2092, 1011.59),
("中信期货", "long", "2026-07-07", 58994, 8116, 1013.45),
("中信期货", "long", "2026-07-06", 50878, -14738, 1022.46),
("中信期货", "long", "2026-07-03", 65616, 9777, 1022.46),
("中信期货", "long", "2026-07-02", 55839, -2024, 1031.30),
("高盛期货", "short", "2026-07-22", 188460, -14927, 997.89),
("高盛期货", "short", "2026-07-21", 203387, -15903, 997.89),
("高盛期货", "short", "2026-07-20", 219290, 1220, 997.89),
("高盛期货", "short", "2026-07-17", 218070, 14452, 998.44),
("高盛期货", "short", "2026-07-16", 203618, 12879, 1004.22),
("高盛期货", "short", "2026-07-15", 190739, -5701, 1008.69),
("高盛期货", "short", "2026-07-14", 196440, 1711, 1008.69),
("高盛期货", "short", "2026-07-13", 194729, 418, 1009.20),
("高盛期货", "short", "2026-07-10", 194311, -5203, 1009.31),
("高盛期货", "short", "2026-07-09", 199514, 7653, 1009.31),
("高盛期货", "short", "2026-07-08", 191861, 1394, 1011.47),
("高盛期货", "short", "2026-07-07", 190467, 12046, 1011.86),
("高盛期货", "short", "2026-07-06", 178421, 578, 1015.56),
("高盛期货", "short", "2026-07-03", 177843, 1049, 1015.69),
("高盛期货", "short", "2026-07-02", 176794, 19121, 1015.95),
("国泰君安期货", "short", "2026-07-22", 168449, -9289, 1018.54),
("国泰君安期货", "short", "2026-07-21", 177738, -10380, 1018.54),
("国泰君安期货", "short", "2026-07-20", 188118, -731, 1018.54),
("国泰君安期货", "short", "2026-07-17", 188849, -12984, 1018.54),
("国泰君安期货", "short", "2026-07-16", 201833, -5144, 1018.54),
("国泰君安期货", "short", "2026-07-15", 206977, 2787, 1018.54),
("国泰君安期货", "short", "2026-07-14", 204190, -2399, 1019.49),
("国泰君安期货", "short", "2026-07-13", 206589, 24344, 1019.49),
("国泰君安期货", "short", "2026-07-10", 182245, -4391, 1027.70),
("国泰君安期货", "short", "2026-07-09", 186636, -6510, 1027.70),
("国泰君安期货", "short", "2026-07-08", 193146, -6957, 1027.70),
("国泰君安期货", "short", "2026-07-07", 200103, 9696, 1027.70),
("国泰君安期货", "short", "2026-07-06", 190407, 1344, 1031.30),
("国泰君安期货", "short", "2026-07-03", 189063, -1588, 1031.70),
("国泰君安期货", "short", "2026-07-02", 190651, 4656, 1031.70),
("国泰君安期货", "long", "2026-07-22", 144325, -6262, 991.07),
("国泰君安期货", "long", "2026-07-21", 150587, 17790, 991.07),
("国泰君安期货", "long", "2026-07-20", 132797, 3578, 1003.13),
("国泰君安期货", "long", "2026-07-17", 129219, 12066, 1005.99),
("国泰君安期货", "long", "2026-07-16", 117153, 8387, 1015.15),
("国泰君安期货", "long", "2026-07-15", 108766, 5534, 1021.10),
("国泰君安期货", "long", "2026-07-14", 103232, 4815, 1024.97),
("国泰君安期货", "long", "2026-07-13", 98417, 4839, 1028.58),
("国泰君安期货", "long", "2026-07-10", 93578, -1293, 1032.23),
("国泰君安期货", "long", "2026-07-09", 94871, -807, 1032.23),
("国泰君安期货", "long", "2026-07-08", 95678, -5169, 1032.23),
("国泰君安期货", "long", "2026-07-07", 100847, 3412, 1032.23),
("国泰君安期货", "long", "2026-07-06", 97435, -4615, 1034.87),
("国泰君安期货", "long", "2026-07-03", 102050, 2169, 1034.87),
("国泰君安期货", "long", "2026-07-02", 99881, 9801, 1036.23),
("华泰期货", "short", "2026-07-22", 80564, -8762, 976.69),
("华泰期货", "short", "2026-07-21", 89326, 9844, 976.69),
("华泰期货", "short", "2026-07-20", 79482, 1751, 986.06),
("华泰期货", "short", "2026-07-17", 77731, -8367, 988.00),
("华泰期货", "short", "2026-07-16", 86098, 11358, 988.00),
("华泰期货", "short", "2026-07-15", 74740, 3028, 995.60),
("华泰期货", "short", "2026-07-14", 71712, -2199, 997.57),
("华泰期货", "short", "2026-07-13", 73911, 10715, 997.57),
("华泰期货", "short", "2026-07-10", 63196, -3512, 1004.28),
("华泰期货", "short", "2026-07-09", 66708, 3827, 1004.28),
("华泰期货", "short", "2026-07-08", 62881, -10077, 1007.27),
("华泰期货", "short", "2026-07-07", 72958, 12276, 1007.27),
("华泰期货", "short", "2026-07-06", 60682, -6933, 1017.45),
("华泰期货", "short", "2026-07-03", 65615, -5963, 1017.45),
("华泰期货", "short", "2026-07-02", 71558, -2128, 1017.45),
("华泰期货", "long", "2026-07-22", 68791, -5130, 1002.62),
("华泰期货", "long", "2026-07-21", 73921, -274, 1002.62),
("华泰期货", "long", "2026-07-20", 74195, 840, 1002.62),
("华泰期货", "long", "2026-07-17", 73355, 7012, 1003.79),
("华泰期货", "long", "2026-07-16", 66343, 4562, 1012.97),
("华泰期货", "long", "2026-07-15", 61781, -1562, 1018.50),
("华泰期货", "long", "2026-07-14", 63343, 2364, 1018.50),
("华泰期货", "long", "2026-07-13", 60979, -1843, 1021.12),
("华泰期货", "long", "2026-07-10", 62822, 1161, 1021.12),
("华泰期货", "long", "2026-07-09", 61661, 162, 1022.19),
("华泰期货", "long", "2026-07-08", 61499, 82, 1022.37),
("华泰期货", "long", "2026-07-07", 61417, 6050, 1022.46),
("华泰期货", "long", "2026-07-06", 55367, -2699, 1029.61),
("华泰期货", "long", "2026-07-03", 58066, -4006, 1029.61),
("华泰期货", "long", "2026-07-02", 62072, -2935, 1029.61),
("东证期货", "short", "2026-07-22", 112614, -18937, 965.67),
("东证期货", "short", "2026-07-21", 131551, -45486, 965.67),
("东证期货", "short", "2026-07-20", 177037, -1629, 965.67),
("东证期货", "short", "2026-07-17", 178666, -4252, 965.67),
("东证期货", "short", "2026-07-16", 182918, 47376, 965.67),
("东证期货", "short", "2026-07-15", 135542, -13505, 975.35),
("东证期货", "short", "2026-07-14", 149047, -1108, 975.35),
("东证期货", "short", "2026-07-13", 150155, 40931, 975.35),
("东证期货", "short", "2026-07-10", 109224, -23556, 981.85),
("东证期货", "short", "2026-07-09", 132780, 25017, 981.85),
("东证期货", "short", "2026-07-08", 107763, -56346, 988.08),
("东证期货", "short", "2026-07-07", 164109, 37956, 988.08),
("东证期货", "short", "2026-07-06", 126153, 20536, 997.43),
("东证期货", "short", "2026-07-03", 105617, -10003, 1001.79),
("东证期货", "short", "2026-07-02", 115620, -16409, 1001.79),
("东证期货", "long", "2026-07-22", 85630, 6685, 965.35),
("东证期货", "long", "2026-07-21", 78945, 8359, 970.20),
("东证期货", "long", "2026-07-20", 70586, 2334, 978.40),
("东证期货", "long", "2026-07-17", 68252, -5918, 981.08),
("东证期货", "long", "2026-07-16", 74170, 10413, 981.08),
("东证期货", "long", "2026-07-15", 63757, 1640, 988.12),
("东证期货", "long", "2026-07-14", 62117, -2136, 989.15),
("东证期货", "long", "2026-07-13", 64253, -14158, 989.15),
("东证期货", "long", "2026-07-10", 78411, 7829, 989.15),
("东证期货", "long", "2026-07-09", 70582, -9231, 991.94),
("东证期货", "long", "2026-07-08", 79813, 11598, 991.94),
("东证期货", "long", "2026-07-07", 68215, 10382, 997.54),
("东证期货", "long", "2026-07-06", 57833, -30335, 1004.82),
("东证期货", "long", "2026-07-03", 88168, 20705, 1004.82),
("东证期货", "long", "2026-07-02", 67463, 3862, 1014.89),
("银河期货", "short", "2026-07-22", 62130, -3771, 1022.69),
("银河期货", "short", "2026-07-21", 65901, -3865, 1022.69),
("银河期货", "short", "2026-07-20", 69766, 1572, 1022.69),
("银河期货", "short", "2026-07-17", 68194, 431, 1025.51),
("银河期货", "short", "2026-07-16", 67763, -936, 1026.20),
("银河期货", "short", "2026-07-15", 68699, 372, 1026.20),
("银河期货", "short", "2026-07-14", 68327, 5179, 1026.62),
("银河期货", "short", "2026-07-13", 63148, -3119, 1032.83),
("银河期货", "short", "2026-07-10", 66267, -431, 1032.83),
("银河期货", "short", "2026-07-09", 66698, 1995, 1032.83),
("银河期货", "short", "2026-07-08", 64703, -5025, 1035.23),
("银河期货", "short", "2026-07-07", 69728, 6103, 1035.23),
("银河期货", "short", "2026-07-06", 63625, -1703, 1042.73),
("银河期货", "short", "2026-07-03", 65328, -3861, 1042.73),
("银河期货", "short", "2026-07-02", 69189, 1875, 1042.73),
("银河期货", "long", "2026-07-22", 57020, -7565, 1016.17),
("银河期货", "long", "2026-07-21", 64585, -4812, 1016.17),
("银河期货", "long", "2026-07-20", 69397, -2877, 1016.17),
("银河期货", "long", "2026-07-17", 72274, -4410, 1016.17),
("银河期货", "long", "2026-07-16", 76684, 4888, 1016.17),
("银河期货", "long", "2026-07-15", 71796, -3235, 1021.49),
("银河期货", "long", "2026-07-14", 75031, -456, 1021.49),
("银河期货", "long", "2026-07-13", 75487, 4361, 1021.49),
("银河期货", "long", "2026-07-10", 71126, -6491, 1025.39),
("银河期货", "long", "2026-07-09", 77617, 376, 1025.39),
("银河期货", "long", "2026-07-08", 77241, -3621, 1025.73),
("银河期货", "long", "2026-07-07", 80862, 10307, 1025.73),
("银河期货", "long", "2026-07-06", 70555, 2439, 1035.77),
("银河期货", "long", "2026-07-03", 68116, -4101, 1037.95),
("银河期货", "long", "2026-07-02", 72217, 490, 1037.95),
]
def seed(): def seed():
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)
db = SessionLocal() db = SessionLocal()
@@ -260,38 +90,8 @@ def seed():
if i >= 5: if i >= 5:
bar.amp_5d = compute_amp_5d([b.diff for b in seed_bars[i - 5 : i]]) bar.amp_5d = compute_amp_5d([b.diff for b in seed_bars[i - 5 : i]])
# --- Seed position snapshots ---
existing_pos = {
(r.contract_code, r.institution, r.direction, r.date)
for r in db.query(
PositionSnapshot.contract_code,
PositionSnapshot.institution,
PositionSnapshot.direction,
PositionSnapshot.date,
).all()
}
snaps = []
for inst, direction, date_str, pos, delta, cost in POSITION_DATA_SHORT:
d = date.fromisoformat(date_str)
if ("FG2609", inst, direction, d) not in existing_pos:
snaps.append(
PositionSnapshot(
contract_code="FG2609",
institution=inst,
direction=direction,
date=d,
position=pos,
delta=delta,
avg_cost=cost,
)
)
if snaps:
db.add_all(snaps)
db.commit() db.commit()
print(f"Seeded {len(bars_to_insert)} bars + {len(snaps)} position snapshots") print(f"Seeded {len(bars_to_insert)} bars")
finally: finally:
db.close() db.close()
-75
View File
@@ -1,75 +0,0 @@
{% extends "base.html" %}
{% block title %}博弈分析{% endblock %}
{% block heading %}博弈分析{% endblock %}
{% block breadcrumb %}机构持仓{% endblock %}
{% block content %}
{% if contracts %}
<div style="display:flex;align-items:center;gap:12px;margin-bottom:20px;">
<span style="font-size:0.85rem;color:var(--sub);">合约</span>
<select onchange="window.location='?contract='+this.value" style="padding:6px 12px;border:1px solid var(--border);border-radius:6px;background:var(--surface);color:var(--fg);font-size:0.88rem;">
{% for c in contracts %}
<option value="{{ c }}"{% if c == selected %} selected{% endif %}>{{ c }}</option>
{% endfor %}
</select>
</div>
{% endif %}
{% if latest_date %}
<div class="stat-grid">
<div class="stat-card">
<div class="label">数据日期</div>
<div class="value">{{ latest_date }}</div>
</div>
<div class="stat-card">
<div class="label">{{ selected }} 现价</div>
<div class="value">{{ current_price }}</div>
</div>
<div class="stat-card">
<div class="label">六家净空</div>
<div class="value">{{ total_net_short }}</div>
</div>
<div class="stat-card">
<div class="label">浮动盈亏</div>
<div class="value">{{ total_pnl }}</div>
</div>
</div>
<div class="section-title">机构持仓明细</div>
<div class="table-wrap">
<table>
<tr>
<th>机构</th><th>多单</th><th>空单</th><th>净持仓</th><th>净盈亏</th>
</tr>
{% for r in rows %}
<tr>
<td><strong>{{ r.institution }}</strong></td>
<td>{{ r.long_pos }}</td>
<td>{{ r.short_pos }}</td>
<td>{{ r.net_pos }}</td>
<td>
{% if r.pnl_raw > 0 %}
<span class="badge badge-up">{{ r.pnl }}</span>
{% elif r.pnl_raw < 0 %}
<span class="badge badge-down">{{ r.pnl }}</span>
{% else %}
<span class="na"></span>
{% endif %}
</td>
</tr>
{% endfor %}
<tr style="font-weight:700; background:var(--th-bg);">
<td>合计</td>
<td></td>
<td></td>
<td>{{ total_net_short }}</td>
<td>{{ total_pnl }}</td>
</tr>
</table>
</div>
{% else %}
<div style="text-align:center;padding:60px 0;color:var(--sub);">
<p style="font-size:1.1rem;">暂无「{{ selected }}」的持仓数据</p>
</div>
{% endif %}
{% endblock %}
-3
View File
@@ -195,9 +195,6 @@
<a href="/contracts/" class="{% if active_nav == 'contracts' %}active{% endif %}"> <a href="/contracts/" class="{% if active_nav == 'contracts' %}active{% endif %}">
<span class="icon">📈</span> 行情数据 <span class="icon">📈</span> 行情数据
</a> </a>
<a href="/analysis/" class="{% if active_nav == 'analysis' %}active{% endif %}">
<span class="icon">⚔️</span> 博弈分析
</a>
<a href="/admin/" class="{% if active_nav == 'admin' %}active{% endif %}"> <a href="/admin/" class="{% if active_nav == 'admin' %}active{% endif %}">
<span class="icon">⚙️</span> 系统管理 <span class="icon">⚙️</span> 系统管理
</a> </a>