Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/standardize-quote: Proposal to Standardize EquityQuote #5922

Merged
merged 5 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class EquityQuoteQueryParams(QueryParams):

symbol: str = Field(
description=QUERY_DESCRIPTIONS.get("symbol", "")
+ " In this case, the comma separated list of symbols."
+ " This endpoint will accept multiple symbols separated by commas."
)

@field_validator("symbol", mode="before", check_fields=False)
Expand All @@ -33,14 +33,125 @@ def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
class EquityQuoteData(Data):
"""Equity Quote Data."""

day_low: Optional[float] = Field(
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
asset_type: Optional[str] = Field(
default=None, description="Type of asset - i.e, stock, ETF, etc."
)
name: Optional[str] = Field(
default=None, description="Name of the company or asset."
)
exchange: Optional[str] = Field(
default=None,
description="The name or symbol of the venue where the data is from.",
)
bid: Optional[float] = Field(
default=None, description="Price of the top bid order."
)
bid_size: Optional[int] = Field(
default=None,
description="This represents the number of round lot orders at the given price."
+ " The normal round lot size is 100 shares."
+ " A size of 2 means there are 200 shares available at the given price.",
)
bid_exchange: Optional[str] = Field(
default=None,
description="The specific trading venue where the purchase order was placed.",
)
ask: Optional[float] = Field(
default=None, description="Price of the top ask order."
)
ask_size: Optional[int] = Field(
default=None,
description="This represents the number of round lot orders at the given price."
+ " The normal round lot size is 100 shares."
+ " A size of 2 means there are 200 shares available at the given price.",
)
ask_exchange: Optional[str] = Field(
default=None,
description="The specific trading venue where the sale order was placed.",
)
quote_conditions: Optional[Union[str, int, List[str], List[int]]] = Field(
default=None,
description="Conditions or condition codes applicable to the quote.",
)
quote_indicators: Optional[Union[str, int, List[str], List[int]]] = Field(
default=None,
description="Indicators or indicator codes applicable to the participant"
+ " quote related to the price bands for the issue, or the affect the quote has"
+ " on the NBBO.",
)
sales_conditions: Optional[Union[str, int, List[str], List[int]]] = Field(
default=None,
description="Conditions or condition codes applicable to the sale.",
)
sequence_number: Optional[int] = Field(
default=None,
description="The sequence number represents the sequence in which message events happened."
+ " These are increasing and unique per ticker symbol,"
+ " but will not always be sequential (e.g., 1, 2, 6, 9, 10, 11).",
)
market_center: Optional[str] = Field(
default=None,
description="The ID of the UTP participant that originated the message.",
)
participant_timestamp: Optional[datetime] = Field(
default=None,
description="Lowest price of the stock in the current trading day.",
description="Timestamp for when the quote was generated by the exchange.",
)
day_high: Optional[float] = Field(
trf_timestamp: Optional[datetime] = Field(
default=None,
description="Highest price of the stock in the current trading day.",
description="Timestamp for when the TRF (Trade Reporting Facility) received the message.",
)
sip_timestamp: Optional[datetime] = Field(
default=None,
description="Timestamp for when the SIP (Security Information Processor)"
+ " received the message from the exchange.",
)
last_price: Optional[float] = Field(
default=None, description="Price of the last trade."
)
last_tick: Optional[str] = Field(
default=None, description="Whether the last sale was an up or down tick."
)
last_size: Optional[int] = Field(
default=None, description="Size of the last trade."
)
last_timestamp: Optional[datetime] = Field(
default=None, description="Date and Time when the last price was recorded."
)
open: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("open", "")
)
high: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("high", "")
)
low: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("low", "")
)
close: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("close", "")
)
volume: Optional[Union[int, float]] = Field(
default=None, description=DATA_DESCRIPTIONS.get("volume", "")
)
exchange_volume: Optional[Union[int, float]] = Field(
default=None,
description="Volume of shares exchanged during the trading day on the specific exchange.",
)
prev_close: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("prev_close", "")
)
change: Optional[float] = Field(
default=None, description="Change in price from previous close."
)
change_percent: Optional[float] = Field(
default=None,
description="Change in price as a normalized percentage.",
json_schema_extra={"x-frontendmultiply": 100},
)
year_high: Optional[float] = Field(
default=None, description="The one year high (52W High)."
)
date: Optional[datetime] = Field(
description=DATA_DESCRIPTIONS.get("date", ""), default=None
year_low: Optional[float] = Field(
default=None, description="The one year low (52W Low)."
)
93 changes: 40 additions & 53 deletions openbb_platform/providers/fmp/openbb_fmp/models/equity_quote.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""FMP Equity Quote Model."""

from datetime import datetime
from typing import Any, Dict, List, Optional
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional, Union

from openbb_core.provider.abstract.data import ForceInt
from openbb_core.provider.abstract.fetcher import Fetcher
Expand All @@ -27,71 +27,58 @@ class FMPEquityQuoteData(EquityQuoteData):
__alias_dict__ = {
"price_avg50": "priceAvg50",
"price_avg200": "priceAvg200",
"date": "timestamp",
"last_timestamp": "timestamp",
"high": "dayHigh",
"low": "dayLow",
"last_price": "price",
"change_percent": "changesPercentage",
"prev_close": "previousClose",
}

symbol: Optional[str] = Field(default=None, description="Symbol of the company.")
name: Optional[str] = Field(default=None, description="Name of the company.")
price: Optional[float] = Field(
default=None, description="Current trading price of the equity."
)
changes_percentage: Optional[float] = Field(
default=None, description="Change percentage of the equity price."
)
change: Optional[float] = Field(
default=None, description="Change in the equity price."
)
year_high: Optional[float] = Field(
default=None, description="Highest price of the equity in the last 52 weeks."
)
year_low: Optional[float] = Field(
default=None, description="Lowest price of the equity in the last 52 weeks."
)
market_cap: Optional[float] = Field(
default=None, description="Market cap of the company."
)
price_avg50: Optional[float] = Field(
default=None, description="50 days average price of the equity."
default=None, description="50 day moving average price."
)
price_avg200: Optional[float] = Field(
default=None, description="200 days average price of the equity."
)
volume: Optional[ForceInt] = Field(
default=None,
description="Volume of the equity in the current trading day.",
default=None, description="200 day moving average price."
)
avg_volume: Optional[ForceInt] = Field(
default=None,
description="Average volume of the equity in the last 10 trading days.",
)
exchange: Optional[str] = Field(
default=None, description="Exchange the equity is traded on."
description="Average volume over the last 10 trading days.",
)
open: Optional[float] = Field(
default=None,
description="Opening price of the equity in the current trading day.",
)
previous_close: Optional[float] = Field(
default=None, description="Previous closing price of the equity."
)
eps: Optional[float] = Field(
default=None, description="Earnings per share of the equity."
)
pe: Optional[float] = Field(
default=None, description="Price earnings ratio of the equity."
)
earnings_announcement: Optional[str] = Field(
default=None, description="Earnings announcement date of the equity."
market_cap: Optional[float] = Field(
default=None, description="Market cap of the company."
)
shares_outstanding: Optional[ForceInt] = Field(
default=None, description="Number of shares outstanding of the equity."
default=None, description="Number of shares outstanding."
)
eps: Optional[float] = Field(default=None, description="Earnings per share.")
pe: Optional[float] = Field(default=None, description="Price earnings ratio.")
earnings_announcement: Optional[Union[datetime, str]] = Field(
default=None, description="Upcoming earnings announcement date."
)

@field_validator("timestamp", mode="before", check_fields=False)
@field_validator("last_timestamp", mode="before", check_fields=False)
@classmethod
def date_validate(cls, v): # pylint: disable=E0213
def validate_last_timestamp(cls, v): # pylint: disable=E0213
"""Return the date as a datetime object."""
return datetime.strptime(v, "%Y-%m-%d")
v = int(v) if isinstance(v, str) else v
return datetime.utcfromtimestamp(int(v)).replace(tzinfo=timezone.utc)

@field_validator("earnings_announcement", mode="before", check_fields=False)
@classmethod
def timestamp_validate(cls, v): # pylint: disable=E0213
"""Return the datetime string as a datetime object."""
if v:
dt = datetime.strptime(v, "%Y-%m-%dT%H:%M:%S.%f%z")
dt = dt.replace(microsecond=0)
timestamp = dt.timestamp()
return datetime.fromtimestamp(timestamp, tz=timezone.utc)
return None

@field_validator("change_percent", mode="after", check_fields=False)
@classmethod
def normalize_percent(cls, v): # pylint: disable=E0213
"""Return the percent value as a normalized value."""
return float(v) / 100 if v else None


class FMPEquityQuoteFetcher(
Expand Down
Loading
Loading