diff --git a/tastytrade/dxfeed/candle.py b/tastytrade/dxfeed/candle.py index 6e36e36..73cb4a7 100644 --- a/tastytrade/dxfeed/candle.py +++ b/tastytrade/dxfeed/candle.py @@ -1,18 +1,20 @@ from decimal import Decimal from typing import Optional -from .event import Event +from pydantic import Field, computed_field +from .event import IndexedEvent -class Candle(Event): +ZERO = Decimal(0) + + +class Candle(IndexedEvent): """ A Candle event with open, high, low, close prices and other information for a specific period. Candles are build with a specified period using a specified price type with data taken from a specified exchange. """ - #: transactional event flags - event_flags: int #: unique per-symbol index of this candle event index: int #: timestamp of the candle in milliseconds @@ -21,14 +23,6 @@ class Candle(Event): sequence: int #: total number of events in the candle count: int - #: the first (open) price of the candle - open: Decimal - #: the maximal (high) price of the candle - high: Decimal - #: the minimal (low) price of the candle - low: Decimal - #: the last (close) price of the candle - close: Decimal #: the total volume of the candle volume: Optional[Decimal] = None #: volume-weighted average price @@ -41,3 +35,40 @@ class Candle(Event): imp_volatility: Optional[Decimal] = None #: open interest in the candle open_interest: Optional[int] = None + # these fields will not show up in serialization + raw_open: Optional[Decimal] = Field(validation_alias="open", exclude=True) + raw_high: Optional[Decimal] = Field(validation_alias="high", exclude=True) + raw_low: Optional[Decimal] = Field(validation_alias="low", exclude=True) + raw_close: Optional[Decimal] = Field(validation_alias="close", exclude=True) + + @computed_field + @property + def open(self) -> Decimal: + """ + the first (open) price of the candle + """ + return self.raw_open or ZERO + + @computed_field + @property + def high(self) -> Decimal: + """ + the maximal (high) price of the candle + """ + return self.raw_high or ZERO + + @computed_field + @property + def low(self) -> Decimal: + """ + the minimal (low) price of the candle + """ + return self.raw_low or ZERO + + @computed_field + @property + def close(self) -> Decimal: + """ + the last (close) price of the candle + """ + return self.raw_close or ZERO diff --git a/tastytrade/dxfeed/event.py b/tastytrade/dxfeed/event.py index 37912bf..e43c86f 100644 --- a/tastytrade/dxfeed/event.py +++ b/tastytrade/dxfeed/event.py @@ -6,8 +6,19 @@ from tastytrade import logger from tastytrade.utils import TastytradeError +REMOVE_EVENT = 0x2 +SNAPSHOT_BEGIN = 0x4 +SNAPSHOT_END = 0x8 +SNAPSHOT_MODE = 0x40 +SNAPSHOT_SNIP = 0x10 +TX_PENDING = 0x1 + class Event(BaseModel): + """ + Base class for dxfeed events received from the data streamer. + """ + #: symbol of this event event_symbol: str #: time of this event @@ -47,6 +58,40 @@ def from_stream(cls, data: list) -> List["Event"]: try: objs.append(cls(**event_dict)) except ValidationError as e: - # we just skip these events as they're generally useless - logger.debug(e) + # we just skip these events as they're generally not helpful + logger.debug(f"Skipping event due to error: {e}") return objs + + +class IndexedEvent(Event): + """ + A dxfeed `IndexedEvent` with flags computed bitwise. + For info see `here `_. + """ + + #: flags for the event + event_flags: int + + @property + def pending(self) -> bool: + return self.event_flags & TX_PENDING != 0 + + @property + def remove(self) -> bool: + return self.event_flags & REMOVE_EVENT != 0 + + @property + def snapshot_begin(self) -> bool: + return self.event_flags & SNAPSHOT_BEGIN != 0 + + @property + def snapshot_end(self) -> bool: + return self.event_flags & SNAPSHOT_END != 0 + + @property + def snapshot_mode(self) -> bool: + return self.event_flags & SNAPSHOT_MODE != 0 + + @property + def snapshot_snip(self) -> bool: + return self.event_flags & SNAPSHOT_SNIP != 0 diff --git a/tastytrade/dxfeed/greeks.py b/tastytrade/dxfeed/greeks.py index 5036b7d..2ee7a3f 100644 --- a/tastytrade/dxfeed/greeks.py +++ b/tastytrade/dxfeed/greeks.py @@ -1,9 +1,9 @@ from decimal import Decimal -from .event import Event +from .event import IndexedEvent -class Greeks(Event): +class Greeks(IndexedEvent): """ Greek ratios, or simply Greeks, are differential values that show how the price of an option depends on other market parameters: on the price of the @@ -13,8 +13,6 @@ class Greeks(Event): portfolio has a risky sensitivity in this parameter. """ - #: transactional event flags - event_flags: int #: unique per-symbol index of this event index: int #: timestamp of this event in milliseconds diff --git a/tastytrade/dxfeed/theoprice.py b/tastytrade/dxfeed/theoprice.py index ce9ba87..a9c647c 100644 --- a/tastytrade/dxfeed/theoprice.py +++ b/tastytrade/dxfeed/theoprice.py @@ -1,9 +1,9 @@ from decimal import Decimal -from .event import Event +from .event import IndexedEvent -class TheoPrice(Event): +class TheoPrice(IndexedEvent): """ Theo price is a snapshot of the theoretical option price computation that is periodically performed by dxPrice model-free computation. dxFeed does @@ -12,8 +12,6 @@ class TheoPrice(Event): this event. """ - #: transactional event flags - event_flags: int #: unique per-symbol index of this event index: int #: timestamp of this event in milliseconds diff --git a/tastytrade/dxfeed/timeandsale.py b/tastytrade/dxfeed/timeandsale.py index 256366e..3cab451 100644 --- a/tastytrade/dxfeed/timeandsale.py +++ b/tastytrade/dxfeed/timeandsale.py @@ -1,9 +1,9 @@ from decimal import Decimal -from .event import Event +from .event import IndexedEvent -class TimeAndSale(Event): +class TimeAndSale(IndexedEvent): """ TimeAndSale event represents a trade or other market event with a price, like market open/close price. TimeAndSale events are intended to provide @@ -13,8 +13,6 @@ class TimeAndSale(Event): correction/cancellation processing. """ - #: transactional event flags - event_flags: int #: unique per-symbol index of this time and sale event index: int #: timestamp of the original event diff --git a/tastytrade/dxfeed/underlying.py b/tastytrade/dxfeed/underlying.py index ab5197c..97991bd 100644 --- a/tastytrade/dxfeed/underlying.py +++ b/tastytrade/dxfeed/underlying.py @@ -1,9 +1,9 @@ from decimal import Decimal -from .event import Event +from .event import IndexedEvent -class Underlying(Event): +class Underlying(IndexedEvent): """ Underlying event is a snapshot of computed values that are available for an option underlying symbol based on the option prices on the market. It @@ -11,8 +11,6 @@ class Underlying(Event): corresponding values on the market at any given moment of time. """ - #: transactional event flags - event_flags: int #: unique per-symbol index of this event index: int #: timestamp of this event in milliseconds