from strategy_roll_lib import ( preview_roll, roll_breakout_invalidate, roll_breakout_trigger_crossed, roll_fib_invalidate, roll_fib_trigger_crossed, solve_add_amount_for_total_risk, validate_roll_geometry, ) 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_preview_roll_market_short(): preview, err = preview_roll( direction="short", symbol="HYPE/USDT", qty_existing=3.0, entry_existing=65.0, initial_take_profit=60.0, add_mode="market", 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["add_mode_label"] == "市价加仓" sl = preview["new_stop_loss"] avg = preview["avg_entry_after"] qty = preview["qty_after"] loss = (sl - avg) * qty assert abs(loss - 20.0) < 0.01 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=3050.0, ) assert err is None assert preview["add_mode_label"] == "突破加仓" def test_breakout_geometry_short_mark_above_breakout(): err = validate_roll_geometry( "short", "breakout", new_stop_loss=568.0, breakthrough_price=551.0, entry_existing=560.0, initial_take_profit=540.0, mark_price=560.0, ) assert err is None def test_breakout_geometry_short_rejects_mark_at_or_below_breakout(): err = validate_roll_geometry( "short", "breakout", new_stop_loss=568.0, breakthrough_price=551.0, entry_existing=560.0, initial_take_profit=540.0, mark_price=551.0, ) assert err is not None assert "高于突破价" in err def test_breakout_geometry_long_rejects_mark_at_or_above_breakout(): err = validate_roll_geometry( "long", "breakout", new_stop_loss=2980.0, breakthrough_price=3100.0, entry_existing=3000.0, initial_take_profit=3500.0, mark_price=3100.0, ) assert err is not None assert "低于突破价" in err