feat: 计仓改为固定手数/固定金额,推荐过滤与CTP保证金,下单与持仓UI优化
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+55
-26
@@ -14,9 +14,10 @@ from fee_specs import calc_fee_breakdown
|
||||
from kline_stream import sse_format
|
||||
from market_sessions import is_trading_session
|
||||
from position_sizing import (
|
||||
MODE_AMOUNT,
|
||||
MODE_FIXED,
|
||||
MODE_RISK,
|
||||
DEFAULT_MAX_ORDER_LOTS,
|
||||
calc_lots_by_amount,
|
||||
calc_lots_by_risk,
|
||||
calc_margin_usage_pct,
|
||||
calc_order_tick_metrics,
|
||||
@@ -62,6 +63,8 @@ from trading_context import (
|
||||
TRADING_MODE_LIVE,
|
||||
TRADING_MODE_SIM,
|
||||
get_account_capital,
|
||||
get_fixed_amount,
|
||||
get_fixed_lots,
|
||||
get_max_margin_pct,
|
||||
get_risk_percent,
|
||||
get_sizing_mode,
|
||||
@@ -90,6 +93,23 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
"""注册交易相关路由。"""
|
||||
_nav = require_nav
|
||||
|
||||
def _sizing_mode_label(mode: str) -> str:
|
||||
m = normalize_sizing_mode(mode)
|
||||
if m == MODE_AMOUNT:
|
||||
return "固定金额"
|
||||
return "固定手数"
|
||||
|
||||
def _recommend_payload(conn) -> dict:
|
||||
mode = get_trading_mode(get_setting)
|
||||
return recommend_payload(
|
||||
conn,
|
||||
live_capital=_capital(conn),
|
||||
max_margin_pct=get_max_margin_pct(get_setting),
|
||||
trading_mode=mode,
|
||||
sizing_mode=get_sizing_mode(get_setting),
|
||||
fixed_lots=get_fixed_lots(get_setting),
|
||||
)
|
||||
|
||||
def _settings_dict() -> dict:
|
||||
return {
|
||||
"trading_mode": get_trading_mode(get_setting),
|
||||
@@ -847,11 +867,15 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning("positions recommend refresh failed: %s", exc)
|
||||
rec_cache = recommend_payload(conn, live_capital=capital, max_margin_pct=max_pct)
|
||||
rec_cache = _recommend_payload(conn)
|
||||
if not rec_cache.get("rows") and capital > 0:
|
||||
try:
|
||||
from product_recommend import list_product_recommendations
|
||||
from recommend_store import enrich_recommend_rows, filter_affordable_recommendations
|
||||
from recommend_store import (
|
||||
enrich_recommend_rows,
|
||||
filter_affordable_recommendations,
|
||||
filter_recommend_by_sizing,
|
||||
)
|
||||
|
||||
live_rows = filter_affordable_recommendations(
|
||||
list_product_recommendations(
|
||||
@@ -859,8 +883,13 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
)
|
||||
)
|
||||
if live_rows:
|
||||
rec_cache["rows"] = enrich_recommend_rows(
|
||||
live_rows, capital, max_margin_pct=max_pct,
|
||||
enriched = enrich_recommend_rows(
|
||||
live_rows, capital, max_margin_pct=max_pct, trading_mode=mode,
|
||||
)
|
||||
rec_cache["rows"] = filter_recommend_by_sizing(
|
||||
enriched,
|
||||
sizing_mode=sizing,
|
||||
fixed_lots=get_fixed_lots(get_setting),
|
||||
)
|
||||
rec_cache["updated_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
except Exception as exc:
|
||||
@@ -877,7 +906,9 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
monitor_count=monitor_count,
|
||||
roll_count=roll_count,
|
||||
sizing_mode=sizing,
|
||||
sizing_mode_label="以损定仓" if sizing == MODE_RISK else "固定张数",
|
||||
sizing_mode_label=_sizing_mode_label(sizing),
|
||||
fixed_lots=get_fixed_lots(get_setting),
|
||||
fixed_amount=get_fixed_amount(get_setting),
|
||||
risk_percent=get_risk_percent(get_setting),
|
||||
max_margin_pct=get_max_margin_pct(get_setting),
|
||||
recommend_rows=rec_cache.get("rows") or [],
|
||||
@@ -1262,13 +1293,15 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
conn.close()
|
||||
sizing = get_sizing_mode(get_setting)
|
||||
margin_pct = get_max_margin_pct(get_setting)
|
||||
if sizing == MODE_RISK:
|
||||
lots, err = calc_lots_by_risk(
|
||||
entry, sl, direction, capital, get_risk_percent(get_setting), sym,
|
||||
max_margin_pct=margin_pct,
|
||||
if sizing == MODE_AMOUNT:
|
||||
lots, err = calc_lots_by_amount(
|
||||
entry, sl, direction, get_fixed_amount(get_setting), sym,
|
||||
capital=capital, max_margin_pct=margin_pct,
|
||||
)
|
||||
if err:
|
||||
return jsonify({"ok": False, "error": err}), 400
|
||||
elif sizing == MODE_FIXED:
|
||||
lots = get_fixed_lots(get_setting)
|
||||
else:
|
||||
try:
|
||||
lots = max(1, int(d.get("lots") or 1))
|
||||
@@ -1322,19 +1355,21 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
return jsonify({"ok": False, "error": "CTP 连接中,请稍候再下单"}), 400
|
||||
return jsonify({"ok": False, "error": "请先连接 CTP"}), 400
|
||||
sizing = get_sizing_mode(get_setting)
|
||||
if offset.startswith("open") and sizing == MODE_RISK:
|
||||
if offset.startswith("open") and sizing == MODE_AMOUNT:
|
||||
sl = float(d.get("stop_loss") or 0)
|
||||
if sl <= 0:
|
||||
conn.close()
|
||||
return jsonify({"ok": False, "error": "以损定仓模式须填写止损价"}), 400
|
||||
lots_calc, err = calc_lots_by_risk(
|
||||
price, sl, direction, _capital(conn), get_risk_percent(get_setting), sym,
|
||||
max_margin_pct=get_max_margin_pct(get_setting),
|
||||
return jsonify({"ok": False, "error": "固定金额模式须填写止损价"}), 400
|
||||
lots_calc, err = calc_lots_by_amount(
|
||||
price, sl, direction, get_fixed_amount(get_setting), sym,
|
||||
capital=_capital(conn), max_margin_pct=get_max_margin_pct(get_setting),
|
||||
)
|
||||
if err:
|
||||
conn.close()
|
||||
return jsonify({"ok": False, "error": err}), 400
|
||||
lots = lots_calc or lots
|
||||
elif offset.startswith("open") and sizing == MODE_FIXED:
|
||||
lots = get_fixed_lots(get_setting)
|
||||
margin_pct = get_max_margin_pct(get_setting)
|
||||
usage = calc_margin_usage_pct(
|
||||
_ctp_positions(mode),
|
||||
@@ -1482,11 +1517,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
"""只读数据库缓存,不在请求时拉行情。"""
|
||||
conn = get_db()
|
||||
try:
|
||||
payload = recommend_payload(
|
||||
conn,
|
||||
live_capital=_capital(conn),
|
||||
max_margin_pct=get_max_margin_pct(get_setting),
|
||||
)
|
||||
payload = _recommend_payload(conn)
|
||||
return jsonify({"ok": True, **payload})
|
||||
finally:
|
||||
conn.close()
|
||||
@@ -1501,11 +1532,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
try:
|
||||
conn = get_db()
|
||||
try:
|
||||
payload = recommend_payload(
|
||||
conn,
|
||||
live_capital=_capital(conn),
|
||||
max_margin_pct=get_max_margin_pct(get_setting),
|
||||
)
|
||||
payload = _recommend_payload(conn)
|
||||
finally:
|
||||
conn.close()
|
||||
yield sse_format("recommend", {"ok": True, **payload})
|
||||
@@ -1542,7 +1569,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
max_margin_pct=get_max_margin_pct(get_setting),
|
||||
)
|
||||
max_pct = get_max_margin_pct(get_setting)
|
||||
payload = recommend_payload(conn, live_capital=capital, max_margin_pct=max_pct)
|
||||
payload = _recommend_payload(conn)
|
||||
recommend_hub.broadcast("recommend", {"ok": True, **payload})
|
||||
return jsonify({"ok": True, "count": len(rows), **payload})
|
||||
finally:
|
||||
@@ -1876,6 +1903,8 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
init_tables_fn=_init_tables,
|
||||
get_mode_fn=lambda: get_trading_mode(get_setting),
|
||||
get_max_margin_pct_fn=lambda: get_max_margin_pct(get_setting),
|
||||
get_sizing_mode_fn=lambda: get_sizing_mode(get_setting),
|
||||
get_fixed_lots_fn=lambda: get_fixed_lots(get_setting),
|
||||
)
|
||||
start_ctp_reconnect_worker(get_mode_fn=lambda: get_trading_mode(get_setting))
|
||||
start_ctp_premarket_connect_worker(get_mode_fn=lambda: get_trading_mode(get_setting))
|
||||
|
||||
Reference in New Issue
Block a user