Files
llm-trade-bot/tests/test_risk.py
2026-09-22 11:43:32 +02:00

74 lines
2.2 KiB
Python

from decimal import Decimal
import pytest
from llm_trade_bot.models import (
AccountSnapshot,
Category,
InstrumentRules,
OpenTradeRequest,
OrderType,
Side,
)
from llm_trade_bot.risk import OrderRejected, OrderValidator
@pytest.fixture
def engine() -> OrderValidator:
return OrderValidator()
@pytest.fixture
def account() -> AccountSnapshot:
return AccountSnapshot(Decimal(10000), Decimal(10000), Decimal(0), 0)
@pytest.fixture
def rules() -> InstrumentRules:
return InstrumentRules(Decimal("0.1"), Decimal("0.001"), Decimal("0.001"), Decimal(5), Decimal(100))
def request(**overrides) -> OpenTradeRequest:
values = {
"category": Category.LINEAR,
"symbol": "BTCUSDT",
"side": Side.BUY,
"order_type": OrderType.MARKET,
"reference_price": Decimal(100),
"limit_price": None,
"stop_loss": Decimal(90),
"take_profit": Decimal(120),
"trailing_distance": None,
"leverage": Decimal(3),
"rationale": "test",
}
values.update(overrides)
return OpenTradeRequest(**values)
def test_uses_all_available_buying_power(engine, account, rules):
result = engine.validate_open(request(), account, rules)
assert result.quantity == Decimal(300)
assert result.notional == Decimal(30000)
def test_existing_exposure_does_not_cap_position(engine, rules):
account = AccountSnapshot(Decimal(10000), Decimal(10000), Decimal(3900), 2)
result = engine.validate_open(request(), account, rules)
assert result.quantity == Decimal(300)
assert result.notional == Decimal(30000)
@pytest.mark.parametrize(
"overrides, message",
[
({"leverage": Decimal(101)}, "Leverage"),
({"stop_loss": Decimal(101)}, "stop-loss"),
({"take_profit": None, "trailing_distance": None}, "take-profit or trailing"),
({"category": Category.SPOT, "order_type": OrderType.LIMIT, "limit_price": Decimal(100), "take_profit": None}, "Spot entry requires"),
],
)
def test_rejects_unsafe_requests(engine, account, rules, overrides, message):
with pytest.raises(OrderRejected, match=message):
engine.validate_open(request(**overrides), account, rules)