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
+1 -1
View File
@@ -1,3 +1,3 @@
"""Crypto spot trading bot package."""
__version__ = "1.0.0"
__version__ = "1.0.1"
+5
View File
@@ -397,6 +397,11 @@ class CryptoSpotBot:
self.settings.pattern_analysis_enabled
or self.settings.grid_trading_enabled
or self.settings.rebound_trading_enabled
or (
self.settings.strategy_mode == "torch_forecast"
and self.settings.time_series_trend_fallback_enabled
and self.settings.time_series_fallback_mode == "legacy"
)
)
if self.settings.strategy_mode == "trend_macd" or not patterns_needed:
self.market.patterns = {}
+6
View File
@@ -171,6 +171,7 @@ class Settings:
storage_prune_interval_seconds: int = 3600
bybit_rest_base_url_override: str = ""
bybit_websocket_url_override: str = ""
time_series_fallback_mode: str = "trend_macd"
@property
def rest_base_url(self) -> str:
@@ -354,6 +355,9 @@ def load_settings(env_file: str | Path | None = None) -> Settings:
"" if _bool_env("BYBIT_TESTNET", False) else "https://api.bybit.kz",
).strip(),
bybit_websocket_url_override=os.getenv("BYBIT_WEBSOCKET_URL", "").strip(),
time_series_fallback_mode=os.getenv(
"TIME_SERIES_FALLBACK_MODE", "trend_macd"
).strip().lower(),
)
_validate_settings(settings)
if settings.trading_mode == "live" and not settings.live_ready:
@@ -390,6 +394,8 @@ def _validate_settings(settings: Settings) -> None:
errors.append("LIVE_ORDER_FILL_TIMEOUT_SECONDS must be positive")
if settings.live_reconciliation_interval_seconds <= 0:
errors.append("LIVE_RECONCILIATION_INTERVAL_SECONDS must be positive")
if settings.time_series_fallback_mode not in {"trend_macd", "legacy"}:
errors.append("TIME_SERIES_FALLBACK_MODE must be trend_macd or legacy")
if errors:
raise ValueError("; ".join(errors))
+1
View File
@@ -401,6 +401,7 @@ def _safe_config(settings: Settings) -> dict[str, Any]:
"time_series_probe_size_multiplier": settings.time_series_probe_size_multiplier,
"time_series_rebound_fallback_enabled": settings.time_series_rebound_fallback_enabled,
"time_series_trend_fallback_enabled": settings.time_series_trend_fallback_enabled,
"time_series_fallback_mode": settings.time_series_fallback_mode,
"time_series_require_quality_gate": settings.time_series_require_quality_gate,
"time_series_manual_quality_override": settings.time_series_manual_quality_override,
"time_series_require_fresh_model": settings.time_series_require_fresh_model,
+73 -12
View File
@@ -1,5 +1,7 @@
from __future__ import annotations
from dataclasses import replace
from crypto_spot_bot.config import Settings
from crypto_spot_bot.models import Candle, Position, Signal, Ticker, utc_now
@@ -27,21 +29,45 @@ class SpotStrategy:
if self.settings.strategy_mode == "torch_forecast":
fallback_reasons = torch_model_readiness_reasons(self.settings, forecast or {})
if self.settings.time_series_trend_fallback_enabled and fallback_reasons:
fallback = _trend_macd_entry_signal(
settings=self.settings,
symbol=symbol,
candles=candles,
trend_candles=trend_candles or [],
ticker=ticker,
open_positions_for_symbol=open_positions_for_symbol,
account=account,
)
fallback_mode = _effective_fallback_mode(self.settings)
if fallback_mode == "legacy":
fallback_settings = replace(
self.settings,
strategy_mode="legacy",
time_series_forecast_enabled=False,
)
fallback = SpotStrategy(fallback_settings).entry_signal(
symbol,
candles,
ticker,
open_positions_for_symbol,
pattern,
learning,
llm,
{},
account,
trend_candles,
)
trade_mode = "LEGACY_FALLBACK"
entry_path = "legacy_fallback"
else:
fallback = _trend_macd_entry_signal(
settings=self.settings,
symbol=symbol,
candles=candles,
trend_candles=trend_candles or [],
ticker=ticker,
open_positions_for_symbol=open_positions_for_symbol,
account=account,
)
trade_mode = "TREND_MACD_FALLBACK"
entry_path = "trend_macd_fallback"
diagnostics = dict(fallback.diagnostics)
diagnostics.update(
{
"strategy_mode": "torch_forecast",
"trade_mode": "TREND_MACD_FALLBACK",
"entry_path": "trend_macd_fallback",
"trade_mode": trade_mode,
"entry_path": entry_path,
"forecast_fallback_active": True,
"forecast_fallback_reasons": fallback_reasons,
"forecast": forecast or {},
@@ -397,7 +423,36 @@ class SpotStrategy:
forecast: dict | None = None,
) -> Signal:
if self.settings.strategy_mode == "torch_forecast":
if str(position.entry_diagnostics.get("entry_path", "")) == "trend_macd_fallback":
entry_path = str(position.entry_diagnostics.get("entry_path", ""))
if entry_path == "legacy_fallback":
fallback_settings = replace(
self.settings,
strategy_mode="legacy",
time_series_forecast_enabled=False,
)
fallback = SpotStrategy(fallback_settings)._legacy_exit_signal(
position,
candles,
ticker,
learning,
)
diagnostics = dict(fallback.diagnostics)
diagnostics.update(
{
"strategy_mode": "torch_forecast",
"trade_mode": "LEGACY_FALLBACK",
"entry_path": "legacy_fallback",
"forecast_fallback_active": True,
}
)
return Signal(
fallback.symbol,
fallback.action,
fallback.confidence,
f"torch_forecast fallback: {fallback.reason}",
diagnostics,
)
if entry_path == "trend_macd_fallback":
fallback = _trend_macd_exit_signal(self.settings, position, candles, ticker)
diagnostics = dict(fallback.diagnostics)
diagnostics.update(
@@ -534,6 +589,12 @@ def _has_entry_indicators(candle: Candle) -> bool:
)
def _effective_fallback_mode(settings: Settings) -> str:
if settings.trading_mode != "paper":
return "trend_macd"
return settings.time_series_fallback_mode
def _trend_macd_entry_signal(
*,
settings: Settings,