RAPTOR v18.4: Исправлена отчетность, активированы выходные
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
import logging
|
||||
import time
|
||||
from typing import Iterator, List
|
||||
|
||||
import t_tech
|
||||
from t_tech.invest import (
|
||||
CandleInstrument,
|
||||
InvestError,
|
||||
MarketDataRequest,
|
||||
MarketDataResponse,
|
||||
SubscribeCandlesRequest,
|
||||
SubscriptionAction,
|
||||
SubscriptionInterval,
|
||||
)
|
||||
from t_tech.invest.services import Services
|
||||
from t_tech.invest.strategies.base.account_manager import AccountManager
|
||||
from t_tech.invest.strategies.base.errors import MarketDataNotAvailableError
|
||||
from t_tech.invest.strategies.base.event import DataEvent, SignalEvent
|
||||
from t_tech.invest.strategies.base.models import CandleEvent
|
||||
from t_tech.invest.strategies.base.signal import CloseSignal, OpenSignal, Signal
|
||||
from t_tech.invest.strategies.base.signal_executor_base import SignalExecutor
|
||||
from t_tech.invest.strategies.base.trader_base import Trader
|
||||
from t_tech.invest.strategies.moving_average.strategy import MovingAverageStrategy
|
||||
from t_tech.invest.strategies.moving_average.strategy_settings import (
|
||||
MovingAverageStrategySettings,
|
||||
)
|
||||
from t_tech.invest.strategies.moving_average.strategy_state import (
|
||||
MovingAverageStrategyState,
|
||||
)
|
||||
from t_tech.invest.strategies.moving_average.supervisor import (
|
||||
MovingAverageStrategySupervisor,
|
||||
)
|
||||
from t_tech.invest.utils import floor_datetime, now
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MovingAverageStrategyTrader(Trader):
|
||||
def __init__(
|
||||
self,
|
||||
strategy: MovingAverageStrategy,
|
||||
settings: MovingAverageStrategySettings,
|
||||
services: Services,
|
||||
state: MovingAverageStrategyState,
|
||||
signal_executor: SignalExecutor,
|
||||
account_manager: AccountManager,
|
||||
supervisor: MovingAverageStrategySupervisor,
|
||||
):
|
||||
super().__init__(strategy, services, settings)
|
||||
self._settings: MovingAverageStrategySettings = settings
|
||||
self._strategy = strategy
|
||||
self._services = services
|
||||
self._data: List[CandleEvent]
|
||||
self._market_data_stream: Iterator[MarketDataResponse]
|
||||
self._state = state
|
||||
self._signal_executor = signal_executor
|
||||
self._account_manager = account_manager
|
||||
self._supervisor = supervisor
|
||||
|
||||
self._data = list(
|
||||
self._load_candles(
|
||||
(self._settings.short_period + self._settings.long_period) * 3
|
||||
)
|
||||
)
|
||||
for candle_event in self._data:
|
||||
self._supervisor.notify(self._convert_to_data_event(candle_event))
|
||||
self._strategy.fit(self._data)
|
||||
|
||||
self._ensure_marginal_trade_active()
|
||||
self._subscribe()
|
||||
|
||||
def _ensure_marginal_trade_active(self) -> None:
|
||||
self._account_manager.ensure_marginal_trade()
|
||||
|
||||
def _subscribe(self):
|
||||
current_instrument = CandleInstrument(
|
||||
figi=self._settings.share_id,
|
||||
interval=SubscriptionInterval.SUBSCRIPTION_INTERVAL_ONE_MINUTE,
|
||||
)
|
||||
candle_subscribe_request = MarketDataRequest(
|
||||
subscribe_candles_request=SubscribeCandlesRequest(
|
||||
subscription_action=SubscriptionAction.SUBSCRIPTION_ACTION_SUBSCRIBE,
|
||||
instruments=[current_instrument],
|
||||
)
|
||||
)
|
||||
|
||||
def request_iterator():
|
||||
yield candle_subscribe_request
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
self._market_data_stream = self._services.market_data_stream.market_data_stream(
|
||||
request_iterator()
|
||||
)
|
||||
|
||||
def _is_candle_fresh(self, candle: t_tech.invest.Candle) -> bool:
|
||||
is_fresh_border = floor_datetime(
|
||||
now(), delta=self._settings.candle_interval_timedelta
|
||||
)
|
||||
logger.debug(
|
||||
"Checking if candle is fresh: candle.time=%s > is_fresh_border=%s %s)",
|
||||
candle.time,
|
||||
is_fresh_border,
|
||||
candle.time >= is_fresh_border,
|
||||
)
|
||||
return candle.time >= is_fresh_border
|
||||
|
||||
@staticmethod
|
||||
def _convert_to_data_event(candle_event: CandleEvent) -> DataEvent:
|
||||
return DataEvent(candle_event=candle_event, time=candle_event.time)
|
||||
|
||||
def _make_observations(self) -> None:
|
||||
while True:
|
||||
market_data_response: MarketDataResponse = next(self._market_data_stream)
|
||||
logger.debug("got market_data_response: %s", market_data_response)
|
||||
if market_data_response.candle is None:
|
||||
logger.debug("market_data_response didn't have candle")
|
||||
continue
|
||||
candle = market_data_response.candle
|
||||
logger.debug("candle extracted: %s", candle)
|
||||
candle_event = self._convert_candle(candle)
|
||||
self._strategy.observe(candle_event)
|
||||
self._supervisor.notify(self._convert_to_data_event(candle_event))
|
||||
if self._is_candle_fresh(candle):
|
||||
logger.info("Data refreshed")
|
||||
break
|
||||
|
||||
def _refresh_data(self) -> None:
|
||||
logger.info("Refreshing data")
|
||||
try:
|
||||
self._make_observations()
|
||||
except StopIteration as e:
|
||||
logger.info("Fresh quotations not available")
|
||||
raise MarketDataNotAvailableError() from e
|
||||
|
||||
def _filter_closing_signals(self, signals: List[Signal]) -> List[Signal]:
|
||||
return list(filter(lambda signal: isinstance(signal, CloseSignal), signals))
|
||||
|
||||
def _filter_opening_signals(self, signals: List[Signal]) -> List[Signal]:
|
||||
return list(filter(lambda signal: isinstance(signal, OpenSignal), signals))
|
||||
|
||||
def _execute(self, signal: Signal) -> None:
|
||||
logger.info("Trying to execute signal %s", signal)
|
||||
try:
|
||||
self._signal_executor.execute(signal) # type: ignore
|
||||
except InvestError:
|
||||
was_executed = False
|
||||
else:
|
||||
was_executed = True
|
||||
self._supervisor.notify(
|
||||
SignalEvent(signal=signal, was_executed=was_executed, time=now())
|
||||
)
|
||||
|
||||
def _get_signals(self) -> List[Signal]:
|
||||
signals = list(self._strategy.predict())
|
||||
return [
|
||||
*self._filter_closing_signals(signals),
|
||||
*self._filter_opening_signals(signals),
|
||||
]
|
||||
|
||||
def trade(self) -> None:
|
||||
"""Делает попытку следовать стратегии."""
|
||||
logger.info("Balance: %s", self._account_manager.get_current_balance())
|
||||
self._refresh_data()
|
||||
|
||||
signals = self._get_signals()
|
||||
if signals:
|
||||
logger.info("Got signals %s", signals)
|
||||
for signal in signals:
|
||||
self._execute(signal)
|
||||
if self._state.position == 0:
|
||||
logger.info("Trade try complete")
|
||||
Reference in New Issue
Block a user