07.4.4.1.7 — Live Market Runtime & Advanced Trend Diagnostics
This commit is contained in:
@@ -126,12 +126,12 @@ async def auto_start(callback: CallbackQuery) -> None:
|
||||
|
||||
_, message = service.start()
|
||||
|
||||
AutoTradeRunner.start()
|
||||
|
||||
if callback.message is not None:
|
||||
await _prepare_auto_from_callback(callback)
|
||||
await render_auto_screen(callback.message, edit_mode=True)
|
||||
|
||||
AutoTradeRunner.start()
|
||||
|
||||
await callback.answer(message)
|
||||
|
||||
|
||||
@@ -153,12 +153,12 @@ async def auto_observe(callback: CallbackQuery) -> None:
|
||||
|
||||
_, message = service.observe()
|
||||
|
||||
AutoTradeRunner.start()
|
||||
|
||||
if callback.message is not None:
|
||||
await _prepare_auto_from_callback(callback)
|
||||
await render_auto_screen(callback.message, edit_mode=True)
|
||||
|
||||
AutoTradeRunner.start()
|
||||
|
||||
await callback.answer(message)
|
||||
|
||||
|
||||
|
||||
@@ -179,16 +179,46 @@ class AutoTradeRunner:
|
||||
MarketDataRunner.stop("auto")
|
||||
break
|
||||
|
||||
service.run_cycle()
|
||||
try:
|
||||
service.run_cycle()
|
||||
except Exception as exc:
|
||||
cls._log_refresh_error(
|
||||
"auto_run_cycle_error",
|
||||
{
|
||||
"error": str(exc),
|
||||
"error_type": type(exc).__name__,
|
||||
"symbol": state.symbol,
|
||||
"strategy": state.strategy,
|
||||
"status": state.status,
|
||||
},
|
||||
)
|
||||
|
||||
current_event_version = EventBus.version()
|
||||
has_important_event = current_event_version != cls._last_event_version
|
||||
|
||||
if has_important_event:
|
||||
cls._last_event_version = current_event_version
|
||||
await cls._handle_important_event(state)
|
||||
try:
|
||||
await cls._handle_important_event(state)
|
||||
except Exception as exc:
|
||||
cls._log_refresh_error(
|
||||
"auto_event_handler_error",
|
||||
{
|
||||
"error": str(exc),
|
||||
"error_type": type(exc).__name__,
|
||||
},
|
||||
)
|
||||
|
||||
await cls._refresh_screen(force=has_important_event)
|
||||
try:
|
||||
await cls._refresh_screen(force=has_important_event)
|
||||
except Exception as exc:
|
||||
cls._log_refresh_error(
|
||||
"auto_refresh_loop_error",
|
||||
{
|
||||
"error": str(exc),
|
||||
"error_type": type(exc).__name__,
|
||||
},
|
||||
)
|
||||
|
||||
await asyncio.sleep(cls._analysis_interval_seconds)
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ class ScalpStrategy:
|
||||
"strategy": self.name,
|
||||
"symbol": context.symbol,
|
||||
"error": str(exc),
|
||||
"entry_block_reason": "MARKET_PRICE_ERROR",
|
||||
"entry_block_message": "нет данных рынка",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -75,17 +77,25 @@ class ScalpStrategy:
|
||||
if len(prices) > self._window_size:
|
||||
prices.pop(0)
|
||||
|
||||
base_payload = {
|
||||
"strategy": self.name,
|
||||
"symbol": symbol,
|
||||
"price": current_price,
|
||||
"runtime_window_ttl_seconds": self._window_ttl_seconds,
|
||||
"runtime_window_size": len(prices),
|
||||
}
|
||||
|
||||
if len(prices) < self._window_size:
|
||||
return SignalResult(
|
||||
signal=SignalType.HOLD,
|
||||
reason="Недостаточно данных для SCALP.",
|
||||
confidence=0.0,
|
||||
payload={
|
||||
"strategy": self.name,
|
||||
"symbol": symbol,
|
||||
"price": current_price,
|
||||
**base_payload,
|
||||
"window_size": len(prices),
|
||||
"required_window_size": self._window_size,
|
||||
"entry_block_reason": "NOT_ENOUGH_LIVE_DATA",
|
||||
"entry_block_message": "мало данных",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -98,9 +108,10 @@ class ScalpStrategy:
|
||||
reason="Некорректная стартовая цена в окне SCALP.",
|
||||
confidence=0.0,
|
||||
payload={
|
||||
"strategy": self.name,
|
||||
"symbol": symbol,
|
||||
**base_payload,
|
||||
"prices": prices,
|
||||
"entry_block_reason": "INVALID_WINDOW_PRICE",
|
||||
"entry_block_message": "ошибка цены",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -108,8 +119,7 @@ class ScalpStrategy:
|
||||
direction_ratio = self._direction_ratio(prices, change_percent)
|
||||
|
||||
payload = {
|
||||
"strategy": self.name,
|
||||
"symbol": symbol,
|
||||
**base_payload,
|
||||
"first_price": first_price,
|
||||
"current_price": last_price,
|
||||
"change_percent": round(change_percent, 5),
|
||||
@@ -141,11 +151,23 @@ class ScalpStrategy:
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
expected_direction = "BUY" if change_percent >= 0 else "SELL"
|
||||
entry_block_reason = (
|
||||
"WEAK_UP_IMPULSE"
|
||||
if expected_direction == "BUY"
|
||||
else "WEAK_DOWN_IMPULSE"
|
||||
)
|
||||
|
||||
return SignalResult(
|
||||
signal=SignalType.HOLD,
|
||||
reason="SCALP-импульс недостаточно сильный.",
|
||||
confidence=0.0,
|
||||
payload=payload,
|
||||
payload={
|
||||
**payload,
|
||||
"entry_block_reason": entry_block_reason,
|
||||
"entry_block_message": "слабый импульс",
|
||||
"expected_direction": expected_direction,
|
||||
},
|
||||
)
|
||||
|
||||
def _direction_ratio(self, prices: list[float], change_percent: float) -> float:
|
||||
|
||||
@@ -64,6 +64,8 @@ class TrendStrategy:
|
||||
"symbol": context.symbol,
|
||||
"error": str(exc),
|
||||
"market_analysis": market.payload,
|
||||
"entry_block_reason": "MARKET_SNAPSHOT_ERROR",
|
||||
"entry_block_message": "нет данных рынка",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -80,6 +82,8 @@ class TrendStrategy:
|
||||
"symbol": symbol,
|
||||
"snapshot": snapshot,
|
||||
"market_analysis": market.payload,
|
||||
"entry_block_reason": "INVALID_MARKET_PRICE",
|
||||
"entry_block_message": "нет цены",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -138,6 +142,8 @@ class TrendStrategy:
|
||||
**base_payload,
|
||||
"window_size": len(prices),
|
||||
"required_window_size": self._window_size,
|
||||
"entry_block_reason": "NOT_ENOUGH_LIVE_DATA",
|
||||
"entry_block_message": "мало данных",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -152,6 +158,8 @@ class TrendStrategy:
|
||||
payload={
|
||||
**base_payload,
|
||||
"prices": prices,
|
||||
"entry_block_reason": "INVALID_WINDOW_PRICE",
|
||||
"entry_block_message": "ошибка цены",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -185,7 +193,12 @@ class TrendStrategy:
|
||||
signal=SignalType.HOLD,
|
||||
reason="TREND_UP есть, но live-импульс вверх недостаточно сильный.",
|
||||
confidence=0.0,
|
||||
payload=payload,
|
||||
payload={
|
||||
**payload,
|
||||
"entry_block_reason": "WEAK_UP_IMPULSE",
|
||||
"entry_block_message": "слабый импульс",
|
||||
"expected_direction": "BUY",
|
||||
},
|
||||
)
|
||||
|
||||
if market.state == MarketState.TREND_DOWN:
|
||||
@@ -204,14 +217,23 @@ class TrendStrategy:
|
||||
signal=SignalType.HOLD,
|
||||
reason="TREND_DOWN есть, но live-импульс вниз недостаточно сильный.",
|
||||
confidence=0.0,
|
||||
payload=payload,
|
||||
payload={
|
||||
**payload,
|
||||
"entry_block_reason": "WEAK_DOWN_IMPULSE",
|
||||
"entry_block_message": "слабый импульс",
|
||||
"expected_direction": "SELL",
|
||||
},
|
||||
)
|
||||
|
||||
return SignalResult(
|
||||
signal=SignalType.HOLD,
|
||||
reason=f"Market state не подходит для TREND: {market.state.value}.",
|
||||
confidence=0.0,
|
||||
payload=payload,
|
||||
payload={
|
||||
**payload,
|
||||
"entry_block_reason": "MARKET_STATE_NOT_TREND",
|
||||
"entry_block_message": "рынок флэт",
|
||||
},
|
||||
)
|
||||
|
||||
def _analysis_price(self, snapshot: dict[str, object]) -> float:
|
||||
|
||||
Reference in New Issue
Block a user