Fix roll margin validation and SHFE close offset for night positions.

Use CTP account margin for roll cap checks and prefer close-today on SHFE when yesterday close is rejected.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-03 09:22:17 +08:00
parent 50bb04e2bb
commit 1cd3039605
3 changed files with 87 additions and 24 deletions
+42 -3
View File
@@ -28,6 +28,7 @@ from modules.trading.position_sizing import (
calc_lots_by_risk,
calc_margin_usage_pct,
cap_lots_for_margin_budget,
current_margin_usage_pct,
calc_order_tick_metrics,
normalize_sizing_mode,
)
@@ -1973,6 +1974,11 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
).fetchone()["n"]
if int(pending_n or 0) > 0:
return False, "已有监控中的加仓腿"
cap_err = _validate_roll_margin(
conn, mode=mode, mon=mon, preview=preview, capital=_capital(conn),
)
if cap_err:
return False, cap_err
try:
result = execute_order(
conn,
@@ -4508,8 +4514,15 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
conn = get_db()
init_strategy_tables(conn)
mode = get_trading_mode(get_setting)
capital = _capital(conn)
preview2, merr = _apply_roll_margin_cap(
preview, conn=conn, mode=mode, mon=mon, capital=capital,
)
if merr:
conn.close()
return False, merr
ok, msg = _commit_roll_fill(
conn, mon=mon, preview=preview, add_mode=leg.get("add_mode") or ADD_MODE_MARKET,
conn, mon=mon, preview=preview2, add_mode=leg.get("add_mode") or ADD_MODE_MARKET,
mode=mode, pending_leg_id=int(leg["id"]),
)
conn.close()
@@ -4536,6 +4549,20 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
app._check_roll_monitors = _check_roll_monitors
def _validate_roll_margin(
conn,
*,
mode: str,
mon: dict,
preview: dict,
capital: float,
) -> Optional[str]:
"""滚仓提交前:用柜台保证金复核是否超过滚仓上限。"""
_, merr = _apply_roll_margin_cap(
preview, conn=conn, mode=mode, mon=mon, capital=capital,
)
return merr
def _apply_roll_margin_cap(
preview: dict,
*,
@@ -4558,9 +4585,21 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
mult = int(get_contract_spec(sym).get("mult") or 1)
roll_pct = get_roll_max_margin_pct(get_setting)
add_lots = int(preview.get("add_lots") or 0)
positions = _positions_for_monitor_restore(mode, allow_ctp=False)
positions = _positions_for_monitor_restore(mode, allow_ctp=True)
acct_margin = 0.0
if ctp_status(mode).get("connected"):
acct_margin = float(ctp_account_margin_used(mode) or 0)
current_usage = current_margin_usage_pct(
positions, capital, trading_mode=mode, account_margin_used=acct_margin,
)
if current_usage > roll_pct:
return preview, (
f"当前保证金占用 {current_usage:g}% 已超过滚仓上限 {roll_pct:g}%"
"请先减仓或提高上限后再加仓"
)
capped, usage = cap_lots_for_margin_budget(
positions, capital, sym, direction, price, add_lots, roll_pct, trading_mode=mode,
positions, capital, sym, direction, price, add_lots, roll_pct,
trading_mode=mode, account_margin_used=acct_margin,
)
if capped < 1:
return preview, f"滚仓后保证金占用将超过上限 {roll_pct:g}%"