fix: keep paper trading active without approved model

This commit is contained in:
Курнат Андрей
2026-07-15 00:23:03 +03:00
parent 1f2fb011a7
commit d0869b5d29
10 changed files with 188 additions and 14 deletions
+9
View File
@@ -171,3 +171,12 @@ def test_load_settings_rejects_inconsistent_exposure_limits(tmp_path, monkeypatc
with pytest.raises(ValueError, match="MAX_SYMBOL_EXPOSURE_USDT"):
load_settings(env_file)
def test_load_settings_rejects_unknown_fallback_mode(tmp_path, monkeypatch) -> None:
monkeypatch.delenv("TIME_SERIES_FALLBACK_MODE", raising=False)
env_file = tmp_path / ".env"
env_file.write_text("TIME_SERIES_FALLBACK_MODE=force-trades\n", encoding="utf-8")
with pytest.raises(ValueError, match="TIME_SERIES_FALLBACK_MODE"):
load_settings(env_file)
+1
View File
@@ -45,6 +45,7 @@ def test_safe_config_summarizes_torch_forecast_artifact(make_settings, tmp_path)
assert config["time_series_probe_min_probability_up"] == 0.55
assert config["time_series_probe_size_multiplier"] == 0.40
assert config["time_series_rebound_fallback_enabled"] is True
assert config["time_series_fallback_mode"] == "trend_macd"
assert config["time_series_model_artifact"] == {
"available": True,
"type": "pytorch_recurrent_forecaster",
+88
View File
@@ -631,6 +631,94 @@ def test_torch_forecast_uses_trend_exit_for_fallback_position(make_settings, tmp
assert "MACD" in signal.reason
def test_torch_forecast_uses_legacy_fallback_in_paper_mode(make_settings, tmp_path) -> None:
settings = make_settings(
tmp_path,
strategy_mode="torch_forecast",
time_series_trend_fallback_enabled=True,
time_series_fallback_mode="legacy",
time_series_require_quality_gate=True,
time_series_require_fresh_model=True,
grid_trading_enabled=False,
rebound_trading_enabled=False,
kelly_sizing_enabled=False,
)
strategy = SpotStrategy(settings)
ticker = Ticker("BTCUSDT", 101, 100.99, 101.01, 10_000_000, 1000, 1.0)
signal = strategy.entry_signal(
"BTCUSDT",
_ready_candles(),
ticker,
open_positions_for_symbol=0,
forecast={"usable": False, "model": "none", "quality_gate_passed": False},
account={"equity": 100.0, "cash": 100.0, "exposure": 0.0},
)
assert signal.action == "BUY"
assert signal.diagnostics["trade_mode"] == "LEGACY_FALLBACK"
assert signal.diagnostics["entry_path"] == "legacy_fallback"
assert signal.diagnostics["forecast_fallback_active"] is True
def test_torch_forecast_forces_trend_fallback_in_live_mode(make_settings, tmp_path) -> None:
settings = make_settings(
tmp_path,
trading_mode="live",
strategy_mode="torch_forecast",
time_series_trend_fallback_enabled=True,
time_series_fallback_mode="legacy",
time_series_require_quality_gate=True,
time_series_require_fresh_model=True,
max_position_usdt=50,
)
strategy = SpotStrategy(settings)
ticker = Ticker("BTCUSDT", 105, 104.99, 105.01, 10_000_000, 1000, 1.0)
signal = strategy.entry_signal(
"BTCUSDT",
_trend_entry_candles(),
ticker,
open_positions_for_symbol=0,
forecast={"usable": False, "model": "none", "quality_gate_passed": False},
account={"equity": 100.0},
trend_candles=_daily_trend_candles(),
)
assert signal.action == "BUY"
assert signal.diagnostics["trade_mode"] == "TREND_MACD_FALLBACK"
assert signal.diagnostics["entry_path"] == "trend_macd_fallback"
def test_torch_forecast_uses_legacy_exit_for_paper_fallback_position(make_settings, tmp_path) -> None:
settings = make_settings(
tmp_path,
strategy_mode="torch_forecast",
time_series_trend_fallback_enabled=True,
time_series_fallback_mode="legacy",
)
strategy = SpotStrategy(settings)
position = Position(
1,
"BTCUSDT",
1,
100,
100,
0.1,
96,
103.5,
100,
entry_diagnostics={"entry_path": "legacy_fallback"},
)
ticker = Ticker("BTCUSDT", 104, 103.99, 104.01, 10_000_000, 1000, 1.0)
signal = strategy.exit_signal(position, _ready_candles(), ticker, forecast={})
assert signal.action == "SELL"
assert signal.diagnostics["trade_mode"] == "LEGACY_FALLBACK"
assert signal.diagnostics["entry_path"] == "legacy_fallback"
def test_torch_forecast_allows_explicit_manual_quality_override(make_settings, tmp_path) -> None:
settings = make_settings(
tmp_path,