修改移动止损
This commit is contained in:
@@ -4670,14 +4670,36 @@ def check_order_monitors():
|
|||||||
direction == "long" and new_sl > float(stop_loss)
|
direction == "long" and new_sl > float(stop_loss)
|
||||||
)
|
)
|
||||||
if should_move:
|
if should_move:
|
||||||
conn.execute(
|
ex_sym = resolve_monitor_exchange_symbol(r)
|
||||||
"UPDATE order_monitors SET stop_loss=?, breakeven_armed=1, breakeven_price=? WHERE id=?",
|
new_sl = round_price_to_exchange(ex_sym, new_sl)
|
||||||
(new_sl, new_sl, pid),
|
tp_ex = float(take_profit or 0)
|
||||||
)
|
ok_live, _live_reason = ensure_exchange_live_ready()
|
||||||
stop_loss = new_sl
|
synced_ex = not ok_live
|
||||||
arm_txt = "保本止盈" if not breakeven_armed else "移动止盈"
|
if ok_live and tp_ex > 0:
|
||||||
send_wechat_msg(
|
try:
|
||||||
build_wechat_breakeven_message(
|
replace_active_monitor_tpsl_on_exchange(r, new_sl, tp_ex)
|
||||||
|
synced_ex = True
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"[breakeven] exchange tpsl replace failed order={pid} {sym}: {e}",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
send_wechat_msg(
|
||||||
|
f"⚠️ {sym} 移动保本止损未同步交易所:{friendly_exchange_error(e)}"
|
||||||
|
)
|
||||||
|
elif ok_live:
|
||||||
|
print(
|
||||||
|
f"[breakeven] skip exchange order={pid} {sym}: invalid take_profit",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
if synced_ex:
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE order_monitors SET stop_loss=?, breakeven_armed=1, breakeven_price=? WHERE id=?",
|
||||||
|
(new_sl, new_sl, pid),
|
||||||
|
)
|
||||||
|
stop_loss = new_sl
|
||||||
|
arm_txt = "保本止盈" if not breakeven_armed else "移动止盈"
|
||||||
|
be_msg = build_wechat_breakeven_message(
|
||||||
sym,
|
sym,
|
||||||
direction,
|
direction,
|
||||||
arm_txt,
|
arm_txt,
|
||||||
@@ -4685,7 +4707,9 @@ def check_order_monitors():
|
|||||||
locked_r,
|
locked_r,
|
||||||
new_sl,
|
new_sl,
|
||||||
)
|
)
|
||||||
)
|
if ok_live:
|
||||||
|
be_msg += "\n- 交易所:已先撤后挂止盈止损"
|
||||||
|
send_wechat_msg(be_msg)
|
||||||
|
|
||||||
res = None
|
res = None
|
||||||
# 做多
|
# 做多
|
||||||
|
|||||||
@@ -93,6 +93,7 @@
|
|||||||
- 左列:实盘下单监控(表单、划转、规则)。
|
- 左列:实盘下单监控(表单、划转、规则)。
|
||||||
- 右列:实时持仓(独立模块)。
|
- 右列:实时持仓(独立模块)。
|
||||||
- **人工开仓门控**:计划盈亏比 < `MANUAL_MIN_PLANNED_RR`(默认 **1.4**)时前端弹窗 + 后端拒绝。
|
- **人工开仓门控**:计划盈亏比 < `MANUAL_MIN_PLANNED_RR`(默认 **1.4**)时前端弹窗 + 后端拒绝。
|
||||||
|
- **移动保本**(勾选启用):监控轮询达到触发 RR 后,止损阶梯上移时**同步交易所**——**先撤**该合约全部 TP/SL(含 Algo 条件单)**再挂**新止损 + 原止盈(`replace_active_monitor_tpsl_on_exchange`)。仅交易所成功后才写库;失败发企业微信告警。未配置实盘 API 时仍只更新本地。
|
||||||
|
|
||||||
## 统计分析页(`/stats`)
|
## 统计分析页(`/stats`)
|
||||||
|
|
||||||
|
|||||||
+33
-10
@@ -4817,15 +4817,36 @@ def check_order_monitors():
|
|||||||
direction == "long" and new_sl > float(stop_loss)
|
direction == "long" and new_sl > float(stop_loss)
|
||||||
)
|
)
|
||||||
if should_move:
|
if should_move:
|
||||||
new_sl = round_price_to_exchange(resolve_monitor_exchange_symbol(r), new_sl)
|
ex_sym = resolve_monitor_exchange_symbol(r)
|
||||||
conn.execute(
|
new_sl = round_price_to_exchange(ex_sym, new_sl)
|
||||||
"UPDATE order_monitors SET stop_loss=?, breakeven_armed=1, breakeven_price=? WHERE id=?",
|
tp_ex = float(take_profit or 0)
|
||||||
(new_sl, new_sl, pid),
|
ok_live, _live_reason = ensure_exchange_live_ready()
|
||||||
)
|
synced_ex = not ok_live
|
||||||
stop_loss = new_sl
|
if ok_live and tp_ex > 0:
|
||||||
arm_txt = "保本止盈" if not breakeven_armed else "移动止盈"
|
try:
|
||||||
send_wechat_msg(
|
replace_active_monitor_tpsl_on_exchange(r, new_sl, tp_ex)
|
||||||
build_wechat_breakeven_message(
|
synced_ex = True
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"[breakeven] exchange tpsl replace failed order={pid} {sym}: {e}",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
send_wechat_msg(
|
||||||
|
f"⚠️ {sym} 移动保本止损未同步交易所:{friendly_exchange_error(e)}"
|
||||||
|
)
|
||||||
|
elif ok_live:
|
||||||
|
print(
|
||||||
|
f"[breakeven] skip exchange order={pid} {sym}: invalid take_profit",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
if synced_ex:
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE order_monitors SET stop_loss=?, breakeven_armed=1, breakeven_price=? WHERE id=?",
|
||||||
|
(new_sl, new_sl, pid),
|
||||||
|
)
|
||||||
|
stop_loss = new_sl
|
||||||
|
arm_txt = "保本止盈" if not breakeven_armed else "移动止盈"
|
||||||
|
be_msg = build_wechat_breakeven_message(
|
||||||
sym,
|
sym,
|
||||||
direction,
|
direction,
|
||||||
arm_txt,
|
arm_txt,
|
||||||
@@ -4833,7 +4854,9 @@ def check_order_monitors():
|
|||||||
locked_r,
|
locked_r,
|
||||||
new_sl,
|
new_sl,
|
||||||
)
|
)
|
||||||
)
|
if ok_live:
|
||||||
|
be_msg += "\n- 交易所:已先撤后挂止盈止损"
|
||||||
|
send_wechat_msg(be_msg)
|
||||||
|
|
||||||
res = None
|
res = None
|
||||||
# 做多
|
# 做多
|
||||||
|
|||||||
@@ -93,6 +93,7 @@
|
|||||||
- 左列:实盘下单监控(表单、划转、规则)。
|
- 左列:实盘下单监控(表单、划转、规则)。
|
||||||
- 右列:实时持仓(独立模块)。
|
- 右列:实时持仓(独立模块)。
|
||||||
- **人工开仓门控**:计划盈亏比 < `MANUAL_MIN_PLANNED_RR`(默认 **1.4**)时前端弹窗 + 后端拒绝。
|
- **人工开仓门控**:计划盈亏比 < `MANUAL_MIN_PLANNED_RR`(默认 **1.4**)时前端弹窗 + 后端拒绝。
|
||||||
|
- **移动保本**(勾选启用):监控轮询达到触发 RR 后,止损阶梯上移时**同步交易所**——调用与页面「挂止盈止损」相同的 **先撤后挂**(`replace_active_monitor_tpsl_on_exchange`:撤该合约全部 TP/SL 条件单 → 按新止损 + 原止盈重挂)。仅交易所成功后才写库;失败发企业微信告警,本地止损不变。未配置实盘 API 时仍只更新本地(与旧行为一致)。
|
||||||
|
|
||||||
## 统计分析页(`/stats`)
|
## 统计分析页(`/stats`)
|
||||||
|
|
||||||
|
|||||||
@@ -1656,6 +1656,24 @@ def resolve_monitor_exchange_symbol(row):
|
|||||||
return normalize_exchange_symbol(raw) if raw else ""
|
return normalize_exchange_symbol(raw) if raw else ""
|
||||||
|
|
||||||
|
|
||||||
|
def round_price_to_exchange(exchange_symbol, price):
|
||||||
|
"""与交易所 tick 对齐后的 float,供入库与计算;失败时退回 float(price)。"""
|
||||||
|
if price in (None, ""):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
v = float(price)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None
|
||||||
|
if not exchange_symbol:
|
||||||
|
return v
|
||||||
|
try:
|
||||||
|
ensure_markets_loaded()
|
||||||
|
s = exchange.price_to_precision(exchange_symbol, v)
|
||||||
|
return float(s)
|
||||||
|
except Exception:
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
def _position_contract_symbol_match(position_symbol, wanted_exchange_symbol):
|
def _position_contract_symbol_match(position_symbol, wanted_exchange_symbol):
|
||||||
if not position_symbol or not wanted_exchange_symbol:
|
if not position_symbol or not wanted_exchange_symbol:
|
||||||
return False
|
return False
|
||||||
@@ -3093,6 +3111,28 @@ def cancel_all_open_orders_for_symbol(exchange_symbol):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def replace_active_monitor_tpsl_on_exchange(order_row, stop_loss, take_profit):
|
||||||
|
"""移动保本/手动改价:先撤该合约 TP/SL 条件单,再按新价重挂。"""
|
||||||
|
ok, reason = ensure_exchange_live_ready()
|
||||||
|
if not ok:
|
||||||
|
raise RuntimeError(reason or "实盘未就绪")
|
||||||
|
ex_sym = resolve_monitor_exchange_symbol(order_row)
|
||||||
|
direction = order_row["direction"]
|
||||||
|
cancel_gate_swap_trigger_orders(ex_sym)
|
||||||
|
contracts = get_live_position_contracts(ex_sym, direction)
|
||||||
|
if contracts is None or float(contracts) <= 0:
|
||||||
|
raise ValueError("交易所当前无该方向持仓,无法挂止盈止损")
|
||||||
|
amt = float(contracts)
|
||||||
|
if amt <= 0:
|
||||||
|
try:
|
||||||
|
amt = float(order_row["order_amount"] or 0)
|
||||||
|
except Exception:
|
||||||
|
amt = 0
|
||||||
|
if amt <= 0:
|
||||||
|
raise ValueError("无法确定平仓数量")
|
||||||
|
_gate_place_tp_sl_orders(ex_sym, direction, amt, float(stop_loss), float(take_profit))
|
||||||
|
|
||||||
|
|
||||||
def extract_trade_price_from_order(order):
|
def extract_trade_price_from_order(order):
|
||||||
if not order:
|
if not order:
|
||||||
return None
|
return None
|
||||||
@@ -4491,14 +4531,36 @@ def check_order_monitors():
|
|||||||
direction == "long" and new_sl > float(stop_loss)
|
direction == "long" and new_sl > float(stop_loss)
|
||||||
)
|
)
|
||||||
if should_move:
|
if should_move:
|
||||||
conn.execute(
|
ex_sym = resolve_monitor_exchange_symbol(r)
|
||||||
"UPDATE order_monitors SET stop_loss=?, breakeven_armed=1, breakeven_price=? WHERE id=?",
|
new_sl = round_price_to_exchange(ex_sym, new_sl)
|
||||||
(new_sl, new_sl, pid),
|
tp_ex = float(take_profit or 0)
|
||||||
)
|
ok_live, _live_reason = ensure_exchange_live_ready()
|
||||||
stop_loss = new_sl
|
synced_ex = not ok_live
|
||||||
arm_txt = "保本止盈" if not breakeven_armed else "移动止盈"
|
if ok_live and tp_ex > 0:
|
||||||
send_wechat_msg(
|
try:
|
||||||
build_wechat_breakeven_message(
|
replace_active_monitor_tpsl_on_exchange(r, new_sl, tp_ex)
|
||||||
|
synced_ex = True
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"[breakeven] exchange tpsl replace failed order={pid} {sym}: {e}",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
send_wechat_msg(
|
||||||
|
f"⚠️ {sym} 移动保本止损未同步交易所:{friendly_exchange_error(e)}"
|
||||||
|
)
|
||||||
|
elif ok_live:
|
||||||
|
print(
|
||||||
|
f"[breakeven] skip exchange order={pid} {sym}: invalid take_profit",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
if synced_ex:
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE order_monitors SET stop_loss=?, breakeven_armed=1, breakeven_price=? WHERE id=?",
|
||||||
|
(new_sl, new_sl, pid),
|
||||||
|
)
|
||||||
|
stop_loss = new_sl
|
||||||
|
arm_txt = "保本止盈" if not breakeven_armed else "移动止盈"
|
||||||
|
be_msg = build_wechat_breakeven_message(
|
||||||
sym,
|
sym,
|
||||||
direction,
|
direction,
|
||||||
arm_txt,
|
arm_txt,
|
||||||
@@ -4506,7 +4568,9 @@ def check_order_monitors():
|
|||||||
locked_r,
|
locked_r,
|
||||||
new_sl,
|
new_sl,
|
||||||
)
|
)
|
||||||
)
|
if ok_live:
|
||||||
|
be_msg += "\n- 交易所:已先撤后挂止盈止损"
|
||||||
|
send_wechat_msg(be_msg)
|
||||||
|
|
||||||
res = None
|
res = None
|
||||||
# 做多
|
# 做多
|
||||||
|
|||||||
Reference in New Issue
Block a user