07.4.4.1.9 Adaptive Market Diagnostics Layer
This commit is contained in:
@@ -158,6 +158,7 @@ def _build_waiting_text(state) -> str:
|
||||
signal_lines = [
|
||||
_signal_line(state),
|
||||
_market_state_line(state),
|
||||
_market_diagnostics_line(state),
|
||||
_entry_block_line(state),
|
||||
_execution_quality_line(state),
|
||||
*_signal_confidence_lines(state),
|
||||
@@ -261,6 +262,49 @@ def _market_state_line(state) -> str:
|
||||
return labels.get(market_state, "⏳ Рынок · Идёт анализ")
|
||||
|
||||
|
||||
def _market_diagnostics_line(state) -> str:
|
||||
strength = getattr(state, "market_trend_strength", None)
|
||||
quality = getattr(state, "market_trend_quality", None)
|
||||
phase = getattr(state, "market_phase", None)
|
||||
|
||||
if not strength and not quality and not phase:
|
||||
return ""
|
||||
|
||||
strength_labels = {
|
||||
"WEAK": "слабый",
|
||||
"NORMAL": "нормальный",
|
||||
"STRONG": "сильный",
|
||||
}
|
||||
|
||||
quality_labels = {
|
||||
"CLEAN": "чистый",
|
||||
"NOISY": "шумный",
|
||||
}
|
||||
|
||||
phase_labels = {
|
||||
"IMPULSE": "импульс",
|
||||
"PULLBACK": "откат",
|
||||
"RANGE": "флэт",
|
||||
"SQUEEZE": "сжатие",
|
||||
}
|
||||
|
||||
parts = []
|
||||
|
||||
if strength in strength_labels:
|
||||
parts.append(strength_labels[strength])
|
||||
|
||||
if quality in quality_labels:
|
||||
parts.append(quality_labels[quality])
|
||||
|
||||
if phase in phase_labels:
|
||||
parts.append(phase_labels[phase])
|
||||
|
||||
if not parts:
|
||||
return ""
|
||||
|
||||
return f"Анализ · {' · '.join(parts)}"
|
||||
|
||||
|
||||
def _compact_entry_block_message(message: str) -> str:
|
||||
normalized = message.strip().lower()
|
||||
|
||||
|
||||
@@ -378,6 +378,9 @@ class AutoTradeService:
|
||||
state.execution_quality_reason = None
|
||||
state.execution_quality_message = None
|
||||
state.market_runtime_degraded = False
|
||||
state.market_trend_strength = None
|
||||
state.market_trend_quality = None
|
||||
state.market_phase = None
|
||||
|
||||
# собрать контекст для стратегии
|
||||
def _build_strategy_context(self) -> StrategyContext:
|
||||
@@ -746,6 +749,9 @@ class AutoTradeService:
|
||||
state.market_state = payload.get("market_state")
|
||||
state.market_trend = payload.get("market_trend")
|
||||
state.market_volatility = payload.get("market_volatility")
|
||||
state.market_trend_strength = payload.get("market_trend_strength")
|
||||
state.market_trend_quality = payload.get("market_trend_quality")
|
||||
state.market_phase = payload.get("market_phase")
|
||||
state.market_analysis_interval = payload.get("market_analysis_interval")
|
||||
state.market_analysis_reason = payload.get("market_analysis_reason")
|
||||
state.market_analysis_updated_at = time.monotonic()
|
||||
|
||||
@@ -115,6 +115,15 @@ class AutoTradeState:
|
||||
# волатильность: LOW / NORMAL / HIGH / UNKNOWN
|
||||
market_volatility: str | None = None
|
||||
|
||||
# сила тренда: WEAK / NORMAL / STRONG / UNKNOWN
|
||||
market_trend_strength: str | None = None
|
||||
|
||||
# качество тренда: CLEAN / NOISY / UNKNOWN
|
||||
market_trend_quality: str | None = None
|
||||
|
||||
# фаза рынка: IMPULSE / PULLBACK / RANGE / SQUEEZE / UNKNOWN
|
||||
market_phase: str | None = None
|
||||
|
||||
# таймфрейм анализа рынка
|
||||
market_analysis_interval: str | None = None
|
||||
|
||||
|
||||
@@ -29,6 +29,27 @@ class VolatilityState(StrEnum):
|
||||
UNKNOWN = "UNKNOWN"
|
||||
|
||||
|
||||
class TrendStrength(StrEnum):
|
||||
WEAK = "WEAK"
|
||||
NORMAL = "NORMAL"
|
||||
STRONG = "STRONG"
|
||||
UNKNOWN = "UNKNOWN"
|
||||
|
||||
|
||||
class TrendQuality(StrEnum):
|
||||
CLEAN = "CLEAN"
|
||||
NOISY = "NOISY"
|
||||
UNKNOWN = "UNKNOWN"
|
||||
|
||||
|
||||
class MarketPhase(StrEnum):
|
||||
IMPULSE = "IMPULSE"
|
||||
PULLBACK = "PULLBACK"
|
||||
RANGE = "RANGE"
|
||||
SQUEEZE = "SQUEEZE"
|
||||
UNKNOWN = "UNKNOWN"
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class MarketAnalysisResult:
|
||||
symbol: str
|
||||
@@ -50,3 +71,9 @@ class MarketAnalysisResult:
|
||||
is_trade_allowed: bool
|
||||
|
||||
payload: dict
|
||||
|
||||
trend_strength: TrendStrength
|
||||
trend_quality: TrendQuality
|
||||
market_phase: MarketPhase
|
||||
trend_gap_percent: float | None
|
||||
trend_consistency: float | None
|
||||
@@ -1,13 +1,14 @@
|
||||
# app/src/trading/market_analysis/service.py
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from src.integrations.exchange.service import ExchangeService
|
||||
from src.trading.market_analysis.indicators import atr, ema, rsi
|
||||
from src.trading.market_analysis.models import (
|
||||
MarketAnalysisResult,
|
||||
MarketPhase,
|
||||
MarketState,
|
||||
TrendDirection,
|
||||
TrendQuality,
|
||||
TrendStrength,
|
||||
VolatilityState,
|
||||
)
|
||||
|
||||
@@ -24,6 +25,7 @@ class MarketAnalysisService:
|
||||
_high_volatility_atr_percent = 1.8
|
||||
|
||||
_trend_gap_percent = 0.03
|
||||
_trend_consistency_window = 20
|
||||
|
||||
def analyze(
|
||||
self,
|
||||
@@ -77,12 +79,29 @@ class MarketAnalysisService:
|
||||
)
|
||||
|
||||
atr_percent = (atr_value / close_price) * 100
|
||||
trend_gap_percent = self._trend_gap_percent_value(
|
||||
ema_fast=ema_fast,
|
||||
ema_slow=ema_slow,
|
||||
)
|
||||
|
||||
volatility = self._classify_volatility(atr_percent)
|
||||
trend = self._classify_trend(
|
||||
ema_fast=ema_fast,
|
||||
ema_slow=ema_slow,
|
||||
)
|
||||
trend_strength = self._classify_trend_strength(trend_gap_percent)
|
||||
trend_consistency = self._trend_consistency(
|
||||
closes=closes,
|
||||
trend=trend,
|
||||
)
|
||||
trend_quality = self._classify_trend_quality(trend_consistency)
|
||||
market_phase = self._classify_market_phase(
|
||||
trend=trend,
|
||||
volatility=volatility,
|
||||
trend_strength=trend_strength,
|
||||
trend_quality=trend_quality,
|
||||
rsi_value=rsi_value,
|
||||
)
|
||||
|
||||
state = self._classify_market_state(
|
||||
trend=trend,
|
||||
@@ -123,6 +142,15 @@ class MarketAnalysisService:
|
||||
"market_state": state.value,
|
||||
"trend": trend.value,
|
||||
"volatility": volatility.value,
|
||||
"market_trend_strength": trend_strength.value,
|
||||
"market_trend_quality": trend_quality.value,
|
||||
"market_phase": market_phase.value,
|
||||
"market_trend_gap_percent": round(trend_gap_percent, 5)
|
||||
if trend_gap_percent is not None
|
||||
else None,
|
||||
"market_trend_consistency": round(trend_consistency, 3)
|
||||
if trend_consistency is not None
|
||||
else None,
|
||||
"close_price": close_price,
|
||||
"ema_fast_period": self._fast_ema_period,
|
||||
"ema_slow_period": self._slow_ema_period,
|
||||
@@ -136,18 +164,37 @@ class MarketAnalysisService:
|
||||
"candles_count": len(candles),
|
||||
"is_trade_allowed": is_trade_allowed,
|
||||
},
|
||||
trend_strength=trend_strength,
|
||||
trend_quality=trend_quality,
|
||||
market_phase=market_phase,
|
||||
trend_gap_percent=trend_gap_percent,
|
||||
trend_consistency=trend_consistency,
|
||||
)
|
||||
|
||||
def _trend_gap_percent_value(
|
||||
self,
|
||||
*,
|
||||
ema_fast: float,
|
||||
ema_slow: float,
|
||||
) -> float | None:
|
||||
if ema_slow <= 0:
|
||||
return None
|
||||
|
||||
return ((ema_fast - ema_slow) / ema_slow) * 100
|
||||
|
||||
def _classify_trend(
|
||||
self,
|
||||
*,
|
||||
ema_fast: float,
|
||||
ema_slow: float,
|
||||
) -> TrendDirection:
|
||||
if ema_slow <= 0:
|
||||
return TrendDirection.UNKNOWN
|
||||
gap_percent = self._trend_gap_percent_value(
|
||||
ema_fast=ema_fast,
|
||||
ema_slow=ema_slow,
|
||||
)
|
||||
|
||||
gap_percent = ((ema_fast - ema_slow) / ema_slow) * 100
|
||||
if gap_percent is None:
|
||||
return TrendDirection.UNKNOWN
|
||||
|
||||
if gap_percent >= self._trend_gap_percent:
|
||||
return TrendDirection.UP
|
||||
@@ -157,6 +204,99 @@ class MarketAnalysisService:
|
||||
|
||||
return TrendDirection.FLAT
|
||||
|
||||
def _classify_trend_strength(
|
||||
self,
|
||||
trend_gap_percent: float | None,
|
||||
) -> TrendStrength:
|
||||
if trend_gap_percent is None:
|
||||
return TrendStrength.UNKNOWN
|
||||
|
||||
gap = abs(trend_gap_percent)
|
||||
|
||||
if gap < 0.08:
|
||||
return TrendStrength.WEAK
|
||||
|
||||
if gap < 0.25:
|
||||
return TrendStrength.NORMAL
|
||||
|
||||
return TrendStrength.STRONG
|
||||
|
||||
def _trend_consistency(
|
||||
self,
|
||||
*,
|
||||
closes: list[float],
|
||||
trend: TrendDirection,
|
||||
) -> float | None:
|
||||
if len(closes) < 2:
|
||||
return None
|
||||
|
||||
window = closes[-self._trend_consistency_window :]
|
||||
if len(window) < 2:
|
||||
return None
|
||||
|
||||
up_moves = 0
|
||||
down_moves = 0
|
||||
|
||||
for previous_price, current_price in zip(window, window[1:]):
|
||||
if current_price > previous_price:
|
||||
up_moves += 1
|
||||
elif current_price < previous_price:
|
||||
down_moves += 1
|
||||
|
||||
total_moves = max(1, len(window) - 1)
|
||||
|
||||
if trend == TrendDirection.UP:
|
||||
return up_moves / total_moves
|
||||
|
||||
if trend == TrendDirection.DOWN:
|
||||
return down_moves / total_moves
|
||||
|
||||
return None
|
||||
|
||||
def _classify_trend_quality(
|
||||
self,
|
||||
trend_consistency: float | None,
|
||||
) -> TrendQuality:
|
||||
if trend_consistency is None:
|
||||
return TrendQuality.UNKNOWN
|
||||
|
||||
if trend_consistency >= 0.6:
|
||||
return TrendQuality.CLEAN
|
||||
|
||||
return TrendQuality.NOISY
|
||||
|
||||
def _classify_market_phase(
|
||||
self,
|
||||
*,
|
||||
trend: TrendDirection,
|
||||
volatility: VolatilityState,
|
||||
trend_strength: TrendStrength,
|
||||
trend_quality: TrendQuality,
|
||||
rsi_value: float | None,
|
||||
) -> MarketPhase:
|
||||
if volatility == VolatilityState.LOW:
|
||||
return MarketPhase.SQUEEZE
|
||||
|
||||
if trend == TrendDirection.FLAT:
|
||||
return MarketPhase.RANGE
|
||||
|
||||
if trend not in {TrendDirection.UP, TrendDirection.DOWN}:
|
||||
return MarketPhase.UNKNOWN
|
||||
|
||||
if trend_strength == TrendStrength.WEAK:
|
||||
return MarketPhase.RANGE
|
||||
|
||||
if trend_quality == TrendQuality.NOISY:
|
||||
return MarketPhase.PULLBACK
|
||||
|
||||
if trend == TrendDirection.UP and rsi_value is not None and rsi_value < 45:
|
||||
return MarketPhase.PULLBACK
|
||||
|
||||
if trend == TrendDirection.DOWN and rsi_value is not None and rsi_value > 55:
|
||||
return MarketPhase.PULLBACK
|
||||
|
||||
return MarketPhase.IMPULSE
|
||||
|
||||
def _classify_volatility(self, atr_percent: float) -> VolatilityState:
|
||||
if atr_percent <= 0:
|
||||
return VolatilityState.UNKNOWN
|
||||
@@ -249,8 +389,18 @@ class MarketAnalysisService:
|
||||
"market_state": MarketState.UNKNOWN.value,
|
||||
"trend": TrendDirection.UNKNOWN.value,
|
||||
"volatility": VolatilityState.UNKNOWN.value,
|
||||
"market_trend_strength": TrendStrength.UNKNOWN.value,
|
||||
"market_trend_quality": TrendQuality.UNKNOWN.value,
|
||||
"market_phase": MarketPhase.UNKNOWN.value,
|
||||
"market_trend_gap_percent": None,
|
||||
"market_trend_consistency": None,
|
||||
"candles_count": candles_count,
|
||||
"is_trade_allowed": False,
|
||||
"reason": reason,
|
||||
},
|
||||
trend_strength=TrendStrength.UNKNOWN,
|
||||
trend_quality=TrendQuality.UNKNOWN,
|
||||
market_phase=MarketPhase.UNKNOWN,
|
||||
trend_gap_percent=None,
|
||||
trend_consistency=None,
|
||||
)
|
||||
@@ -5,7 +5,12 @@ from __future__ import annotations
|
||||
import time
|
||||
|
||||
from src.integrations.exchange.service import ExchangeService
|
||||
from src.trading.market_analysis.models import MarketState
|
||||
from src.trading.market_analysis.models import (
|
||||
MarketPhase,
|
||||
MarketState,
|
||||
TrendQuality,
|
||||
TrendStrength,
|
||||
)
|
||||
from src.trading.market_analysis.service import MarketAnalysisService
|
||||
from src.trading.strategies.base import StrategyContext
|
||||
from src.trading.strategies.signals import SignalResult, SignalType
|
||||
@@ -116,6 +121,11 @@ class TrendStrategy:
|
||||
"market_analysis_interval": market.interval,
|
||||
"market_analysis_reason": market.reason,
|
||||
"market_analysis": market.payload,
|
||||
"market_trend_strength": market.trend_strength.value,
|
||||
"market_trend_quality": market.trend_quality.value,
|
||||
"market_phase": market.market_phase.value,
|
||||
"market_trend_gap_percent": market.trend_gap_percent,
|
||||
"market_trend_consistency": market.trend_consistency,
|
||||
"runtime_window_ttl_seconds": self._window_ttl_seconds,
|
||||
"runtime_window_size": len(prices),
|
||||
}
|
||||
@@ -133,6 +143,42 @@ class TrendStrategy:
|
||||
},
|
||||
)
|
||||
|
||||
if market.trend_strength == TrendStrength.WEAK:
|
||||
return SignalResult(
|
||||
signal=SignalType.HOLD,
|
||||
reason="TREND есть, но сила тренда слабая.",
|
||||
confidence=0.0,
|
||||
payload={
|
||||
**base_payload,
|
||||
"entry_block_reason": "WEAK_MARKET_TREND",
|
||||
"entry_block_message": "слабый тренд",
|
||||
},
|
||||
)
|
||||
|
||||
if market.trend_quality == TrendQuality.NOISY:
|
||||
return SignalResult(
|
||||
signal=SignalType.HOLD,
|
||||
reason="TREND есть, но движение шумное.",
|
||||
confidence=0.0,
|
||||
payload={
|
||||
**base_payload,
|
||||
"entry_block_reason": "NOISY_MARKET_TREND",
|
||||
"entry_block_message": "шумный тренд",
|
||||
},
|
||||
)
|
||||
|
||||
if market.market_phase == MarketPhase.PULLBACK:
|
||||
return SignalResult(
|
||||
signal=SignalType.HOLD,
|
||||
reason="TREND есть, но рынок находится в откате.",
|
||||
confidence=0.0,
|
||||
payload={
|
||||
**base_payload,
|
||||
"entry_block_reason": "MARKET_PULLBACK",
|
||||
"entry_block_message": "откат",
|
||||
},
|
||||
)
|
||||
|
||||
if len(prices) < self._window_size:
|
||||
return SignalResult(
|
||||
signal=SignalType.HOLD,
|
||||
|
||||
@@ -635,6 +635,55 @@
|
||||
- подготовлена база для volatility-aware spread thresholds
|
||||
- подготовлена база для adaptive execution quality model
|
||||
|
||||
#### 07.4.4.1.9 ✅ Adaptive Market Diagnostics Layer
|
||||
- добавлен расширенный слой диагностики рынка
|
||||
- добавлены enum-модели TrendStrength, TrendQuality и MarketPhase
|
||||
- MarketAnalysisResult расширен полями trend_strength, trend_quality и market_phase
|
||||
- MarketAnalysisResult расширен полями trend_gap_percent и trend_consistency
|
||||
- MarketAnalysisService получил расчёт EMA gap в процентах
|
||||
- добавлен анализ силы тренда по EMA gap
|
||||
- добавлена классификация WEAK / NORMAL / STRONG trend
|
||||
- добавлен анализ trend consistency по последним свечам
|
||||
- добавлена классификация CLEAN / NOISY trend
|
||||
- добавлена классификация market phase
|
||||
- добавлены фазы IMPULSE / PULLBACK / RANGE / SQUEEZE / UNKNOWN
|
||||
- LOW volatility теперь интерпретируется как SQUEEZE phase
|
||||
- FLAT trend теперь интерпретируется как RANGE phase
|
||||
- слабый тренд теперь может блокировать TREND вход
|
||||
- шумный тренд теперь может блокировать TREND вход
|
||||
- откат внутри тренда теперь может блокировать TREND вход
|
||||
- TrendStrategy получила поддержку adaptive market diagnostics
|
||||
- TrendStrategy пробрасывает market_trend_strength в payload
|
||||
- TrendStrategy пробрасывает market_trend_quality в payload
|
||||
- TrendStrategy пробрасывает market_phase в payload
|
||||
- TrendStrategy пробрасывает market_trend_gap_percent в payload
|
||||
- TrendStrategy пробрасывает market_trend_consistency в payload
|
||||
- добавлен HOLD reason WEAK_MARKET_TREND
|
||||
- добавлен HOLD reason NOISY_MARKET_TREND
|
||||
- добавлен HOLD reason MARKET_PULLBACK
|
||||
- AutoTradeState расширен market_trend_strength
|
||||
- AutoTradeState расширен market_trend_quality
|
||||
- AutoTradeState расширен market_phase
|
||||
- reset runtime очищает новые market diagnostics поля
|
||||
- sync market analysis обновляет новые market diagnostics поля
|
||||
- Telegram UI получил строку расширенной аналитики
|
||||
- Telegram UI отображает силу тренда
|
||||
- Telegram UI отображает качество тренда
|
||||
- Telegram UI отображает фазу рынка
|
||||
- HOLD diagnostics стали точнее
|
||||
- причина HOLD теперь показывает слабый тренд
|
||||
- причина HOLD теперь показывает шумный тренд
|
||||
- причина HOLD теперь показывает откат
|
||||
- исправлен auto_run_cycle_error после расширения MarketAnalysisResult
|
||||
- исправлено зависание market state в “Идёт анализ”
|
||||
- подтверждена работа live runtime после расширения аналитики
|
||||
- подготовлена база для Market Semantic Runtime Layer
|
||||
- подготовлена база для compact semantic UI labels
|
||||
- подготовлена база для adaptive thresholds
|
||||
- подготовлена база для semantic entry filters
|
||||
- подготовлена база для более точного TREND execution
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 07.4.5
|
||||
|
||||
@@ -611,6 +611,54 @@
|
||||
- подготовлена база для volatility-aware spread thresholds
|
||||
- подготовлена база для adaptive execution quality model
|
||||
|
||||
#### 07.4.4.1.9 ✅ Adaptive Market Diagnostics Layer
|
||||
- добавлен расширенный слой диагностики рынка
|
||||
- добавлены enum-модели TrendStrength, TrendQuality и MarketPhase
|
||||
- MarketAnalysisResult расширен полями trend_strength, trend_quality и market_phase
|
||||
- MarketAnalysisResult расширен полями trend_gap_percent и trend_consistency
|
||||
- MarketAnalysisService получил расчёт EMA gap в процентах
|
||||
- добавлен анализ силы тренда по EMA gap
|
||||
- добавлена классификация WEAK / NORMAL / STRONG trend
|
||||
- добавлен анализ trend consistency по последним свечам
|
||||
- добавлена классификация CLEAN / NOISY trend
|
||||
- добавлена классификация market phase
|
||||
- добавлены фазы IMPULSE / PULLBACK / RANGE / SQUEEZE / UNKNOWN
|
||||
- LOW volatility теперь интерпретируется как SQUEEZE phase
|
||||
- FLAT trend теперь интерпретируется как RANGE phase
|
||||
- слабый тренд теперь может блокировать TREND вход
|
||||
- шумный тренд теперь может блокировать TREND вход
|
||||
- откат внутри тренда теперь может блокировать TREND вход
|
||||
- TrendStrategy получила поддержку adaptive market diagnostics
|
||||
- TrendStrategy пробрасывает market_trend_strength в payload
|
||||
- TrendStrategy пробрасывает market_trend_quality в payload
|
||||
- TrendStrategy пробрасывает market_phase в payload
|
||||
- TrendStrategy пробрасывает market_trend_gap_percent в payload
|
||||
- TrendStrategy пробрасывает market_trend_consistency в payload
|
||||
- добавлен HOLD reason WEAK_MARKET_TREND
|
||||
- добавлен HOLD reason NOISY_MARKET_TREND
|
||||
- добавлен HOLD reason MARKET_PULLBACK
|
||||
- AutoTradeState расширен market_trend_strength
|
||||
- AutoTradeState расширен market_trend_quality
|
||||
- AutoTradeState расширен market_phase
|
||||
- reset runtime очищает новые market diagnostics поля
|
||||
- sync market analysis обновляет новые market diagnostics поля
|
||||
- Telegram UI получил строку расширенной аналитики
|
||||
- Telegram UI отображает силу тренда
|
||||
- Telegram UI отображает качество тренда
|
||||
- Telegram UI отображает фазу рынка
|
||||
- HOLD diagnostics стали точнее
|
||||
- причина HOLD теперь показывает слабый тренд
|
||||
- причина HOLD теперь показывает шумный тренд
|
||||
- причина HOLD теперь показывает откат
|
||||
- исправлен auto_run_cycle_error после расширения MarketAnalysisResult
|
||||
- исправлено зависание market state в “Идёт анализ”
|
||||
- подтверждена работа live runtime после расширения аналитики
|
||||
- подготовлена база для Market Semantic Runtime Layer
|
||||
- подготовлена база для compact semantic UI labels
|
||||
- подготовлена база для adaptive thresholds
|
||||
- подготовлена база для semantic entry filters
|
||||
- подготовлена база для более точного TREND execution
|
||||
|
||||
---
|
||||
|
||||
### 07.4.5
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# 07.4.4.1.9 Adaptive Market Diagnostics Layer
|
||||
|
||||
## Что сделано
|
||||
|
||||
Добавлен расширенный слой диагностики рынка поверх базового Market State Engine.
|
||||
|
||||
Теперь система анализирует не только направление рынка:
|
||||
|
||||
- TREND_UP
|
||||
- TREND_DOWN
|
||||
- RANGE
|
||||
- HIGH_VOLATILITY
|
||||
- LOW_VOLATILITY
|
||||
|
||||
но и дополнительные характеристики тренда:
|
||||
|
||||
- сила тренда
|
||||
- качество тренда
|
||||
- текущая фаза рынка
|
||||
- процентный разрыв EMA
|
||||
- consistency движения
|
||||
|
||||
## Новые сущности
|
||||
|
||||
Добавлены новые enum-модели:
|
||||
|
||||
- TrendStrength
|
||||
- WEAK
|
||||
- NORMAL
|
||||
- STRONG
|
||||
- UNKNOWN
|
||||
|
||||
- TrendQuality
|
||||
- CLEAN
|
||||
- NOISY
|
||||
- UNKNOWN
|
||||
|
||||
- MarketPhase
|
||||
- IMPULSE
|
||||
- PULLBACK
|
||||
- RANGE
|
||||
- SQUEEZE
|
||||
- UNKNOWN
|
||||
|
||||
## MarketAnalysisResult
|
||||
|
||||
MarketAnalysisResult расширен новыми полями:
|
||||
|
||||
- trend_strength
|
||||
- trend_quality
|
||||
- market_phase
|
||||
- trend_gap_percent
|
||||
- trend_consistency
|
||||
|
||||
## TrendStrategy
|
||||
|
||||
TrendStrategy теперь получает расширенную аналитику из MarketAnalysisService и пробрасывает её в payload:
|
||||
|
||||
- market_trend_strength
|
||||
- market_trend_quality
|
||||
- market_phase
|
||||
- market_trend_gap_percent
|
||||
- market_trend_consistency
|
||||
|
||||
Добавлены дополнительные HOLD-фильтры:
|
||||
|
||||
- WEAK_MARKET_TREND
|
||||
- NOISY_MARKET_TREND
|
||||
- MARKET_PULLBACK
|
||||
|
||||
## Telegram UI
|
||||
|
||||
В UI добавлена строка расширенной аналитики:
|
||||
|
||||
Анализ · сильный · шумный · откат
|
||||
|
||||
или:
|
||||
|
||||
Анализ · нормальный · чистый · импульс
|
||||
|
||||
## Что исправлено
|
||||
|
||||
Исправлена ошибка auto_run_cycle_error после расширения MarketAnalysisResult.
|
||||
|
||||
После исправления:
|
||||
|
||||
- run_cycle больше не падает
|
||||
- рынок больше не зависает в состоянии “Идёт анализ”
|
||||
- Telegram UI снова получает актуальную market diagnostics
|
||||
- HOLD timer продолжает работать
|
||||
|
||||
## Проверка
|
||||
|
||||
Команды:
|
||||
|
||||
python -m compileall src
|
||||
|
||||
Runtime-проверка:
|
||||
|
||||
- автоторговля запускается
|
||||
- экран обновляется автоматически
|
||||
- HOLD timer растёт
|
||||
- market state отображается корректно
|
||||
- строка Анализ появляется
|
||||
- ошибки auto_run_cycle_error отсутствуют
|
||||
|
||||
## Результат
|
||||
|
||||
Этап подготовил базу для:
|
||||
|
||||
- Market Semantic Runtime Layer
|
||||
- semantic UI labels
|
||||
- adaptive thresholds
|
||||
- semantic entry filters
|
||||
Reference in New Issue
Block a user