from app.services.scanners.funding_reversal import evaluate_funding_reversal from app.services.scanners.sma_reclaim import evaluate_sma_reclaim from app.services.signal_categories import ( is_system_2, is_supported_trading_source, system2_display_name, system2_min_confidence, system2_size_multiplier, ) from app.services.bot_engine import _confidence_floor_for, _should_apply_schedule from app.services.tp_sl_monitor import register_trade, _watched def _daily(close: float, volume: float = 100.0) -> dict: return {"close": close, "volume": volume} def test_sma_reclaim_no_longer_emits_short_breakdowns(): candles = [_daily(110.0) for _ in range(200)] candles.extend(_daily(110.0) for _ in range(31)) candles.append(_daily(90.0, volume=200.0)) is_signal, debug = evaluate_sma_reclaim(candles) assert is_signal is False assert debug["reason"] == "shorts_disabled" def test_funding_reversal_reports_boost_without_standalone_signal(): funding = [ {"time_ms": i * 3_600_000, "rate": -0.00005} for i in range(24 * 29) ] funding.extend( {"time_ms": (24 * 29 + i) * 3_600_000, "rate": -0.00001} for i in range(24) ) candles = [_daily(100.0) for _ in range(8)] candles[-1] = _daily(104.0) is_signal, debug = evaluate_funding_reversal(funding, candles) assert is_signal is True assert debug["direction"] == "buy" def test_bottom_reversal_source_routes_to_system_2(): assert is_system_2("btc_bottom_reversal") def test_old_scanner_sources_no_longer_route_to_module_2(): for source in ("rsi_reversal", "sma_reclaim", "funding_reversal", "breakout", "vcp_breakout"): assert not is_system_2(source) def test_unknown_external_sources_are_not_supported_for_trading(): assert is_supported_trading_source("truth") assert is_supported_trading_source("btc_bottom_reversal") assert not is_supported_trading_source("manual") assert not is_supported_trading_source("sma_reclaim") def test_module_2_uses_own_confidence_floor_and_bypasses_schedule(): sys2_sub = {"_is_system_2": True, "min_confidence": 95} sys1_sub = {"_is_system_2": False, "min_confidence": 95} assert system2_display_name() == "Bitcoin Bottom" assert _confidence_floor_for(sys2_sub) == system2_min_confidence() assert _confidence_floor_for(sys1_sub) == 95 assert _should_apply_schedule(sys2_sub) is False assert _should_apply_schedule(sys1_sub) is True def test_register_trade_keeps_invalidation_price(): register_trade( trade_id=99991, wallet="0xabc", api_key="key", leverage=3, asset="BTC", side="long", entry_price=100.0, take_profit_pct=None, stop_loss_pct=6.0, trailing_stop_pct=5.0, trailing_activate_at_pct=12.0, invalidation="below_entry", invalidation_price=92.5, min_hold_until_ts=None, ) try: wt = _watched[99991] assert wt.invalidation_price == 92.5 finally: _watched.pop(99991, None)