本地监控止盈止损、盘前自动连CTP,并完善保证金与推荐手数。

- 止盈止损改为程序本地监控,触发后市价平仓(含跳空)
- 交易前30分钟后台自动连接 CTP
- 保证金占用上限默认30%,可在系统设置修改
- K线标准蜡烛图红跌绿涨,费率表全宽固定表头
- 品种推荐按保证金比例×总资金计算推荐手数

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-25 12:18:18 +08:00
parent fe1b651900
commit 9875ee6d44
15 changed files with 467 additions and 256 deletions
+16 -8
View File
@@ -1,6 +1,7 @@
"""按账户资金推荐可交易品种(期货核心筛选)。"""
from __future__ import annotations
import math
from concurrent.futures import ThreadPoolExecutor
from typing import Callable, Optional
@@ -20,7 +21,7 @@ def assess_product_for_capital(
capital: float,
price: Optional[float],
*,
max_position_pct: float = 50.0,
max_margin_pct: float = 30.0,
default_stop_ticks: int = 20,
reward_risk_ratio: float = 2.0,
trading_mode: str = "simulation",
@@ -35,6 +36,7 @@ def assess_product_for_capital(
tick = float(spec.get("tick_size") or 1.0)
p = float(price) if price and price > 0 else 0.0
cap = float(capital or 0)
margin_pct = max(1.0, min(100.0, float(max_margin_pct or 30.0)))
if p <= 0:
return {
@@ -45,11 +47,14 @@ def assess_product_for_capital(
"status_label": "暂无行情",
"min_capital_one_lot": None,
"margin_one_lot": None,
"recommended_lots": 0,
"risk_one_lot_1pct": None,
}
margin_one = p * mult * margin_rate
min_capital = margin_one / (max_position_pct / 100.0) if max_position_pct > 0 else margin_one
min_capital = margin_one / (margin_pct / 100.0) if margin_pct > 0 else margin_one
margin_budget = cap * margin_pct / 100.0 if cap > 0 else 0.0
recommended_lots = int(math.floor(margin_budget / margin_one)) if margin_one > 0 and margin_budget > 0 else 0
stop_dist = tick * default_stop_ticks
risk_one_lot = stop_dist * mult
risk_pct_1lot = (risk_one_lot / cap * 100) if cap > 0 else 999.0
@@ -60,13 +65,13 @@ def assess_product_for_capital(
fee_ths, p, p, 1.0, open_time="", close_time="", trading_mode=trading_mode,
)
can_margin = cap >= min_capital
can_margin = recommended_lots >= 1
can_risk = cap > 0 and risk_one_lot <= cap * 0.01
if can_margin and can_risk:
status, label = "ok", "推荐"
status, label = "ok", f"推荐 {recommended_lots}"
elif can_margin:
status, label = "margin_ok", "可开1手·止损偏宽"
status, label = "margin_ok", f"可开 {recommended_lots} 手·止损偏宽"
else:
status, label = "blocked", "资金不足"
@@ -79,6 +84,9 @@ def assess_product_for_capital(
"tick_size": tick,
"margin_one_lot": round(margin_one, 2),
"min_capital_one_lot": round(min_capital, 2),
"recommended_lots": recommended_lots,
"margin_budget": round(margin_budget, 2),
"max_margin_pct": margin_pct,
"risk_one_lot_1pct": round(risk_one_lot, 2),
"risk_pct_1lot_at_1pct_rule": round(risk_pct_1lot, 2),
"ref_stop_loss": ref_sl,
@@ -94,7 +102,7 @@ def list_product_recommendations(
capital: float,
quote_fn: Callable[[str], Optional[dict]],
*,
max_position_pct: float = 50.0,
max_margin_pct: float = 30.0,
trading_mode: str = "simulation",
) -> list[dict]:
"""扫描全部品种并排序:推荐 > 可开 > 不足。quote_fn(品种代码) -> {price, ths_code, ...}"""
@@ -105,7 +113,7 @@ def list_product_recommendations(
price = quote.get("price")
row = assess_product_for_capital(
product, capital, price,
max_position_pct=max_position_pct,
max_margin_pct=max_margin_pct,
trading_mode=trading_mode,
)
main_code = (quote.get("ths_code") or "").strip()
@@ -115,5 +123,5 @@ def list_product_recommendations(
with ThreadPoolExecutor(max_workers=10) as pool:
rows = list(pool.map(_one, PRODUCTS))
order = {"ok": 0, "margin_ok": 1, "blocked": 2, "no_price": 3}
rows.sort(key=lambda r: (order.get(r["status"], 9), r.get("min_capital_one_lot") or 1e18))
rows.sort(key=lambda r: (order.get(r["status"], 9), -(r.get("recommended_lots") or 0)))
return rows