diff options
author | Pratyush Shukla <ps4534@nyu.edu> | 2024-01-18 11:04:11 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-18 05:34:11 +0000 |
commit | 2f4abee46c12d0144b5a3b877541527ca6e51e8a (patch) | |
tree | c85e3116439a71ce93793593919bff4d29a4ea68 | |
parent | 31d03eb5f1dec22f9a4c9876b6532a9b4fd3935a (diff) |
[DEPRECATION] `index.market` renamed to `index.price.historical` (#5962)
* deprecate index.market and add index.price.historical
* add index.historical standard model
* add index.historical provider models
* index extension tests
* provider tests
* linting
33 files changed, 1992 insertions, 13 deletions
diff --git a/openbb_platform/core/openbb_core/provider/standard_models/index_historical.py b/openbb_platform/core/openbb_core/provider/standard_models/index_historical.py new file mode 100644 index 00000000000..2e19d327260 --- /dev/null +++ b/openbb_platform/core/openbb_core/provider/standard_models/index_historical.py @@ -0,0 +1,63 @@ +"""Index Historical Standard Model.""" + +from datetime import ( + date as dateType, + datetime, +) +from typing import List, Optional, Set, Union + +from dateutil import parser +from pydantic import Field, StrictFloat, field_validator + +from openbb_core.provider.abstract.data import Data, ForceInt +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.descriptions import ( + DATA_DESCRIPTIONS, + QUERY_DESCRIPTIONS, +) + + +class IndexHistoricalQueryParams(QueryParams): + """Index Historical Query.""" + + symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", "")) + start_date: Optional[dateType] = Field( + description=QUERY_DESCRIPTIONS.get("start_date", ""), default=None + ) + end_date: Optional[dateType] = Field( + description=QUERY_DESCRIPTIONS.get("end_date", ""), default=None + ) + + @field_validator("symbol", mode="before", check_fields=False) + @classmethod + def upper_symbol(cls, v: Union[str, List[str], Set[str]]): + """Convert symbol to uppercase.""" + if isinstance(v, str): + return v.upper() + return ",".join([symbol.upper() for symbol in list(v)]) + + +class IndexHistoricalData(Data): + """Index Historical Data.""" + + date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", "")) + open: Optional[StrictFloat] = Field( + default=None, description=DATA_DESCRIPTIONS.get("open", "") + ) + high: Optional[StrictFloat] = Field( + default=None, description=DATA_DESCRIPTIONS.get("high", "") + ) + low: Optional[StrictFloat] = Field( + default=None, description=DATA_DESCRIPTIONS.get("low", "") + ) + close: Optional[StrictFloat] = Field( + default=None, description=DATA_DESCRIPTIONS.get("close", "") + ) + volume: Optional[ForceInt] = Field( + default=None, description=DATA_DESCRIPTIONS.get("volume", "") + ) + + @field_validator("date", mode="before", check_fields=False) + def date_validate(cls, v): # pylint: disable=E0213 + """Return formatted datetime.""" + return parser.isoparse(str(v)) diff --git a/openbb_platform/extensions/index/integration/test_index_api.py b/openbb_platform/extensions/index/integration/test_index_api.py index 29250f8955a..8bf77655bbc 100644 --- a/openbb_platform/extensions/index/integration/test_index_api.py +++ b/openbb_platform/extensions/index/integration/test_index_api.py @@ -154,6 +154,121 @@ def test_index_market(params, headers): [ ( { + "symbol": "^DJI", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + "provider": "fmp", + "sort": "desc", + } + ), + ( + { + "interval": "1m", + "provider": "cboe", + "symbol": "AAVE100", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "interval": "1d", + "provider": "cboe", + "symbol": "AAVE100", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "interval": "1min", + "provider": "fmp", + "symbol": "^DJI", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + "timeseries": 1, + "sort": "desc", + } + ), + ( + { + "interval": "1day", + "provider": "fmp", + "symbol": "^DJI", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + "timeseries": 1, + "sort": "desc", + } + ), + ( + { + "timespan": "minute", + "sort": "desc", + "limit": 49999, + "adjusted": True, + "multiplier": 1, + "provider": "polygon", + "symbol": "NDX", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "timespan": "day", + "sort": "desc", + "limit": 49999, + "adjusted": True, + "multiplier": 1, + "provider": "polygon", + "symbol": "NDX", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "interval": "1d", + "period": "max", + "prepost": True, + "rounding": True, + "provider": "yfinance", + "symbol": "DJI", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "provider": "intrinio", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + "symbol": "$DJI", + "tag": "level", + "sort": "desc", + "limit": 100, + "type": None, + } + ), + ], +) +@pytest.mark.integration +def test_index_price_historical(params, headers): + params = {p: v for p, v in params.items() if v} + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/index/price/historical?{query_str}" + result = requests.get(url, headers=headers, timeout=20) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@parametrize( + "params", + [ + ( + { "symbol": "BUKBUS", "start_date": "2023-01-01", "end_date": "2023-06-06", diff --git a/openbb_platform/extensions/index/integration/test_index_python.py b/openbb_platform/extensions/index/integration/test_index_python.py index 8a84157d4d5..d5365cb23f1 100644 --- a/openbb_platform/extensions/index/integration/test_index_python.py +++ b/openbb_platform/extensions/index/integration/test_index_python.py @@ -147,6 +147,116 @@ def test_index_market(params, obb): [ ( { + "symbol": "AAVE100", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "interval": "1m", + "provider": "cboe", + "symbol": "AAVE100", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "interval": "1d", + "provider": "cboe", + "symbol": "AAVE100", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "interval": "1min", + "provider": "fmp", + "symbol": "^DJI", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + "timeseries": 1, + "sort": "desc", + } + ), + ( + { + "interval": "1day", + "provider": "fmp", + "symbol": "^DJI", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + "timeseries": 1, + "sort": "desc", + } + ), + ( + { + "timespan": "minute", + "sort": "desc", + "limit": 49999, + "adjusted": True, + "multiplier": 1, + "provider": "polygon", + "symbol": "NDX", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "timespan": "day", + "sort": "desc", + "limit": 49999, + "adjusted": True, + "multiplier": 1, + "provider": "polygon", + "symbol": "NDX", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "interval": "1d", + "period": "max", + "prepost": True, + "rounding": True, + "provider": "yfinance", + "symbol": "DJI", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + } + ), + ( + { + "provider": "intrinio", + "start_date": "2023-01-01", + "end_date": "2023-06-06", + "symbol": "$DJI", + "tag": "level", + "sort": "desc", + "limit": 100, + "type": None, + } + ), + ], +) +@pytest.mark.integration +def test_index_price_historical(params, obb): + result = obb.index.price.historical(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ( + { "symbol": "BUKBUS", "start_date": "2023-01-01", "end_date": "2023-06-06", diff --git a/openbb_platform/extensions/index/openbb_index/index_router.py b/openbb_platform/extensions/index/openbb_index/index_router.py index 9f4d8df994a..6fc329d310d 100644 --- a/openbb_platform/extensions/index/openbb_index/index_router.py +++ b/openbb_platform/extensions/index/openbb_index/index_router.py @@ -10,12 +10,19 @@ from openbb_core.app.query import Query from openbb_core.app.router import Router from pydantic import BaseModel +from openbb_index.price.price_router import router as price_router + router = Router(prefix="") +router.include_router(price_router) # pylint: disable=unused-argument -@router.command(model="MarketIndices") +@router.command( + model="MarketIndices", + deprecated=True, + deprecation_message="This endpoint will be deprecated in the future releases. Use '/index/price/historical' instead.", +) async def market( cc: CommandContext, provider_choices: ProviderChoices, diff --git a/openbb_platform/extensions/index/openbb_index/price/__init__.py b/openbb_platform/extensions/index/openbb_index/price/__init__.py new file mode 100644 index 00000000000..8246f259eea --- /dev/null +++ b/openbb_platform/extensions/index/openbb_index/price/__init__.py @@ -0,0 +1 @@ +"""Index Price.""" diff --git a/openbb_platform/extensions/index/openbb_index/price/price_router.py b/openbb_platform/extensions/index/openbb_index/price/price_router.py new file mode 100644 index 00000000000..dc44332a687 --- /dev/null +++ b/openbb_platform/extensions/index/openbb_index/price/price_router.py @@ -0,0 +1,27 @@ +"""Price Router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from openbb_core.app.router import Router +from pydantic import BaseModel + +router = Router(prefix="/price") + +# pylint: disable=unused-argument + + +@router.command(model="IndexHistorical") +async def historical( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject[BaseModel]: + """Index Historical Price. Load stock data for a specific index.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/providers/cboe/openbb_cboe/__init__.py b/openbb_platform/providers/cboe/openbb_cboe/__init__.py index 3d72a61c32f..a9b59b1abee 100644 --- a/openbb_platform/providers/cboe/openbb_cboe/__init__.py +++ b/openbb_platform/providers/cboe/openbb_cboe/__init__.py @@ -12,6 +12,9 @@ from openbb_cboe.models.european_indices import ( CboeEuropeanIndicesFetcher, ) from openbb_cboe.models.futures_curve import CboeFuturesCurveFetcher +from openbb_cboe.models.index_historical import ( + CboeIndexHistoricalFetcher, +) from openbb_cboe.models.index_search import CboeIndexSearchFetcher from openbb_cboe.models.index_snapshots import CboeIndexSnapshotsFetcher from openbb_cboe.models.market_indices import ( @@ -28,16 +31,17 @@ cboe_provider = Provider( around the world.""", credentials=None, fetcher_dict={ - "EquitySearch": CboeEquitySearchFetcher, - "OptionsChains": CboeOptionsChainsFetcher, + "AvailableIndices": CboeAvailableIndicesFetcher, "EquityHistorical": CboeEquityHistoricalFetcher, "EquityInfo": CboeEquityInfoFetcher, - "FuturesCurve": CboeFuturesCurveFetcher, - "AvailableIndices": CboeAvailableIndicesFetcher, + "EquitySearch": CboeEquitySearchFetcher, "EuropeanIndexConstituents": CboeEuropeanIndexConstituentsFetcher, "EuropeanIndices": CboeEuropeanIndicesFetcher, - "MarketIndices": CboeMarketIndicesFetcher, + "FuturesCurve": CboeFuturesCurveFetcher, + "IndexHistorical": CboeIndexHistoricalFetcher, "IndexSearch": CboeIndexSearchFetcher, "IndexSnapshots": CboeIndexSnapshotsFetcher, + "MarketIndices": CboeMarketIndicesFetcher, + "OptionsChains": CboeOptionsChainsFetcher, }, ) diff --git a/openbb_platform/providers/cboe/openbb_cboe/models/index_historical.py b/openbb_platform/providers/cboe/openbb_cboe/models/index_historical.py new file mode 100644 index 00000000000..d68b413fa43 --- /dev/null +++ b/openbb_platform/providers/cboe/openbb_cboe/models/index_historical.py @@ -0,0 +1,212 @@ +"""CBOE Index Historical Model.""" + +from datetime import datetime, timedelta +from typing import Any, Dict, List, Literal, Optional + +import pandas as pd +from openbb_cboe.utils.helpers import ( + TICKER_EXCEPTIONS, + get_cboe_index_directory, + get_ticker_info, +) +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.standard_models.index_historical import ( + IndexHistoricalData, + IndexHistoricalQueryParams, +) +from openbb_core.provider.utils.helpers import make_request +from pydantic import Field + + +class CboeIndexHistoricalQueryParams(IndexHistoricalQueryParams): + """CBOE Index Historical Query. + + Source: https://www.cboe.com/ + """ + + interval: Literal["1d", "1m"] = Field( + description="Use interval, 1m, for intraday prices during the most recent trading period.", + default="1d", + ) + + +class CboeIndexHistoricalData(IndexHistoricalData): + """CBOE Index Historical Data.""" + + calls_volume: Optional[float] = Field( + default=None, + description="Number of calls traded during the most recent trading period. Only valid if interval is 1m.", + ) + puts_volume: Optional[float] = Field( + default=None, + description="Number of puts traded during the most recent trading period. Only valid if interval is 1m.", + ) + total_options_volume: Optional[float] = Field( + default=None, + description="Total number of options traded during the most recent trading period. Only valid if interval is 1m.", + ) + + +class CboeIndexHistoricalFetcher( + Fetcher[ + CboeIndexHistoricalQueryParams, + List[CboeIndexHistoricalData], + ] +): + """Transform the query, extract and transform the data from the CBOE endpoints.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> CboeIndexHistoricalQueryParams: + """Transform the query. Setting the start and end dates for a 1 year period.""" + return CboeIndexHistoricalQueryParams(**params) + + @staticmethod + def extract_data( + query: CboeIndexHistoricalQueryParams, # pylint: disable=unused-argument + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[Dict]: + """Return the raw data from the CBOE endpoint.""" + # Symbol directories are cached for seven days and are used for error handling and URL generation. + INDEXES = get_cboe_index_directory().index.to_list() |