Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/tinkoff/invest/utils.py
7826 views
import ast1import dataclasses2from datetime import datetime, timedelta, timezone3from decimal import Decimal4from typing import Generator, Tuple56import dateutil.parser78from .schemas import CandleInterval, Quotation, SubscriptionInterval910__all__ = ("get_intervals",)1112DAYS_IN_YEAR = 365131415MAX_INTERVALS = {16CandleInterval.CANDLE_INTERVAL_1_MIN: timedelta(days=1),17CandleInterval.CANDLE_INTERVAL_5_MIN: timedelta(days=1),18CandleInterval.CANDLE_INTERVAL_15_MIN: timedelta(days=1),19CandleInterval.CANDLE_INTERVAL_HOUR: timedelta(weeks=1),20CandleInterval.CANDLE_INTERVAL_DAY: timedelta(days=DAYS_IN_YEAR),21}222324def get_intervals(25interval: CandleInterval, from_: datetime, to: datetime26) -> Generator[Tuple[datetime, datetime], None, None]:27max_interval = MAX_INTERVALS[interval]28local_from = from_29interval_timedelta = candle_interval_to_timedelta(interval)30while local_from + interval_timedelta <= to:31yield local_from, min(local_from + max_interval, to)32local_from += max_interval333435def quotation_to_decimal(quotation: Quotation) -> Decimal:36fractional = quotation.nano / Decimal("10e8")37return Decimal(quotation.units) + fractional383940def decimal_to_quotation(decimal: Decimal) -> Quotation:41fractional = decimal % 142return Quotation(units=int(decimal // 1), nano=int(fractional * Decimal("10e8")))434445# fmt: off46_CANDLE_INTERVAL_TO_SUBSCRIPTION_INTERVAL_MAPPING = {47CandleInterval.CANDLE_INTERVAL_1_MIN:48SubscriptionInterval.SUBSCRIPTION_INTERVAL_ONE_MINUTE,49CandleInterval.CANDLE_INTERVAL_5_MIN:50SubscriptionInterval.SUBSCRIPTION_INTERVAL_FIVE_MINUTES,51CandleInterval.CANDLE_INTERVAL_UNSPECIFIED:52SubscriptionInterval.SUBSCRIPTION_INTERVAL_UNSPECIFIED,53}54# fmt: on555657def candle_interval_to_subscription_interval(58candle_interval: CandleInterval,59) -> SubscriptionInterval:60return _CANDLE_INTERVAL_TO_SUBSCRIPTION_INTERVAL_MAPPING.get(61candle_interval, SubscriptionInterval.SUBSCRIPTION_INTERVAL_UNSPECIFIED62)636465def now() -> datetime:66return datetime.utcnow().replace(tzinfo=timezone.utc)676869_CANDLE_INTERVAL_TO_TIMEDELTA_MAPPING = {70CandleInterval.CANDLE_INTERVAL_1_MIN: timedelta(minutes=1),71CandleInterval.CANDLE_INTERVAL_5_MIN: timedelta(minutes=5),72CandleInterval.CANDLE_INTERVAL_15_MIN: timedelta(minutes=15),73CandleInterval.CANDLE_INTERVAL_HOUR: timedelta(hours=1),74CandleInterval.CANDLE_INTERVAL_DAY: timedelta(days=1),75CandleInterval.CANDLE_INTERVAL_UNSPECIFIED: timedelta(minutes=1),76}777879def candle_interval_to_timedelta(candle_interval: CandleInterval) -> timedelta:80if delta := _CANDLE_INTERVAL_TO_TIMEDELTA_MAPPING.get(candle_interval):81return delta82raise ValueError(f"Cannot convert {candle_interval} to timedelta")838485_DATETIME_MIN = datetime.min.replace(tzinfo=timezone.utc)868788def ceil_datetime(datetime_: datetime, delta: timedelta):89return datetime_ + (_DATETIME_MIN - datetime_) % delta909192def floor_datetime(datetime_: datetime, delta: timedelta):93return datetime_ - (datetime_ - _DATETIME_MIN) % delta949596def dataclass_from_dict(klass, d):97if issubclass(int, klass):98return int(d)99if issubclass(bool, klass):100return bool(d)101if issubclass(klass, datetime):102return dateutil.parser.parse(d).replace(tzinfo=timezone.utc)103if issubclass(klass, Quotation):104d = ast.literal_eval(d)105fieldtypes = {f.name: f.type for f in dataclasses.fields(klass)}106return klass(**{f: dataclass_from_dict(fieldtypes[f], d[f]) for f in d})107108109def datetime_range_floor(110date_range: Tuple[datetime, datetime]111) -> Tuple[datetime, datetime]:112start, end = date_range113return start.replace(second=0, microsecond=0), end.replace(second=0, microsecond=0)114115116