本地监控止盈止损、盘前自动连CTP,并完善保证金与推荐手数。
- 止盈止损改为程序本地监控,触发后市价平仓(含跳空) - 交易前30分钟后台自动连接 CTP - 保证金占用上限默认30%,可在系统设置修改 - K线标准蜡烛图红跌绿涨,费率表全宽固定表头 - 品种推荐按保证金比例×总资金计算推荐手数 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+45
-25
@@ -16,20 +16,22 @@ from position_sizing import (
|
||||
MODE_RISK,
|
||||
DEFAULT_MAX_ORDER_LOTS,
|
||||
calc_lots_by_risk,
|
||||
calc_margin_usage_pct,
|
||||
calc_order_tick_metrics,
|
||||
normalize_sizing_mode,
|
||||
)
|
||||
from recommend_store import load_recommend_cache, recommend_payload, refresh_recommend_cache
|
||||
from recommend_stream import recommend_hub, start_recommend_worker
|
||||
from ctp_reconnect import start_ctp_reconnect_worker
|
||||
from ctp_premarket_connect import start_ctp_premarket_connect_worker
|
||||
from ctp_fee_worker import start_ctp_fee_worker
|
||||
from sl_tp_guard import (
|
||||
cancel_monitor_exit_orders,
|
||||
ensure_monitor_order_columns,
|
||||
monitor_order_status,
|
||||
place_monitor_exit_orders,
|
||||
reconcile_monitors_without_position,
|
||||
start_sl_tp_guard_worker,
|
||||
sync_all_sl_tp_orders,
|
||||
)
|
||||
from risk.account_risk_lib import (
|
||||
assert_can_open,
|
||||
@@ -50,6 +52,7 @@ from trading_context import (
|
||||
TRADING_MODE_LIVE,
|
||||
TRADING_MODE_SIM,
|
||||
get_account_capital,
|
||||
get_max_margin_pct,
|
||||
get_risk_percent,
|
||||
get_sizing_mode,
|
||||
get_trading_mode,
|
||||
@@ -79,6 +82,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
"trading_mode": get_trading_mode(get_setting),
|
||||
"position_sizing_mode": get_sizing_mode(get_setting),
|
||||
"risk_percent": str(get_risk_percent(get_setting)),
|
||||
"max_margin_pct": str(get_max_margin_pct(get_setting)),
|
||||
}
|
||||
|
||||
def _capital(conn) -> float:
|
||||
@@ -194,14 +198,14 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
pending.append({
|
||||
**base,
|
||||
"order_kind": "stop_loss",
|
||||
"label": "止损挂单",
|
||||
"label": "止损监控",
|
||||
"price": float(sl),
|
||||
})
|
||||
if tp is not None:
|
||||
pending.append({
|
||||
**base,
|
||||
"order_kind": "take_profit",
|
||||
"label": "止盈挂单",
|
||||
"label": "止盈监控",
|
||||
"price": float(tp),
|
||||
})
|
||||
ctp_st = ctp_status(mode)
|
||||
@@ -311,16 +315,11 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
order_st = monitor_order_status(
|
||||
mon or {}, mode=mode, ths_code=sym, direction=direction,
|
||||
)
|
||||
can_place = bool(
|
||||
mon
|
||||
and (mon.get("stop_loss") is not None or mon.get("take_profit") is not None)
|
||||
and (order_st.get("needs_sl_order") or order_st.get("needs_tp_order"))
|
||||
)
|
||||
pending_for_row: list[dict] = []
|
||||
if sl is not None:
|
||||
pending_for_row.append({
|
||||
"order_kind": "stop_loss",
|
||||
"label": "止损挂单",
|
||||
"label": "止损监控",
|
||||
"price": sl,
|
||||
"lots": lots,
|
||||
"source": "monitor",
|
||||
@@ -329,7 +328,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
if tp is not None:
|
||||
pending_for_row.append({
|
||||
"order_kind": "take_profit",
|
||||
"label": "止盈挂单",
|
||||
"label": "止盈监控",
|
||||
"price": tp,
|
||||
"lots": lots,
|
||||
"source": "monitor",
|
||||
@@ -360,9 +359,11 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
"est_fee_close": fee_info["close_fee"],
|
||||
"est_fee_close_type": fee_info["close_type"],
|
||||
"est_pnl_net": est_net,
|
||||
"sl_order_active": order_st.get("sl_order_active"),
|
||||
"tp_order_active": order_st.get("tp_order_active"),
|
||||
"can_place_orders": can_place,
|
||||
"sl_order_active": order_st.get("sl_monitoring"),
|
||||
"tp_order_active": order_st.get("tp_monitoring"),
|
||||
"sl_monitoring": order_st.get("sl_monitoring"),
|
||||
"tp_monitoring": order_st.get("tp_monitoring"),
|
||||
"can_place_orders": False,
|
||||
"tick_value_total": tick.get("tick_value_total"),
|
||||
"price_precision": tick.get("price_precision"),
|
||||
"tick_size": tick.get("tick_size"),
|
||||
@@ -414,6 +415,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
sizing_mode=sizing,
|
||||
sizing_mode_label="以损定仓" if sizing == MODE_RISK else "固定张数",
|
||||
risk_percent=get_risk_percent(get_setting),
|
||||
max_margin_pct=get_max_margin_pct(get_setting),
|
||||
recommend_rows=rec_cache.get("rows") or [],
|
||||
recommend_updated_at=rec_cache.get("updated_at"),
|
||||
)
|
||||
@@ -531,20 +533,14 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
mon_row = conn.execute(
|
||||
"SELECT * FROM trade_order_monitors WHERE id=?", (mid,),
|
||||
).fetchone()
|
||||
if mon_row and (sl is not None or tp is not None):
|
||||
try:
|
||||
ensure_monitor_order_columns(conn)
|
||||
place_monitor_exit_orders(conn, dict(mon_row), mode=mode, force=False)
|
||||
except Exception as exc:
|
||||
logger.warning("补充止盈止损后自动委托失败: %s", exc)
|
||||
return jsonify({"ok": True, "monitor_id": mid, "message": "止盈止损已保存"})
|
||||
return jsonify({"ok": True, "monitor_id": mid, "message": "止盈止损已保存,程序本地监控"})
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@app.route("/api/trading/monitor/place-orders", methods=["POST"])
|
||||
@login_required
|
||||
def api_trading_monitor_place_orders():
|
||||
"""按开仓快照向 CTP 挂止盈止损平仓委托。"""
|
||||
"""本地监控模式:清理旧版柜台挂单,不再向交易所挂止盈止损。"""
|
||||
d = request.get_json(silent=True) or {}
|
||||
try:
|
||||
monitor_id = int(d.get("monitor_id") or 0)
|
||||
@@ -757,8 +753,12 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
capital = _capital(conn)
|
||||
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)
|
||||
lots, err = calc_lots_by_risk(
|
||||
entry, sl, direction, capital, get_risk_percent(get_setting), sym,
|
||||
max_margin_pct=margin_pct,
|
||||
)
|
||||
if err:
|
||||
return jsonify({"ok": False, "error": err}), 400
|
||||
else:
|
||||
@@ -815,11 +815,26 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
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),
|
||||
)
|
||||
if err:
|
||||
conn.close()
|
||||
return jsonify({"ok": False, "error": err}), 400
|
||||
lots = lots_calc or lots
|
||||
margin_pct = get_max_margin_pct(get_setting)
|
||||
usage = calc_margin_usage_pct(
|
||||
_ctp_positions(mode),
|
||||
_capital(conn),
|
||||
extra_symbol=sym if offset.startswith("open") else "",
|
||||
extra_lots=lots if offset.startswith("open") else 0,
|
||||
extra_price=price if offset.startswith("open") else 0,
|
||||
)
|
||||
if offset.startswith("open") and usage > margin_pct:
|
||||
conn.close()
|
||||
return jsonify({
|
||||
"ok": False,
|
||||
"error": f"保证金占用 {usage:.1f}% 超过上限 {margin_pct:g}%(可在系统设置修改)",
|
||||
}), 403
|
||||
if lots > DEFAULT_MAX_ORDER_LOTS:
|
||||
conn.close()
|
||||
return jsonify({
|
||||
@@ -881,9 +896,9 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
if mon_row and (sl or tp):
|
||||
try:
|
||||
ensure_monitor_order_columns(conn)
|
||||
place_monitor_exit_orders(conn, dict(mon_row), mode=mode, force=False)
|
||||
cancel_monitor_exit_orders(conn, dict(mon_row), mode=mode)
|
||||
except Exception as exc:
|
||||
logger.warning("开仓后自动挂止盈止损失败: %s", exc)
|
||||
logger.warning("清理旧版止盈止损挂单失败: %s", exc)
|
||||
conn.commit()
|
||||
send_wechat_msg(f"{trading_mode_label(get_setting)} {offset} {sym} {direction} {lots}手 @{price}")
|
||||
conn.close()
|
||||
@@ -1011,7 +1026,10 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
init_strategy_tables(conn)
|
||||
capital = _capital(conn)
|
||||
mode = get_trading_mode(get_setting)
|
||||
rows = refresh_recommend_cache(conn, capital, _main_quote, trading_mode=mode)
|
||||
rows = refresh_recommend_cache(
|
||||
conn, capital, _main_quote, trading_mode=mode,
|
||||
max_margin_pct=get_max_margin_pct(get_setting),
|
||||
)
|
||||
payload = recommend_payload(conn, live_capital=capital)
|
||||
recommend_hub.broadcast("recommend", {"ok": True, **payload})
|
||||
return jsonify({"ok": True, "count": len(rows), **payload})
|
||||
@@ -1345,8 +1363,10 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
quote_fn=_main_quote,
|
||||
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),
|
||||
)
|
||||
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))
|
||||
start_sl_tp_guard_worker(
|
||||
db_path=DB_PATH,
|
||||
get_mode_fn=lambda: get_trading_mode(get_setting),
|
||||
|
||||
Reference in New Issue
Block a user