fix: 突破加仓按 mark 触价触发,避免漏单

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-02 22:31:49 +08:00
parent 0b8f410fbe
commit 394793b9d2
3 changed files with 16 additions and 9 deletions
+7 -8
View File
@@ -176,21 +176,20 @@ def roll_breakout_trigger_crossed(
mark: float,
breakthrough_price: float,
) -> bool:
"""突破:多=向上穿越突破价;空=向下穿越突破价。"""
"""突破:多=mark 在突破价之上;空=mark 在突破价之下
提交时已校验 mark 在逆势侧(多低于突破价、空高于突破价),触价侧到达即成交。
不再要求单 tick 内穿越,避免 mark 已破位但 last_mark 也落在突破价另一侧时永久漏触发。
"""
try:
m = float(mark)
bp = float(breakthrough_price)
pm = float(prev_mark) if prev_mark is not None else None
except (TypeError, ValueError):
return False
direction = (direction or "long").strip().lower()
if direction == "long":
if pm is None:
return m > bp
return pm <= bp and m > bp
if pm is None:
return m < bp
return pm >= bp and m < bp
return m > bp
return m < bp
def roll_fib_invalidate(direction: str, mark: float, upper: float, lower: float) -> bool:
@@ -6,7 +6,7 @@
<strong>仅人工提交</strong>;须先在「实盘下单」有同向持仓。仅<strong>以损定仓</strong>模式可用。<br>
做多/做空各最多滚仓 <strong>3</strong> 次(仅计已成交腿);止盈<strong>锁定首仓</strong>不变。<br>
风险比例读取所选监控单,<strong>不可手改</strong>;打到新止损时合并持仓亏损 ≈ 1 个风险单位(当前基数 × 监控 risk%)。<br>
斐波/突破为<strong>程序监控</strong>mark 价穿越触发),触价后市价加仓;填写后直接点「执行滚仓」(无需预览)。同时仅允许 <strong>1</strong> 条监控中腿,提交后<strong>不可修改</strong>,可删除。<br>
斐波/突破为<strong>程序监控</strong>交易所 mark 价),触价后市价加仓;填写后直接点「执行滚仓」(无需预览)。同时仅允许 <strong>1</strong> 条监控中腿,提交后<strong>不可修改</strong>,可删除。<br>
手动平仓后滚仓监控自动结束;<strong>已成交腿历史保留</strong>供复盘。<br>
<a href="/strategy/roll/docs" target="_blank" rel="noopener" class="roll-doc-link">→ 顺势加仓完整逻辑说明</a><br>
{% if roll_trend_active %}<span style="color:#ff8f8f">当前有运行中的趋势回调计划,请先结束后再滚仓。</span>{% endif %}
+8
View File
@@ -49,10 +49,18 @@ def test_fib_cross_long_down():
def test_breakout_cross_long_up():
assert roll_breakout_trigger_crossed("long", 99.0, 100.5, 100.0) is True
assert roll_breakout_trigger_crossed("long", 99.0, 100.0, 100.0) is False
assert roll_breakout_invalidate("long", 98.0, 99.0) is True
assert roll_fib_invalidate("long", 110.0, 105.0, 95.0) is True
def test_breakout_short_below_breakthrough():
assert roll_breakout_trigger_crossed("short", 81.0, 80.57, 80.65) is True
assert roll_breakout_trigger_crossed("short", 80.64, 80.57, 80.65) is True
assert roll_breakout_trigger_crossed("short", 80.57, 80.57, 80.65) is True
assert roll_breakout_trigger_crossed("short", 81.0, 80.70, 80.65) is False
def test_preview_breakout_mode_label():
preview, err = preview_roll(
direction="long",