顺势加仓 v2:程序监控滚仓、文档页与平仓同步

重写滚仓计仓与四种加仓方式(市价/斐波/突破),程序盯 mark 触价成交;风险读监控单;pending 可删不可改;手动平仓同步结束滚仓。新增 /strategy/roll/docs 说明页与顺势加仓滚仓说明.md。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-26 22:03:23 +08:00
parent 4aebe70611
commit d467760d5c
17 changed files with 1506 additions and 630 deletions
+46 -35
View File
@@ -1,29 +1,24 @@
from strategy_roll_lib import (
preview_roll,
resolve_roll_stop_spec,
roll_stop_after_fill,
unified_stop_from_avg,
roll_breakout_invalidate,
roll_breakout_trigger_crossed,
roll_fib_invalidate,
roll_fib_trigger_crossed,
solve_add_amount_for_total_risk,
)
def test_resolve_roll_stop_spec_treats_small_value_as_offset_pct():
mode, val = resolve_roll_stop_spec(new_stop_loss=1.0, entry_ref=63.976)
assert mode == "offset"
assert val == 1.0
def test_solve_add_amount_long_one_risk():
q2, err = solve_add_amount_for_total_risk(
"long", 1.0, 3000.0, 3100.0, 2950.0, 200.0, 1.0
)
assert err is None
avg = (1 * 3000 + q2 * 3100) / (1 + q2)
loss = (avg - 2950) * (1 + q2)
assert abs(loss - 200.0) < 0.01
def test_resolve_roll_stop_spec_treats_price_as_absolute():
mode, val = resolve_roll_stop_spec(new_stop_loss=64.6, entry_ref=63.976)
assert mode == "absolute"
assert val == 64.6
def test_unified_stop_from_avg_short_one_percent():
sl = unified_stop_from_avg("short", 63.976, 1.0)
assert abs(sl - 63.976 * 1.01) < 1e-6
def test_preview_roll_offset_mode_not_breakeven():
def test_preview_roll_market_short():
preview, err = preview_roll(
direction="short",
symbol="HYPE/USDT",
@@ -31,29 +26,45 @@ def test_preview_roll_offset_mode_not_breakeven():
entry_existing=65.0,
initial_take_profit=60.0,
add_mode="market",
stop_offset_pct=1.0,
new_stop_loss=66.5,
risk_percent=2.0,
capital_base_usdt=1000.0,
add_price=64.0,
legs_done=1,
)
assert err is None
assert preview["stop_mode"] == "offset"
assert preview["stop_offset_pct"] == 1.0
avg = preview["avg_entry_after"]
assert preview["add_mode_label"] == "市价加仓"
sl = preview["new_stop_loss"]
assert sl > avg * 1.009
assert sl < avg * 1.011
avg = preview["avg_entry_after"]
qty = preview["qty_after"]
loss = (sl - avg) * qty
assert abs(loss - 20.0) < 0.01
def test_roll_stop_after_fill_recomputes_from_actual_fill():
sl = roll_stop_after_fill(
"short",
qty_before=3.0,
entry_before=65.0,
add_qty=5.0,
fill_price=63.5,
stop_offset_pct=1.0,
def test_fib_cross_long_down():
assert roll_fib_trigger_crossed("long", 101.0, 100.0, 100.5) is True
assert roll_fib_trigger_crossed("long", 100.6, 100.6, 100.5) is False
def test_breakout_cross_long_up():
assert roll_breakout_trigger_crossed("long", 99.0, 100.5, 100.0) is True
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_preview_breakout_mode_label():
preview, err = preview_roll(
direction="long",
symbol="ETH/USDT",
qty_existing=1.0,
entry_existing=3000.0,
initial_take_profit=3500.0,
add_mode="breakout",
new_stop_loss=2980.0,
breakthrough_price=3100.0,
risk_percent=10.0,
capital_base_usdt=1000.0,
add_price=3150.0,
)
avg = (3 * 65.0 + 5 * 63.5) / 8.0
assert abs(sl - avg * 1.01) < 1e-6
assert err is None
assert preview["add_mode_label"] == "突破加仓"