91 lines
3.3 KiB
Python
91 lines
3.3 KiB
Python
# app/src/trading/strategies/trend.py
|
||
|
||
from __future__ import annotations
|
||
|
||
from src.integrations.exchange.service import ExchangeService
|
||
from src.trading.strategies.base import StrategyContext
|
||
from src.trading.strategies.signals import SignalResult, SignalType
|
||
|
||
|
||
class TrendStrategy:
|
||
name = "TREND"
|
||
|
||
_last_prices: dict[str, float] = {}
|
||
_threshold_percent = 0.02
|
||
|
||
# анализ простого тренда по изменению цены
|
||
def analyze(self, context: StrategyContext) -> SignalResult:
|
||
try:
|
||
ticker = ExchangeService().get_price(context.symbol)
|
||
except Exception as exc:
|
||
return SignalResult(
|
||
signal=SignalType.HOLD,
|
||
reason="Не удалось получить рыночную цену. Безопасный HOLD.",
|
||
confidence=0.0,
|
||
payload={
|
||
"strategy": self.name,
|
||
"symbol": context.symbol,
|
||
"error": str(exc),
|
||
},
|
||
)
|
||
|
||
symbol = ticker.symbol
|
||
current_price = ticker.price
|
||
previous_price = self._last_prices.get(symbol)
|
||
|
||
self._last_prices[symbol] = current_price
|
||
|
||
if previous_price is None or previous_price <= 0:
|
||
return SignalResult(
|
||
signal=SignalType.HOLD,
|
||
reason="Недостаточно данных для определения тренда.",
|
||
confidence=0.0,
|
||
payload={
|
||
"strategy": self.name,
|
||
"symbol": symbol,
|
||
"price": current_price,
|
||
},
|
||
)
|
||
|
||
change_percent = ((current_price - previous_price) / previous_price) * 100
|
||
|
||
if change_percent >= self._threshold_percent:
|
||
return SignalResult(
|
||
signal=SignalType.BUY,
|
||
reason="Цена растёт выше порога тренда.",
|
||
confidence=min(1.0, abs(change_percent) / self._threshold_percent),
|
||
payload={
|
||
"strategy": self.name,
|
||
"symbol": symbol,
|
||
"previous_price": previous_price,
|
||
"current_price": current_price,
|
||
"change_percent": round(change_percent, 5),
|
||
},
|
||
)
|
||
|
||
if change_percent <= -self._threshold_percent:
|
||
return SignalResult(
|
||
signal=SignalType.SELL,
|
||
reason="Цена падает ниже порога тренда.",
|
||
confidence=min(1.0, abs(change_percent) / self._threshold_percent),
|
||
payload={
|
||
"strategy": self.name,
|
||
"symbol": symbol,
|
||
"previous_price": previous_price,
|
||
"current_price": current_price,
|
||
"change_percent": round(change_percent, 5),
|
||
},
|
||
)
|
||
|
||
return SignalResult(
|
||
signal=SignalType.HOLD,
|
||
reason="Изменение цены ниже порога тренда.",
|
||
confidence=0.0,
|
||
payload={
|
||
"strategy": self.name,
|
||
"symbol": symbol,
|
||
"previous_price": previous_price,
|
||
"current_price": current_price,
|
||
"change_percent": round(change_percent, 5),
|
||
},
|
||
) |