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:
+32
-17
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user