Align margin display with CTP counter rates and position margin.

Read margin ratios from CTP instrument query and margin-rate API instead of vnpy ContractData (which lacks ratios). Keep occupied margin on position UseMargin; use per-lot max rate for recommend table.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-29 10:21:44 +08:00
parent 71c480a587
commit 19676943d0
5 changed files with 332 additions and 50 deletions
+32 -17
View File
@@ -287,27 +287,42 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
mode: str,
capital: float,
) -> list[dict]:
"""CTP 已连接时,用「权益−可用」校正占用保证金与仓位占比"""
"""仅在持仓缺少柜台保证金时补全;已有 CTP 持仓保证金的行不覆盖"""
if not ctp_status(mode).get("connected"):
return rows
total_used = ctp_account_margin_used(mode)
if not total_used:
return rows
active = [
r for r in rows
if r.get("order_state") != "pending" and int(r.get("lots") or 0) > 0
]
if not active:
return rows
if len(active) == 1:
row = active[0]
row["margin"] = total_used
row["margin_source"] = "ctp"
if capital > 0:
row["position_pct"] = round(total_used / capital * 100, 2)
return rows
weights: list[float] = []
def _has_ctp_margin(row: dict) -> bool:
return (
float(row.get("margin") or 0) > 0
and row.get("margin_source") == "ctp"
)
without_margin = [r for r in active if not _has_ctp_margin(r)]
for row in active:
if _has_ctp_margin(row) and capital > 0:
m = float(row.get("margin") or 0)
row["position_pct"] = round(m / capital * 100, 2)
if not without_margin:
return rows
total_used = ctp_account_margin_used(mode)
if not total_used:
return rows
known_sum = sum(
float(r.get("margin") or 0) for r in active if _has_ctp_margin(r)
)
pool = max(0.0, float(total_used) - known_sum) if known_sum > 0 else float(total_used)
if pool <= 0:
return rows
weights: list[float] = []
for row in without_margin:
sym = (row.get("symbol_code") or "").strip()
lots = int(row.get("lots") or 0)
entry = float(row.get("entry_price") or 0)
@@ -318,13 +333,13 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
weights.append(0.0)
total_weight = sum(weights)
assigned = 0.0
for i, row in enumerate(active):
for i, row in enumerate(without_margin):
if total_weight <= 0:
margin = round(total_used / len(active), 2)
elif i == len(active) - 1:
margin = round(total_used - assigned, 2)
margin = round(pool / len(without_margin), 2)
elif i == len(without_margin) - 1:
margin = round(pool - assigned, 2)
else:
margin = round(total_used * weights[i] / total_weight, 2)
margin = round(pool * weights[i] / total_weight, 2)
assigned += margin
row["margin"] = margin
row["margin_source"] = "ctp"