From 667bbfc1a91bef80dffd32a7e1befe22aa406215 Mon Sep 17 00:00:00 2001 From: Pratyush Shukla Date: Fri, 28 Jul 2023 01:50:45 +0530 Subject: [PATCH] refactored `ForexPairs` and added Polygon provider --- .../fmp/openbb_fmp/models/forex_pairs.py | 24 +++- .../openbb_polygon/models/forex_pairs.py | 136 ++++++++++++++++++ .../polygon/openbb_polygon/provider.py | 2 + .../openbb_provider/models/forex_pairs.py | 28 +--- 4 files changed, 161 insertions(+), 29 deletions(-) create mode 100644 openbb_sdk/providers/polygon/openbb_polygon/models/forex_pairs.py diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/forex_pairs.py b/openbb_sdk/providers/fmp/openbb_fmp/models/forex_pairs.py index a58519cfbdb..9413b618542 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/forex_pairs.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/forex_pairs.py @@ -3,24 +3,35 @@ from typing import Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher from openbb_provider.helpers import data_transformer from openbb_provider.models.forex_pairs import ForexPairsData, ForexPairsQueryParams from openbb_fmp.utils.helpers import get_data_many +from pydantic import Field -class FMPForexPairsQueryParams(QueryParams): - """FMP Forex available pairs query. + +class FMPForexPairsQueryParams(ForexPairsQueryParams): + """FMP Forex available pairs Query. Source: https://site.financialmodelingprep.com/developer/docs/#Historical-Forex-Price - """ -class FMPForexPairsData(Data): - """FMP Forex available pairs data.""" +class FMPForexPairsData(ForexPairsData): + """FMP Forex available pairs Data.""" + + symbol: str = Field(description="The symbol of the currency pair.") + currency: str = Field(description="The base currency of the currency pair.") + stockExchange: Optional[str] = Field( + description="The stock exchange of the currency pair.", + alias="stock_exchange", + ) + exchange_short_name: Optional[str] = Field( + description="The short name of the stock exchange of the currency pair.", + alias="exchange_short_name", + ) class FMPForexPairsFetcher( @@ -46,6 +57,7 @@ class FMPForexPairsFetcher( base_url = "https://financialmodelingprep.com/api/v3" url = f"{base_url}/symbol/available-forex-currency-pairs?apikey={api_key}" + return get_data_many(url, FMPForexPairsData) @staticmethod diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/forex_pairs.py b/openbb_sdk/providers/polygon/openbb_polygon/models/forex_pairs.py new file mode 100644 index 00000000000..799e4f6879f --- /dev/null +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/forex_pairs.py @@ -0,0 +1,136 @@ +"""Polygon available pairs fetcher.""" + + +from datetime import date as dateType, datetime +from typing import Dict, List, Literal, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.helpers import data_transformer +from openbb_provider.metadata import QUERY_DESCRIPTIONS +from openbb_provider.models.forex_pairs import ForexPairsData, ForexPairsQueryParams +from pydantic import Field, PositiveInt, validator + +from openbb_polygon.utils.helpers import get_data + + +class PolygonForexPairsQueryParams(ForexPairsQueryParams): + """Polygon available pairs Query. + + Source: https://polygon.io/docs/forex/get_v3_reference_tickers + """ + + symbol: Optional[str] = Field(description="Symbol of the pair to search.") + date: Optional[dateType] = Field( + default=datetime.now().date(), description=QUERY_DESCRIPTIONS.get("date", "") + ) + search: Optional[str] = Field( + default="", + description="Search for terms within the ticker and/or company name.", + ) + active: Optional[Literal[True, False]] = Field( + default=True, + description="Specify if the tickers returned should be actively traded on the queried date.", + ) + order: Optional[Literal["asc", "desc"]] = Field( + default="asc", description="Order data by ascending or descending." + ) + sort: Optional[ + Literal[ + "ticker", + "name", + "market", + "locale", + "currency_symbol", + "currency_name", + "base_currency_symbol", + "base_currency_name", + "last_updated_utc", + "delisted_utc", + ] + ] = Field(default="", description="Sort field used for ordering.") + limit: Optional[PositiveInt] = Field( + default=1000, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + + +class PolygonForexPairsData(ForexPairsData): + """Polygon available pairs Data.""" + + market: str = Field(description="The name of the trading market. Always 'fx'.") + locale: str = Field(description="The locale of the currency pair.") + currency_symbol: str = Field(description="The symbol of the quote currency.") + currency_name: str = Field(description="The name of the quote currency.") + base_currency_symbol: str = Field(description="The symbol of the base currency.") + base_currency_name: str = Field(description="The name of the base currency.") + last_updated_utc: datetime = Field(description="The last updated timestamp in UTC.") + delisted_utc: Optional[datetime] = Field( + description="The delisted timestamp in UTC." + ) + + @validator("last_updated_utc", pre=True, check_fields=False) + def last_updated_utc_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%dT%H:%M:%SZ") + + @validator("delisted_utc", pre=True, check_fields=False) + def delisted_utc_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%dT%H:%M:%SZ") + + +class PolygonForexPairsFetcher( + Fetcher[ + ForexPairsQueryParams, + ForexPairsData, + PolygonForexPairsQueryParams, + PolygonForexPairsData, + ] +): + @staticmethod + def transform_query( + query: ForexPairsQueryParams, extra_params: Optional[Dict] = None + ) -> PolygonForexPairsQueryParams: + return PolygonForexPairsQueryParams( + **extra_params or {}, + ) + + @staticmethod + def extract_data( + query: PolygonForexPairsQueryParams, credentials: Optional[Dict[str, str]] + ) -> List[PolygonForexPairsData]: + if credentials: + api_key = credentials.get("polygon_api_key") + + tickers = f"ticker=C:{query.symbol}" if query.symbol else "" + request_url = ( + f"https://api.polygon.io/v3/reference/" + f"tickers?{tickers}&market=fx&date={query.date}&" + f"search={query.search}&active={query.active}&order={query.order}&" + f"limit={query.limit}&sort={query.sort}&apiKey={api_key}" + ) + + data = {"next_url": request_url} + all_data: List[Dict] = [] + + while "next_url" in data: + data = get_data(request_url) + + if isinstance(data, list): + raise ValueError("Expected a dict, got a list") + + if len(data["results"]) == 0: + raise RuntimeError( + "No results found. Please change your query parameters." + ) + + if data["status"] == "OK": + all_data.extend(data["results"]) + elif data["status"] == "ERROR": + raise UserWarning(data["error"]) + + if "next_url" in data: + request_url = f"{data['next_url']}&apiKey={api_key}" + + return all_data + + @staticmethod + def transform_data(data: List[PolygonForexPairsData]) -> List[ForexPairsData]: + return data_transformer(data, ForexPairsData) diff --git a/openbb_sdk/providers/polygon/openbb_polygon/provider.py b/openbb_sdk/providers/polygon/openbb_polygon/provider.py index dabee82dc54..90686f2badf 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/provider.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/provider.py @@ -8,6 +8,7 @@ from openbb_polygon.models.cash_flow import PolygonCashFlowStatementFetcher from openbb_polygon.models.crypto_eod import PolygonCryptoEODFetcher from openbb_polygon.models.crypto_price import PolygonCryptoPriceFetcher from openbb_polygon.models.forex_eod import PolygonForexEODFetcher +from openbb_polygon.models.forex_pairs import PolygonForexPairsFetcher from openbb_polygon.models.forex_price import PolygonForexPriceFetcher from openbb_polygon.models.income_statement import PolygonIncomeStatementFetcher from openbb_polygon.models.major_indices_eod import PolygonMajorIndicesEODFetcher @@ -34,5 +35,6 @@ polygon_provider = Provider( PolygonMajorIndicesPriceFetcher, PolygonForexEODFetcher, PolygonForexPriceFetcher, + PolygonForexPairsFetcher, ], ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/forex_pairs.py b/openbb_sdk/sdk/provider/openbb_provider/models/forex_pairs.py index 269a426ca72..4455a751cb9 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/models/forex_pairs.py +++ b/openbb_sdk/sdk/provider/openbb_provider/models/forex_pairs.py @@ -1,34 +1,16 @@ """Forex available pairs data model.""" -from typing import Optional - from openbb_provider.abstract.data import Data, QueryParams +from pydantic import Field + class ForexPairsQueryParams(QueryParams): - """Forex available pairs query.""" + """Forex available pairs Query.""" class ForexPairsData(Data): - """Forex available pairs data. + """Forex available pairs Data.""" - Returns - ------- - symbol : str - The symbol of the currency pair. - name : str - The name of the currency pair separated by '/'. - currency : str - The base currency of the currency pair. - stock_exchange : Optional[str] - The stock exchange of the currency pair. - exchange_short_name : Optional[str] - The short name of the stock exchange of the currency pair. - """ - - symbol: str - name: str - currency: str - stock_exchange: Optional[str] - exchange_short_name: Optional[str] + name: str = Field(description="The name of the currency pair.")