alphax/tests/test_trailing_stop.py
2026-05-31 14:10:44 +08:00

66 lines
2.3 KiB
Python

import pytest
from app.core.trailing_stop import evaluate_trailing_stop, trailing_profile
def test_fixed_trailing_stop_activates_with_profit_floor():
decision = evaluate_trailing_stop(
position={"entry_price": 100, "max_price": 100, "min_price": 100, "trailing_stop": 0},
current_price=104,
pnl_pct=4,
config={
"trailing_stop_enabled": True,
"trailing_mode": "fixed",
"trailing_activate_pnl_pct": 3,
"trailing_min_lock_profit_pct": 0.5,
"trailing_distance_pct": 1.5,
},
).as_dict()
assert decision["action"] == "activate"
assert decision["event_type"] == "trailing_activate"
assert decision["activated"] is True
assert decision["trailing_mode"] == "fixed"
assert decision["trailing_stop"] == pytest.approx(102.44)
def test_trailing_stop_never_moves_down():
decision = evaluate_trailing_stop(
position={"entry_price": 100, "max_price": 106, "min_price": 100, "trailing_stop": 104},
current_price=104.5,
pnl_pct=4.5,
config={
"trailing_stop_enabled": True,
"trailing_mode": "fixed",
"trailing_activate_pnl_pct": 3,
"trailing_min_lock_profit_pct": 0.5,
"trailing_distance_pct": 1.5,
},
).as_dict()
assert decision["action"] == "hold"
assert decision["reason"] == "unchanged"
assert decision["trailing_stop"] == pytest.approx(104)
def test_volatility_profile_widens_activation_and_distance():
profile = trailing_profile(
{"entry_price": 100, "max_price": 100, "min_price": 98},
current_price=110,
pnl_pct=10,
config={
"trailing_mode": "volatility",
"trailing_activate_pnl_pct": 3,
"trailing_distance_pct": 1.5,
"trailing_volatility_activation_mult": 0.6,
"trailing_volatility_distance_mult": 0.7,
"trailing_volatility_max_activation_pct": 8,
"trailing_volatility_max_distance_pct": 8,
},
)
assert profile["trailing_mode"] == "volatility"
assert profile["volatility_pct"] == pytest.approx(12)
assert profile["activate_pnl_pct"] == pytest.approx(7.2)
assert profile["distance_pct"] == pytest.approx(8.0)