from app.core.market_regime import classify_market_regime from app.core.global_risk import evaluate_global_risk def _overview(**overrides): data = { "sample_count": 200, "benchmarks": { "BTC/USDT": {"change_24h": 0.5}, "ETH/USDT": {"change_24h": 0.4}, }, "advance_decline_ratio": 1.0, "avg_change_24h": 0.1, "hot_count_5pct": 8, "crash_count_5pct": 4, "funding": {"avg_funding_rate": 0.0001, "extreme_positive_count": 2}, } data.update(overrides) return data def test_market_regime_reduces_size_for_clear_risk_off(): result = classify_market_regime( _overview( benchmarks={"BTC/USDT": {"change_24h": -3.4}, "ETH/USDT": {"change_24h": -4.2}}, advance_decline_ratio=0.4, crash_count_5pct=35, ) ) assert result["regime"] == "risk_off" assert result["risk_level"] == "critical" assert result["position_multiplier"] == 0.25 def test_market_regime_detects_altcoin_rotation(): result = classify_market_regime( _overview( benchmarks={"BTC/USDT": {"change_24h": 0.6}, "ETH/USDT": {"change_24h": 1.1}}, advance_decline_ratio=1.4, avg_change_24h=1.2, hot_count_5pct=22, crash_count_5pct=3, ) ) assert result["regime"] == "altcoin_rotation" assert result["risk_level"] == "medium" def test_global_risk_blocks_same_sector_concentration(pg_conn, monkeypatch): monkeypatch.setattr( "app.core.global_risk.get_crypto_market_overview", lambda allow_live_fallback=False: _overview(), ) pg_conn.execute( """ INSERT INTO paper_trades ( recommendation_id, symbol, side, status, opened_at, entry_price, qty, notional_usdt, current_price, pnl_pct, created_at, updated_at ) VALUES (1, 'DOGE/USDT', 'long', 'open', '2026-05-23T10:00:00', 1, 100, 1000, 1, 0, '2026-05-23T10:00:00', '2026-05-23T10:00:00'), (2, 'SHIB/USDT', 'long', 'open', '2026-05-23T10:00:00', 1, 100, 1000, 1, 0, '2026-05-23T10:00:00', '2026-05-23T10:00:00') """ ) pg_conn.commit() result = evaluate_global_risk( conn=pg_conn, config={ "account_equity_usdt": 20000, "global_risk_max_same_sector_positions": 2, "global_risk_max_same_direction_positions": 10, }, rec={"symbol": "PEPE/USDT", "sector": "MEME"}, additional_notional=1000, ) assert result["allow_new_entries"] is False assert result["decision"] == "block_same_sector_concentration" def test_global_risk_off_is_favorable_for_short_entries(pg_conn, monkeypatch): monkeypatch.setattr( "app.core.global_risk.get_crypto_market_overview", lambda allow_live_fallback=False: _overview( benchmarks={"BTC/USDT": {"change_24h": -3.4}, "ETH/USDT": {"change_24h": -4.2}}, advance_decline_ratio=0.4, crash_count_5pct=35, ), ) result = evaluate_global_risk( conn=pg_conn, config={ "account_equity_usdt": 20000, "global_risk_gate_enabled": True, "global_risk_block_critical": False, "global_risk_critical_min_rec_score": 80, }, rec={"symbol": "SHORT/USDT", "side": "short", "rec_score": 55}, additional_notional=1000, ) assert result["allow_new_entries"] is True assert result["side"] == "short" assert result["risk_level"] == "medium" assert result["directional_market_bias"]["market_bias"] == "favorable" assert "risk_off 对空头属于顺风环境" in " ".join(result["reasons"]) def test_altcoin_rotation_is_adverse_for_short_entries(pg_conn, monkeypatch): monkeypatch.setattr( "app.core.global_risk.get_crypto_market_overview", lambda allow_live_fallback=False: _overview( benchmarks={"BTC/USDT": {"change_24h": 0.6}, "ETH/USDT": {"change_24h": 1.1}}, advance_decline_ratio=1.4, avg_change_24h=1.2, hot_count_5pct=22, crash_count_5pct=3, ), ) result = evaluate_global_risk( conn=pg_conn, config={ "account_equity_usdt": 20000, "global_risk_gate_enabled": True, "global_risk_high_min_rec_score": 70, }, rec={"symbol": "SQUEEZE/USDT", "side": "short", "rec_score": 55}, additional_notional=1000, ) assert result["allow_new_entries"] is False assert result["risk_level"] == "high" assert result["decision"] == "block_high_weak_score" assert result["directional_market_bias"]["market_bias"] == "adverse"