Skip to content

Commit

Permalink
use wrap validator for candle
Browse files Browse the repository at this point in the history
  • Loading branch information
Graeme22 committed Dec 16, 2024
1 parent c0a3786 commit 4195df8
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 46 deletions.
66 changes: 27 additions & 39 deletions tastytrade/dxfeed/candle.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
from decimal import Decimal
from typing import Optional
from typing import Annotated, Any, Optional

from pydantic import Field, computed_field
from pydantic import (
ValidationError,
ValidationInfo,
ValidatorFunctionWrapHandler,
WrapValidator,
)

from .event import IndexedEvent

ZERO = Decimal(0)


def zero_from_none(
v: Any, handler: ValidatorFunctionWrapHandler, info: ValidationInfo
) -> Decimal:
try:
return handler(v)
except ValidationError:
return ZERO


ZeroFromNone = Annotated[Decimal, WrapValidator(zero_from_none)]


class Candle(IndexedEvent):
"""
A Candle event with open, high, low, close prices and other information
Expand Down Expand Up @@ -35,40 +52,11 @@ class Candle(IndexedEvent):
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
#: the first (open) price of the candle
open: ZeroFromNone
#: the maximal (high) price of the candle
high: ZeroFromNone
#: the minimal (low) price of the candle
low: ZeroFromNone
#: the last (close) price of the candle
close: ZeroFromNone
11 changes: 5 additions & 6 deletions tastytrade/dxfeed/event.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from typing import Any, List
from typing import Any

from pydantic import BaseModel, ConfigDict, ValidationError, field_validator
from pydantic.alias_generators import to_camel

from tastytrade import logger
from tastytrade.utils import TastytradeError

REMOVE_EVENT = 0x2
Expand Down Expand Up @@ -34,7 +33,7 @@ def change_nan_to_none(cls, v: Any) -> Any:
return v

@classmethod
def from_stream(cls, data: list) -> List["Event"]:
def from_stream(cls, data: list) -> list["Event"]:
"""
Makes a list of event objects from a list of raw trade data fetched by
a :class:`~tastyworks.streamer.DXFeedStreamer`.
Expand All @@ -56,10 +55,10 @@ def from_stream(cls, data: list) -> List["Event"]:
local_values = data[offset : (i + 1) * size]
event_dict = dict(zip(keys, local_values))
try:
objs.append(cls(**event_dict))
except ValidationError as e:
objs.append(cls.model_validate(event_dict))
except ValidationError:
# we just skip these events as they're generally not helpful
logger.debug(f"Skipping event due to error: {e}")
pass
return objs


Expand Down
1 change: 0 additions & 1 deletion tastytrade/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from enum import Enum
from typing import Any, Optional, Union

from pandas_market_calendars.calendar_registry import prop
from pydantic import computed_field, field_serializer, model_validator

from tastytrade import VERSION
Expand Down

0 comments on commit 4195df8

Please sign in to comment.