07.4.4.1.5 — Runtime Window Cleanup & Symbol Lifecycle Isolation
This commit is contained in:
@@ -116,18 +116,18 @@ class MarketDataRunner:
|
||||
):
|
||||
MarketPriceCache.clear(cache_symbol)
|
||||
|
||||
if previous_symbol is not None:
|
||||
cls._log_info(
|
||||
context,
|
||||
"market_symbol_changed",
|
||||
f"Инструмент автоторговли изменён: {cache_symbol}.",
|
||||
{
|
||||
"previous_symbol": previous_symbol,
|
||||
"symbol": symbol,
|
||||
"cache_symbol": cache_symbol,
|
||||
"ws_symbol": ws_symbol,
|
||||
},
|
||||
)
|
||||
#if previous_symbol is not None:
|
||||
# cls._log_info(
|
||||
# context,
|
||||
# "market_symbol_changed",
|
||||
# f"Инструмент автоторговли изменён: {cache_symbol}.",
|
||||
# {
|
||||
# "previous_symbol": previous_symbol,
|
||||
# "symbol": symbol,
|
||||
# "cache_symbol": cache_symbol,
|
||||
# "ws_symbol": ws_symbol,
|
||||
# },
|
||||
# )
|
||||
try:
|
||||
await cls._run_websocket(context, symbol)
|
||||
except asyncio.CancelledError:
|
||||
|
||||
@@ -229,15 +229,28 @@ class AutoTradeService:
|
||||
# установить инструмент
|
||||
def set_symbol(self, symbol: str) -> AutoTradeState:
|
||||
state = self.get_state()
|
||||
previous_symbol = state.symbol
|
||||
|
||||
state.symbol = symbol
|
||||
self._reset_signal_tracking()
|
||||
|
||||
StrategyRegistry.reset_runtime(symbol=previous_symbol)
|
||||
StrategyRegistry.reset_runtime(symbol=symbol)
|
||||
|
||||
return state
|
||||
|
||||
# установить стратегию
|
||||
def set_strategy(self, strategy: str) -> AutoTradeState:
|
||||
state = self.get_state()
|
||||
state.strategy = strategy.strip().upper()
|
||||
previous_strategy = state.strategy
|
||||
normalized_strategy = strategy.strip().upper()
|
||||
|
||||
state.strategy = normalized_strategy
|
||||
self._reset_signal_tracking()
|
||||
|
||||
StrategyRegistry.reset_runtime(previous_strategy)
|
||||
StrategyRegistry.reset_runtime(normalized_strategy)
|
||||
|
||||
return state
|
||||
|
||||
# установить риск
|
||||
@@ -297,6 +310,13 @@ class AutoTradeService:
|
||||
state.is_signal_ready = False
|
||||
state.execution_block_reason = None
|
||||
state.signal_started_at = None
|
||||
state.market_state = None
|
||||
state.market_trend = None
|
||||
state.market_volatility = None
|
||||
state.market_analysis_interval = None
|
||||
state.market_analysis_reason = None
|
||||
state.entry_block_reason = None
|
||||
state.entry_block_message = None
|
||||
|
||||
# собрать контекст для стратегии
|
||||
def _build_strategy_context(self) -> StrategyContext:
|
||||
|
||||
@@ -26,4 +26,8 @@ class BaseStrategy(Protocol):
|
||||
|
||||
# выполнить анализ и вернуть торговый сигнал
|
||||
def analyze(self, context: StrategyContext) -> SignalResult:
|
||||
...
|
||||
|
||||
# сбросить runtime-память стратегии
|
||||
def reset_runtime(self, symbol: str | None = None) -> None:
|
||||
...
|
||||
@@ -20,4 +20,7 @@ class HoldStrategy:
|
||||
"status": context.status,
|
||||
"strategy": self.name,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
def reset_runtime(self, symbol: str | None = None) -> None:
|
||||
return
|
||||
@@ -17,6 +17,27 @@ class StrategyRegistry:
|
||||
"SCALP": ScalpStrategy(),
|
||||
}
|
||||
|
||||
# сбросить runtime-память одной стратегии
|
||||
@classmethod
|
||||
def reset_runtime(
|
||||
cls,
|
||||
name: str | None = None,
|
||||
*,
|
||||
symbol: str | None = None,
|
||||
) -> None:
|
||||
if name:
|
||||
strategy = cls.get(name)
|
||||
strategy.reset_runtime(symbol)
|
||||
return
|
||||
|
||||
for strategy in cls._strategies.values():
|
||||
strategy.reset_runtime(symbol)
|
||||
|
||||
# сбросить runtime-память всех стратегий
|
||||
@classmethod
|
||||
def reset_all_runtime(cls) -> None:
|
||||
cls.reset_runtime()
|
||||
|
||||
# получить стратегию по имени
|
||||
@classmethod
|
||||
def get(cls, name: str | None) -> BaseStrategy:
|
||||
|
||||
@@ -21,6 +21,20 @@ class ScalpStrategy:
|
||||
# для scalp допускаем чуть больше шума
|
||||
_min_direction_ratio = 0.55
|
||||
|
||||
def reset_runtime(self, symbol: str | None = None) -> None:
|
||||
if symbol is None:
|
||||
self._price_window.clear()
|
||||
return
|
||||
|
||||
normalized_symbol = symbol.upper()
|
||||
keys_to_delete = [
|
||||
key for key in self._price_window.keys()
|
||||
if key.upper() == normalized_symbol
|
||||
]
|
||||
|
||||
for key in keys_to_delete:
|
||||
self._price_window.pop(key, None)
|
||||
|
||||
def analyze(self, context: StrategyContext) -> SignalResult:
|
||||
try:
|
||||
ticker = ExchangeService().get_price(context.symbol)
|
||||
|
||||
@@ -22,6 +22,20 @@ class TrendStrategy:
|
||||
# основной таймфрейм анализа рынка
|
||||
_market_interval = "5m"
|
||||
|
||||
def reset_runtime(self, symbol: str | None = None) -> None:
|
||||
if symbol is None:
|
||||
self._price_window.clear()
|
||||
return
|
||||
|
||||
normalized_symbol = symbol.upper()
|
||||
keys_to_delete = [
|
||||
key for key in self._price_window.keys()
|
||||
if key.upper() == normalized_symbol
|
||||
]
|
||||
|
||||
for key in keys_to_delete:
|
||||
self._price_window.pop(key, None)
|
||||
|
||||
def analyze(self, context: StrategyContext) -> SignalResult:
|
||||
market = MarketAnalysisService().analyze(
|
||||
context.symbol,
|
||||
|
||||
Reference in New Issue
Block a user