from __future__ import annotations import pytest from app.config import Settings from app.engine.live_guard import LIVE_CONFIRMATION_PHRASE, check_live_ready def live_settings(**overrides) -> Settings: base = { "mode": "live", "max_daily_loss": 100.0, "max_position_pct": 0.5, "live_confirmation": LIVE_CONFIRMATION_PHRASE, "exchange_api_key": "k", "exchange_api_secret": "s", "starting_balance": 1000.0, "min_live_balance": 100.0, } base.update(overrides) return Settings(**base) def test_paper_mode_always_ok() -> None: check = check_live_ready(Settings(mode="paper", max_position_pct=1.0)) assert check.ok is True assert check.blockers == [] def test_live_ok_when_fully_configured() -> None: check = check_live_ready(live_settings()) assert check.ok is True def test_live_blocked_without_any_risk_limit() -> None: check = check_live_ready( live_settings(max_daily_loss=0, max_drawdown_pct=0, max_trades_per_day=0) ) assert check.ok is False assert any("risk limit" in b for b in check.blockers) def test_live_blocked_when_position_cap_is_full_account() -> None: check = check_live_ready(live_settings(max_position_pct=1.0)) assert check.ok is False assert any("MAX_POSITION_PCT" in b for b in check.blockers) def test_live_blocked_without_confirmation_phrase() -> None: check = check_live_ready(live_settings(live_confirmation="")) assert check.ok is False assert any("LIVE_CONFIRMATION" in b for b in check.blockers) def test_live_blocked_below_min_balance() -> None: check = check_live_ready(live_settings(starting_balance=50.0)) assert check.ok is False assert any("MIN_LIVE_BALANCE" in b for b in check.blockers) def test_live_blocked_without_credentials() -> None: check = check_live_ready(live_settings(exchange_api_key="", exchange_api_secret="")) assert check.ok is False assert any("EXCHANGE_API_KEY" in b for b in check.blockers) async def test_start_many_refuses_live_without_confirmation(settings) -> None: from app.runtime import Runtime from app.schemas import StartManyRequest, StrategySpec settings.mode = "live" settings.exchange_api_key = "k" settings.exchange_api_secret = "s" settings.max_position_pct = 1.0 # unsafe runtime = Runtime(settings) with pytest.raises(ValueError, match="live trading blocked"): await runtime.start_many( StartManyRequest(strategies=[StrategySpec(strategy_key="sma_cross")]) ) def test_live_check_endpoint(runtime) -> None: from fastapi.testclient import TestClient from app.main import create_app app = create_app(runtime.settings, runtime) with TestClient(app) as client: body = client.get("/api/live-check").json() assert body["ok"] is True # paper mode assert body["mode"] == "paper"