feat: 07.4.1 base strategy + 07.4.2 strategy registry + docs sync

This commit is contained in:
2026-04-29 23:26:46 +03:00
parent 7c8895c3a5
commit 80f29443d4
22 changed files with 961 additions and 237 deletions

View File

@@ -0,0 +1 @@
"""Package marker."""

View File

@@ -0,0 +1,29 @@
# app/src/trading/strategies/base.py
from __future__ import annotations
from dataclasses import dataclass
from typing import Protocol
from src.trading.strategies.signals import SignalResult
@dataclass(slots=True)
class StrategyContext:
# выбранный торговый инструмент
symbol: str
# текущий режим автоторговли: OBSERVING / RUNNING
status: str
# риск на сделку в процентах
risk_percent: float | None = None
class BaseStrategy(Protocol):
# техническое имя стратегии
name: str
# выполнить анализ и вернуть торговый сигнал
def analyze(self, context: StrategyContext) -> SignalResult:
...

View File

@@ -0,0 +1,23 @@
# app/src/trading/strategies/hold.py
from __future__ import annotations
from src.trading.strategies.base import StrategyContext
from src.trading.strategies.signals import SignalResult, SignalType
class HoldStrategy:
name = "HOLD"
# безопасная стратегия по умолчанию: ничего не делает
def analyze(self, context: StrategyContext) -> SignalResult:
return SignalResult(
signal=SignalType.HOLD,
reason="Стратегия не выбрана. Используется безопасный HOLD.",
confidence=0.0,
payload={
"symbol": context.symbol,
"status": context.status,
"strategy": self.name,
},
)

View File

@@ -0,0 +1,30 @@
# app/src/trading/strategies/registry.py
from __future__ import annotations
from src.trading.strategies.base import BaseStrategy
from src.trading.strategies.hold import HoldStrategy
class StrategyRegistry:
# доступные стратегии
_strategies: dict[str, BaseStrategy] = {
"HOLD": HoldStrategy(),
"TREND": HoldStrategy(),
"GRID": HoldStrategy(),
"SCALP": HoldStrategy(),
}
# получить стратегию по имени
@classmethod
def get(cls, name: str | None) -> BaseStrategy:
if not name:
return cls._strategies["HOLD"]
normalized_name = name.strip().upper()
return cls._strategies.get(normalized_name, cls._strategies["HOLD"])
# получить список доступных стратегий
@classmethod
def names(cls) -> list[str]:
return sorted(cls._strategies.keys())

View File

@@ -0,0 +1,32 @@
# app/src/trading/strategies/signals.py
from __future__ import annotations
from dataclasses import dataclass
from enum import StrEnum
class SignalType(StrEnum):
# купить
BUY = "BUY"
# продать
SELL = "SELL"
# ничего не делать
HOLD = "HOLD"
@dataclass(slots=True)
class SignalResult:
# итоговый сигнал стратегии
signal: SignalType
# человекочитаемая причина сигнала
reason: str
# уверенность стратегии от 0.0 до 1.0
confidence: float = 0.0
# дополнительные данные стратегии для логов / отладки
payload: dict | None = None