982497d65a
Co-authored-by: Cursor <cursoragent@cursor.com>
174 lines
5.5 KiB
Python
174 lines
5.5 KiB
Python
"""对冲计划 SQLite 表."""
|
|
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
from typing import Any, Optional
|
|
|
|
|
|
def init_hedge_plan_tables(conn: sqlite3.Connection) -> None:
|
|
conn.execute(
|
|
"""
|
|
CREATE TABLE IF NOT EXISTS hedge_plans (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
plan_type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
underlying TEXT NOT NULL,
|
|
direction TEXT,
|
|
entry_mark REAL,
|
|
tp REAL,
|
|
sl REAL,
|
|
target_price REAL,
|
|
sizing_mode_at_open TEXT,
|
|
perp_size REAL,
|
|
margin REAL,
|
|
leverage REAL,
|
|
premium_total REAL,
|
|
realized_pnl_perp REAL,
|
|
realized_pnl_options REAL,
|
|
realized_pnl_total REAL,
|
|
stats_bucket TEXT,
|
|
close_reason TEXT,
|
|
wechat_start_sent INTEGER DEFAULT 0,
|
|
wechat_end_sent INTEGER DEFAULT 0,
|
|
note TEXT,
|
|
preview_json TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
opened_at TIMESTAMP,
|
|
closed_at TIMESTAMP
|
|
)
|
|
"""
|
|
)
|
|
conn.execute(
|
|
"""
|
|
CREATE TABLE IF NOT EXISTS hedge_plan_legs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
plan_id INTEGER NOT NULL,
|
|
leg_role TEXT NOT NULL,
|
|
symbol TEXT,
|
|
inst_id TEXT,
|
|
opt_type TEXT,
|
|
strike REAL,
|
|
side TEXT,
|
|
size REAL,
|
|
avg_open REAL,
|
|
premium REAL,
|
|
status TEXT,
|
|
linked_monitor_id INTEGER,
|
|
options_trade_id INTEGER,
|
|
exchange_ord_id TEXT,
|
|
realized_pnl REAL,
|
|
close_reason TEXT,
|
|
opened_at TIMESTAMP,
|
|
closed_at TIMESTAMP,
|
|
FOREIGN KEY(plan_id) REFERENCES hedge_plans(id)
|
|
)
|
|
"""
|
|
)
|
|
conn.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_hedge_plans_status ON hedge_plans(status)"
|
|
)
|
|
conn.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_hedge_plan_legs_plan ON hedge_plan_legs(plan_id)"
|
|
)
|
|
|
|
|
|
def count_active_plans(conn: sqlite3.Connection, plan_type: Optional[str] = None) -> int:
|
|
if plan_type:
|
|
row = conn.execute(
|
|
"SELECT COUNT(1) AS c FROM hedge_plans WHERE status IN ('opening','active','partial') AND plan_type=?",
|
|
(plan_type,),
|
|
).fetchone()
|
|
else:
|
|
row = conn.execute(
|
|
"SELECT COUNT(1) AS c FROM hedge_plans WHERE status IN ('opening','active','partial')"
|
|
).fetchone()
|
|
return int((row["c"] if row else 0) or 0)
|
|
|
|
|
|
def insert_plan(conn: sqlite3.Connection, row: dict[str, Any]) -> int:
|
|
cols = list(row.keys())
|
|
placeholders = ",".join(["?"] * len(cols))
|
|
conn.execute(
|
|
f"INSERT INTO hedge_plans ({','.join(cols)}) VALUES ({placeholders})",
|
|
[row[c] for c in cols],
|
|
)
|
|
return int(conn.execute("SELECT last_insert_rowid()").fetchone()[0])
|
|
|
|
|
|
def insert_leg(conn: sqlite3.Connection, row: dict[str, Any]) -> int:
|
|
cols = list(row.keys())
|
|
placeholders = ",".join(["?"] * len(cols))
|
|
conn.execute(
|
|
f"INSERT INTO hedge_plan_legs ({','.join(cols)}) VALUES ({placeholders})",
|
|
[row[c] for c in cols],
|
|
)
|
|
return int(conn.execute("SELECT last_insert_rowid()").fetchone()[0])
|
|
|
|
|
|
def update_plan(conn: sqlite3.Connection, plan_id: int, **fields: Any) -> None:
|
|
if not fields:
|
|
return
|
|
sets = ", ".join(f"{k}=?" for k in fields)
|
|
conn.execute(f"UPDATE hedge_plans SET {sets} WHERE id=?", [*fields.values(), plan_id])
|
|
|
|
|
|
def list_plans(
|
|
conn: sqlite3.Connection,
|
|
*,
|
|
status: Optional[str] = None,
|
|
plan_type: Optional[str] = None,
|
|
underlying: Optional[str] = None,
|
|
limit: int = 50,
|
|
) -> list[dict[str, Any]]:
|
|
wheres: list[str] = []
|
|
args: list[Any] = []
|
|
if status:
|
|
wheres.append("status=?")
|
|
args.append(status)
|
|
if plan_type:
|
|
wheres.append("plan_type=?")
|
|
args.append(plan_type)
|
|
if underlying:
|
|
wheres.append("underlying=?")
|
|
args.append(underlying)
|
|
where = (" WHERE " + " AND ".join(wheres)) if wheres else ""
|
|
rows = conn.execute(
|
|
f"SELECT * FROM hedge_plans{where} ORDER BY id DESC LIMIT ?",
|
|
[*args, int(limit)],
|
|
).fetchall()
|
|
return [dict(r) for r in rows]
|
|
|
|
|
|
def get_plan(conn: sqlite3.Connection, plan_id: int) -> Optional[dict[str, Any]]:
|
|
row = conn.execute("SELECT * FROM hedge_plans WHERE id=?", (plan_id,)).fetchone()
|
|
return dict(row) if row else None
|
|
|
|
|
|
def get_plan_legs(conn: sqlite3.Connection, plan_id: int) -> list[dict[str, Any]]:
|
|
rows = conn.execute(
|
|
"SELECT * FROM hedge_plan_legs WHERE plan_id=? ORDER BY id", (plan_id,)
|
|
).fetchall()
|
|
return [dict(r) for r in rows]
|
|
|
|
|
|
def stats_summary(conn: sqlite3.Connection) -> dict[str, Any]:
|
|
rows = conn.execute(
|
|
"""
|
|
SELECT plan_type, close_reason, COUNT(1) AS n,
|
|
COALESCE(SUM(realized_pnl_total), 0) AS pnl
|
|
FROM hedge_plans
|
|
WHERE status='closed'
|
|
GROUP BY plan_type, close_reason
|
|
"""
|
|
).fetchall()
|
|
closed = conn.execute(
|
|
"SELECT COUNT(1) AS c, COALESCE(SUM(realized_pnl_total),0) AS pnl FROM hedge_plans WHERE status='closed'"
|
|
).fetchone()
|
|
active = count_active_plans(conn)
|
|
return {
|
|
"active": active,
|
|
"closed_count": int((closed["c"] if closed else 0) or 0),
|
|
"closed_pnl_total": float((closed["pnl"] if closed else 0) or 0),
|
|
"by_reason": [dict(r) for r in rows],
|
|
}
|