Hotfix/fix yfinance sdk4 (#5455)

This commit is contained in:
Danglewood
2023-09-21 21:57:09 -07:00
committed by GitHub
parent 2d2592df45
commit 95d7392cb0
24 changed files with 305 additions and 417 deletions

View File

@@ -15,7 +15,7 @@ from openbb_provider.standard_models.major_indices_historical import (
MajorIndicesHistoricalQueryParams,
)
from openbb_provider.utils.helpers import make_request
from pydantic import Field, validator
from pydantic import Field
class CboeMajorIndicesHistoricalQueryParams(MajorIndicesHistoricalQueryParams):
@@ -43,18 +43,6 @@ class CboeMajorIndicesHistoricalData(MajorIndicesHistoricalData):
description="Total number of options traded during the most recent trading period. Only valid if interval is 1m."
)
@validator("date", pre=True, check_fields=False)
def date_validate(cls, v): # pylint: disable=E0213
"""Return formatted datetime string."""
if isinstance(v, str):
try:
dt = datetime.strptime(v, "%Y-%m-%d %H:%M:%S")
return dt.strftime("%Y-%m-%d %H:%M:%S")
except ValueError:
dt = datetime.strptime(v, "%Y-%m-%d")
return dt.strftime("%Y-%m-%d")
return v
class CboeMajorIndicesHistoricalFetcher(
Fetcher[
@@ -166,14 +154,13 @@ class CboeMajorIndicesHistoricalFetcher(
data["close"] = round(data.close.astype(float), 2)
data["volume"] = 0
data.index = pd.to_datetime(data.index, format="%Y-%m-%d")
data.index = pd.to_datetime(data.index)
data = data[data["open"] > 0]
data = data[
(data.index >= pd.to_datetime(query.start_date, format="%Y-%m-%d"))
& (data.index <= pd.to_datetime(query.end_date, format="%Y-%m-%d"))
(data.index >= pd.to_datetime(query.start_date))
& (data.index <= pd.to_datetime(query.end_date))
]
data.index = data.index.astype(str)
if query.interval == "1m":
data_list = r.json()["data"]
@@ -200,8 +187,6 @@ class CboeMajorIndicesHistoricalFetcher(
data_list[i]["volume"]["total_options_volume"]
)
data = pd.DataFrame()
date = [d.replace("T", " ") for d in date]
date = [datetime.strptime(d, "%Y-%m-%d %H:%M:%S") for d in date]
data["date"] = pd.to_datetime(date)
data["open"] = open_
data["high"] = high

View File

@@ -162,13 +162,12 @@ class CboeStockHistoricalFetcher(
data["close"] = round(data.close.astype(float), 2)
data["volume"] = 0
data.index = pd.to_datetime(data.index, format="%Y-%m-%d")
data.index = pd.to_datetime(data.index)
data = data[data["open"] > 0]
data = data[
(data.index >= pd.to_datetime(start_date, format="%Y-%m-%d"))
& (data.index <= pd.to_datetime(end_date, format="%Y-%m-%d"))
(data.index >= pd.to_datetime(start_date))
& (data.index <= pd.to_datetime(end_date))
]
data.index = data.index.strftime("%Y-%m-%d")
if query.interval == "1m":
data_list = r.json()["data"]
date: list[datetime] = []

View File

@@ -2,7 +2,7 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Literal, Optional
from dateutil.relativedelta import relativedelta
from openbb_fmp.utils.helpers import get_data_many
@@ -12,7 +12,7 @@ from openbb_provider.standard_models.crypto_historical import (
CryptoHistoricalQueryParams,
)
from openbb_provider.utils.helpers import get_querystring
from pydantic import Field, NonNegativeInt, validator
from pydantic import Field, NonNegativeInt
class FMPCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
@@ -26,35 +26,32 @@ class FMPCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
timeseries: Optional[NonNegativeInt] = Field(
default=None, description="Number of days to look back."
)
interval: Literal[
"1min", "5min", "15min", "30min", "1hour", "4hour", "1day"
] = Field(default="1day", description="Data granularity.")
class FMPCryptoHistoricalData(CryptoHistoricalData):
"""FMP Crypto end of day Data."""
adjClose: float = Field(
description="Adjusted Close Price of the symbol.", alias="adj_close"
adjClose: Optional[float] = Field(description="Adjusted Close Price of the symbol.")
unadjustedVolume: Optional[float] = Field(
description="Unadjusted volume of the symbol."
)
unadjustedVolume: float = Field(
description="Unadjusted volume of the symbol.", alias="unadjusted_volume"
change: Optional[float] = Field(
description="Change in the price of the symbol from the previous day."
)
change: float = Field(
description="Change in the price of the symbol from the previous day.",
alias="change",
changePercent: Optional[float] = Field(
description=r"Change % in the price of the symbol."
)
changePercent: float = Field(
description=r"Change % in the price of the symbol.", alias="change_percent"
vwap: Optional[float] = Field(
description="Volume Weighted Average Price of the symbol."
)
label: str = Field(description="Human readable format of the date.")
changeOverTime: float = Field(
description=r"Change % in the price of the symbol over a period of time.",
alias="change_over_time",
label: Optional[str] = Field(description="Human readable format of the date.")
changeOverTime: Optional[float] = Field(
description=r"Change % in the price of the symbol over a period of time."
)
@validator("date", pre=True)
def date_validate(cls, v): # pylint: disable=E0213
"""Return the date as a datetime object."""
return datetime.strptime(v, "%Y-%m-%d")
class FMPCryptoHistoricalFetcher(
Fetcher[
@@ -87,10 +84,18 @@ class FMPCryptoHistoricalFetcher(
"""Return the raw data from the FMP endpoint."""
api_key = credentials.get("fmp_api_key") if credentials else ""
base_url = "https://financialmodelingprep.com/api/v3/"
query_str = get_querystring(query.dict(by_alias=True), ["symbol"])
query_str = query_str.replace("start_date", "from").replace("end_date", "to")
url = f"{base_url}historical-price-full/crypto/{query.symbol}?{query_str}&apikey={api_key}"
base_url = "https://financialmodelingprep.com/api/v3"
query_str = (
get_querystring(query.dict(), ["symbol"])
.replace("start_date", "from")
.replace("end_date", "to")
)
url_params = f"{query.symbol}?{query_str}&apikey={api_key}"
url = f"{base_url}/historical-chart/{query.interval}/{url_params}"
if query.interval == "1day":
url = f"{base_url}/historical-price-full/crypto/{url_params}"
return get_data_many(url, "historical", **kwargs)

View File

@@ -2,7 +2,7 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Literal, Optional
from dateutil.relativedelta import relativedelta
from openbb_fmp.utils.helpers import get_data_many, get_querystring
@@ -11,7 +11,7 @@ from openbb_provider.standard_models.forex_historical import (
ForexHistoricalData,
ForexHistoricalQueryParams,
)
from pydantic import Field, validator
from pydantic import Field
class FMPForexHistoricalQueryParams(ForexHistoricalQueryParams):
@@ -20,34 +20,33 @@ class FMPForexHistoricalQueryParams(ForexHistoricalQueryParams):
Source: https://site.financialmodelingprep.com/developer/docs/#Historical-Forex-Price
"""
interval: Literal[
"1min", "5min", "15min", "30min", "1hour", "4hour", "1day"
] = Field(default="1day", description="Data granularity.")
class FMPForexHistoricalData(ForexHistoricalData):
"""FMP Forex end of day Data."""
adjClose: float = Field(
adjClose: Optional[float] = Field(
description="Adjusted Close Price of the symbol.", alias="adj_close"
)
unadjustedVolume: float = Field(
unadjustedVolume: Optional[float] = Field(
description="Unadjusted volume of the symbol.", alias="unadjusted_volume"
)
change: float = Field(
change: Optional[float] = Field(
description="Change in the price of the symbol from the previous day.",
alias="change",
)
changePercent: float = Field(
changePercent: Optional[float] = Field(
description=r"Change % in the price of the symbol.", alias="change_percent"
)
label: str = Field(description="Human readable format of the date.")
changeOverTime: float = Field(
label: Optional[str] = Field(description="Human readable format of the date.")
changeOverTime: Optional[float] = Field(
description=r"Change % in the price of the symbol over a period of time.",
alias="change_over_time",
)
@validator("date", pre=True)
def date_validate(cls, v): # pylint: disable=E0213
"""Return the date as a datetime object."""
return datetime.strptime(v, "%Y-%m-%d")
class FMPForexHistoricalFetcher(
Fetcher[
@@ -81,9 +80,17 @@ class FMPForexHistoricalFetcher(
api_key = credentials.get("fmp_api_key") if credentials else ""
base_url = "https://financialmodelingprep.com/api/v3"
query_str = get_querystring(query.dict(by_alias=True), ["symbol"])
query_str = query_str.replace("start_date", "from").replace("end_date", "to")
url = f"{base_url}/historical-price-full/forex/{query.symbol}?{query_str}&apikey={api_key}"
query_str = (
get_querystring(query.dict(), ["symbol"])
.replace("start_date", "from")
.replace("end_date", "to")
)
url_params = f"{query.symbol}?{query_str}&apikey={api_key}"
url = f"{base_url}/historical-chart/{query.interval}/{url_params}"
if query.interval == "1day":
url = f"{base_url}/historical-price-full/forex/{url_params}"
return get_data_many(url, "historical", **kwargs)

View File

@@ -12,7 +12,7 @@ from openbb_provider.standard_models.major_indices_historical import (
MajorIndicesHistoricalQueryParams,
)
from openbb_provider.utils.helpers import get_querystring
from pydantic import Field, NonNegativeInt, validator
from pydantic import Field, NonNegativeInt
class FMPMajorIndicesHistoricalQueryParams(MajorIndicesHistoricalQueryParams):
@@ -56,14 +56,6 @@ class FMPMajorIndicesHistoricalData(MajorIndicesHistoricalData):
default=None,
)
@validator("date", pre=True, check_fields=False)
def date_validate(cls, v) -> datetime: # pylint: disable=E0213
"""Return the date as a datetime object."""
try:
return datetime.strptime(v, "%Y-%m-%d %H:%M:%S")
except Exception:
return datetime.strptime(v, "%Y-%m-%d")
class FMPMajorIndicesHistoricalFetcher(
Fetcher[

View File

@@ -11,7 +11,7 @@ from openbb_provider.standard_models.stock_historical import (
StockHistoricalQueryParams,
)
from openbb_provider.utils.helpers import get_querystring
from pydantic import Field, NonNegativeInt, validator
from pydantic import Field, NonNegativeInt
class FMPStockHistoricalQueryParams(StockHistoricalQueryParams):
@@ -49,14 +49,6 @@ class FMPStockHistoricalData(StockHistoricalData):
description=r"Change % in the price of the symbol over a period of time."
)
@validator("date", pre=True, check_fields=False)
def date_validate(cls, v): # pylint: disable=E0213
"""Return the date as a datetime object."""
try:
return datetime.strptime(v, "%Y-%m-%d %H:%M:%S")
except ValueError:
return datetime.strptime(v, "%Y-%m-%d")
class FMPStockHistoricalFetcher(
Fetcher[

View File

@@ -5,11 +5,11 @@ interactions:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
- gzip, deflate
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v3/historical-price-full/crypto/BTCUSD?apikey=MOCK_API_KEY&from=2023-01-01&to=2023-01-10
uri: https://financialmodelingprep.com/api/v3/historical-price-full/crypto/BTCUSD?apikey=MOCK_API_KEY&from=2023-01-01&interval=1day&to=2023-01-10
response:
body:
string: !!binary |
@@ -48,7 +48,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Tue, 19 Sep 2023 11:13:17 GMT
- Thu, 21 Sep 2023 17:35:22 GMT
ETag:
- W/"eec-rA4PG5qrZArsSp6t/v+2L1hjzQk"
Server:

View File

@@ -5,11 +5,11 @@ interactions:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
- gzip, deflate
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v3/historical-price-full/forex/EURUSD?apikey=MOCK_API_KEY&from=2023-01-01&to=2023-01-10
uri: https://financialmodelingprep.com/api/v3/historical-price-full/forex/EURUSD?apikey=MOCK_API_KEY&from=2023-01-01&interval=1day&to=2023-01-10
response:
body:
string: !!binary |
@@ -44,7 +44,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Tue, 19 Sep 2023 11:13:17 GMT
- Thu, 21 Sep 2023 17:08:45 GMT
ETag:
- W/"cd0-Rv/ywzhqPUdb7KJ/ZDpUXIC8K1A"
Server:

View File

@@ -13,7 +13,7 @@ from openbb_provider.standard_models.stock_historical import (
)
from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS
from openbb_provider.utils.helpers import get_querystring
from pydantic import Field, validator
from pydantic import Field
class IntrinioStockHistoricalQueryParams(StockHistoricalQueryParams):
@@ -67,24 +67,6 @@ class IntrinioStockHistoricalData(StockHistoricalData):
alias="change",
)
@validator("time", pre=True, check_fields=False)
def date_validate(cls, v): # pylint: disable=E0213
"""Return the date as a datetime object."""
return (
datetime.fromisoformat(v.replace("Z", "+00:00"))
if v.endswith(("Z", "+00:00"))
else datetime.fromisoformat(v)
)
@validator("close_time", pre=True, check_fields=False)
def close_time_validate(cls, v): # pylint: disable=E0213
"""Return the date as a datetime object."""
return (
datetime.fromisoformat(v.replace("Z", "+00:00"))
if v.endswith(("Z", "+00:00"))
else datetime.fromisoformat(v)
)
class IntrinioStockHistoricalFetcher(
Fetcher[

View File

@@ -5,14 +5,14 @@ from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
from dateutil.relativedelta import relativedelta
from openbb_polygon.utils.helpers import get_data
from openbb_polygon.utils.helpers import get_data_many
from openbb_provider.abstract.fetcher import Fetcher
from openbb_provider.standard_models.crypto_historical import (
CryptoHistoricalData,
CryptoHistoricalQueryParams,
)
from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS
from pydantic import Field, PositiveInt, validator
from pydantic import Field, PositiveInt
class PolygonCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
@@ -21,6 +21,9 @@ class PolygonCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
Source: https://polygon.io/docs/crypto/get_v2_aggs_ticker__cryptoticker__range__multiplier___timespan___from___to
"""
multiplier: PositiveInt = Field(
default=1, description="Multiplier of the timespan."
)
timespan: Literal[
"minute", "hour", "day", "week", "month", "quarter", "year"
] = Field(default="day", description="Timespan of the data.")
@@ -31,9 +34,6 @@ class PolygonCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
default=49999, description=QUERY_DESCRIPTIONS.get("limit", "")
)
adjusted: bool = Field(default=True, description="Whether the data is adjusted.")
multiplier: PositiveInt = Field(
default=1, description="Multiplier of the timespan."
)
class PolygonCryptoHistoricalData(CryptoHistoricalData):
@@ -54,10 +54,6 @@ class PolygonCryptoHistoricalData(CryptoHistoricalData):
description="Number of transactions for the symbol in the time period."
)
@validator("t", pre=True, check_fields=False)
def time_validate(cls, v): # pylint: disable=E0213
return datetime.fromtimestamp(v / 1000)
class PolygonCryptoHistoricalFetcher(
Fetcher[
@@ -94,18 +90,15 @@ class PolygonCryptoHistoricalFetcher(
f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}"
f"&sort={query.sort}&limit={query.limit}&apiKey={api_key}"
)
data = get_data_many(request_url, "results", **kwargs)
data = get_data(request_url, **kwargs)
if isinstance(data, list):
raise ValueError("Expected a dict, got a list")
if "results" not in data or len(data["results"]) == 0:
raise RuntimeError("No results found. Please change your query parameters.")
for d in data:
d["t"] = datetime.fromtimestamp(d["t"] / 1000)
if query.timespan not in ["minute", "hour"]:
d["t"] = d["t"].date()
return data
@staticmethod
def transform_data(data: dict) -> List[PolygonCryptoHistoricalData]:
return [
PolygonCryptoHistoricalData.parse_obj(d) for d in data.get("results", [])
]
return [PolygonCryptoHistoricalData.parse_obj(d) for d in data]

View File

@@ -5,14 +5,14 @@ from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
from dateutil.relativedelta import relativedelta
from openbb_polygon.utils.helpers import get_data
from openbb_polygon.utils.helpers import get_data_many
from openbb_provider.abstract.fetcher import Fetcher
from openbb_provider.standard_models.forex_historical import (
ForexHistoricalData,
ForexHistoricalQueryParams,
)
from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS
from pydantic import Field, PositiveInt, validator
from pydantic import Field, PositiveInt
class PolygonForexHistoricalQueryParams(ForexHistoricalQueryParams):
@@ -21,6 +21,9 @@ class PolygonForexHistoricalQueryParams(ForexHistoricalQueryParams):
Source: https://polygon.io/docs/forex/get_v2_aggs_ticker__forexticker__range__multiplier___timespan___from___to
"""
multiplier: PositiveInt = Field(
default=1, description="Multiplier of the timespan."
)
timespan: Literal[
"minute", "hour", "day", "week", "month", "quarter", "year"
] = Field(default="day", description="Timespan of the data.")
@@ -31,9 +34,6 @@ class PolygonForexHistoricalQueryParams(ForexHistoricalQueryParams):
default=49999, description=QUERY_DESCRIPTIONS.get("limit", "")
)
adjusted: bool = Field(default=True, description="Whether the data is adjusted.")
multiplier: PositiveInt = Field(
default=1, description="Multiplier of the timespan."
)
class PolygonForexHistoricalData(ForexHistoricalData):
@@ -50,14 +50,11 @@ class PolygonForexHistoricalData(ForexHistoricalData):
"vwap": "vw",
}
n: PositiveInt = Field(
description="Number of transactions for the symbol in the time period."
transactions: Optional[PositiveInt] = Field(
description="Number of transactions for the symbol in the time period.",
alias="n",
)
@validator("t", pre=True, check_fields=False)
def time_validate(cls, v): # pylint: disable=E0213
return datetime.fromtimestamp(v / 1000)
class PolygonForexHistoricalFetcher(
Fetcher[
@@ -91,18 +88,15 @@ class PolygonForexHistoricalFetcher(
f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}"
f"&sort={query.sort}&limit={query.limit}&apiKey={api_key}"
)
data = get_data_many(request_url, "results", **kwargs)
data = get_data(request_url, **kwargs)
if isinstance(data, list):
raise ValueError("Expected a dict, got a list")
if "results" not in data or len(data["results"]) == 0:
raise RuntimeError("No results found. Please change your query parameters.")
for d in data:
d["t"] = datetime.fromtimestamp(d["t"] / 1000)
if query.timespan not in ["minute", "hour"]:
d["t"] = d["t"].date()
return data
@staticmethod
def transform_data(data: dict) -> List[PolygonForexHistoricalData]:
return [
PolygonForexHistoricalData.parse_obj(d) for d in data.get("results", [])
]
return [PolygonForexHistoricalData.parse_obj(d) for d in data]

View File

@@ -5,14 +5,14 @@ from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
from dateutil.relativedelta import relativedelta
from openbb_polygon.utils.helpers import get_data
from openbb_polygon.utils.helpers import get_data_many
from openbb_provider.abstract.fetcher import Fetcher
from openbb_provider.standard_models.major_indices_historical import (
MajorIndicesHistoricalData,
MajorIndicesHistoricalQueryParams,
)
from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS
from pydantic import Field, PositiveInt, validator
from pydantic import Field, PositiveInt
class PolygonMajorIndicesHistoricalQueryParams(MajorIndicesHistoricalQueryParams):
@@ -48,9 +48,10 @@ class PolygonMajorIndicesHistoricalData(MajorIndicesHistoricalData):
"close": "c",
}
@validator("t", pre=True, check_fields=False)
def time_validate(cls, v): # pylint: disable=E0213
return datetime.fromtimestamp(v / 1000)
transactions: Optional[PositiveInt] = Field(
description="Number of transactions for the symbol in the time period.",
alias="n",
)
class PolygonMajorIndicesHistoricalFetcher(
@@ -87,13 +88,12 @@ class PolygonMajorIndicesHistoricalFetcher(
f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}"
f"&sort={query.sort}&limit={query.limit}&apiKey={api_key}"
)
data = get_data_many(request_url, "results", **kwargs)
data = get_data(request_url, **kwargs)
if isinstance(data, list):
raise ValueError("Expected a dict, got a list")
if "results" not in data or len(data["results"]) == 0:
raise RuntimeError("No results found. Please change your query parameters.")
for d in data:
d["t"] = datetime.fromtimestamp(d["t"] / 1000)
if query.timespan not in ["minute", "hour"]:
d["t"] = d["t"].date()
return data
@@ -101,7 +101,4 @@ class PolygonMajorIndicesHistoricalFetcher(
def transform_data(
data: dict,
) -> List[PolygonMajorIndicesHistoricalData]:
return [
PolygonMajorIndicesHistoricalData.parse_obj(d)
for d in data.get("results", [])
]
return [PolygonMajorIndicesHistoricalData.parse_obj(d) for d in data]

View File

@@ -23,6 +23,9 @@ class PolygonStockHistoricalQueryParams(StockHistoricalQueryParams):
Source: https://polygon.io/docs/stocks/getting-started
"""
multiplier: PositiveInt = Field(
default=1, description="Multiplier of the timespan."
)
timespan: Literal[
"minute", "hour", "day", "week", "month", "quarter", "year"
] = Field(default="day", description="Timespan of the data.")
@@ -36,9 +39,6 @@ class PolygonStockHistoricalQueryParams(StockHistoricalQueryParams):
default=True,
description="Output time series is adjusted by historical split and dividend events.",
)
multiplier: PositiveInt = Field(
default=1, description="Multiplier of the timespan."
)
class PolygonStockHistoricalData(StockHistoricalData):

View File

@@ -64,11 +64,11 @@ def get_data(url: str, **kwargs: Any) -> Union[list, dict]:
if r.status_code != 200:
message = data.get("message")
error = data.get("error")
value = message or error
value = error or message
raise RuntimeError(f"Error in Polygon request -> {value}")
if len(data) == 0:
if "results" not in data or len(data) == 0:
raise RuntimeError("No results found. Try adjusting the query parameters.")
return data

View File

@@ -50,23 +50,9 @@ class YFinanceCryptoHistoricalFetcher(
if params.get("start_date") is None:
transformed_params["start_date"] = now - relativedelta(years=1)
else:
try:
transformed_params["start_date"] = datetime.strptime(
transformed_params["start_date"], "%Y-%m-%d"
).date()
except TypeError:
pass
if params.get("end_date") is None:
transformed_params["end_date"] = now
else:
try:
transformed_params["end_date"] = datetime.strptime(
transformed_params["end_date"], "%Y-%m-%d"
).date()
except TypeError:
pass
return YFinanceCryptoHistoricalQueryParams(**params)
@@ -96,13 +82,16 @@ class YFinanceCryptoHistoricalFetcher(
if query.interval in ["1m", "2m", "5m", "15m", "30m", "60m", "1h", "90m"]
else 0
)
if query.start_date:
data.set_index("date", inplace=True)
data.index = to_datetime(data.index).date
data.index = to_datetime(data.index)
start_date_dt = datetime.combine(query.start_date, datetime.min.time())
end_date_dt = datetime.combine(query.end_date, datetime.min.time())
data = data[
(data.index >= query.start_date - timedelta(days=days))
& (data.index <= query.end_date)
(data.index >= start_date_dt + timedelta(days=days))
& (data.index <= end_date_dt)
]
data.reset_index(inplace=True)

View File

@@ -55,23 +55,9 @@ class YFinanceForexHistoricalFetcher(
if params.get("start_date") is None:
transformed_params["start_date"] = now - relativedelta(years=1)
else:
try:
transformed_params["start_date"] = datetime.strptime(
params["start_date"], "%Y-%m-%d"
).date()
except TypeError:
pass
if params.get("end_date") is None:
transformed_params["end_date"] = now
else:
try:
transformed_params["end_date"] = datetime.strptime(
params["end_date"], "%Y-%m-%d"
).date()
except TypeError:
pass
return YFinanceForexHistoricalQueryParams(**transformed_params)
@@ -92,22 +78,21 @@ class YFinanceForexHistoricalFetcher(
actions=False,
)
# query.end_date = (
# datetime.now().date() if query.end_date is None else query.end_date
# )
days = (
1
if query.interval in ["1m", "2m", "5m", "15m", "30m", "60m", "1h", "90m"]
else 0
)
if query.start_date:
data.set_index("date", inplace=True)
data.index = to_datetime(data.index).date
data.index = to_datetime(data.index)
start_date_dt = datetime.combine(query.start_date, datetime.min.time())
end_date_dt = datetime.combine(query.end_date, datetime.min.time())
data = data[
(data.index >= query.start_date - timedelta(days=days))
& (data.index <= query.end_date)
(data.index >= start_date_dt + timedelta(days=days))
& (data.index <= end_date_dt)
]
data.reset_index(inplace=True)

View File

@@ -55,23 +55,9 @@ class YFinanceMajorIndicesHistoricalFetcher(
if params.get("start_date") is None:
transformed_params["start_date"] = now - relativedelta(years=1)
else:
try:
transformed_params["start_date"] = datetime.strptime(
transformed_params["start_date"], "%Y-%m-%d"
).date()
except TypeError:
pass
if params.get("end_date") is None:
transformed_params["end_date"] = now
else:
try:
transformed_params["end_date"] = datetime.strptime(
transformed_params["end_date"], "%Y-%m-%d"
).date()
except TypeError:
pass
return YFinanceMajorIndicesHistoricalQueryParams(**params)
@@ -113,10 +99,14 @@ class YFinanceMajorIndicesHistoricalFetcher(
if query.start_date:
data.set_index("date", inplace=True)
data.index = to_datetime(data.index).date
data.index = to_datetime(data.index)
start_date_dt = datetime.combine(query.start_date, datetime.min.time())
end_date_dt = datetime.combine(query.end_date, datetime.min.time())
data = data[
(data.index >= query.start_date - timedelta(days=days))
& (data.index <= query.end_date)
(data.index >= start_date_dt + timedelta(days=days))
& (data.index <= end_date_dt)
]
data.reset_index(inplace=True)

View File

@@ -80,23 +80,9 @@ class YFinanceStockHistoricalFetcher(
if params.get("start_date") is None:
transformed_params["start_date"] = now - relativedelta(years=1)
else:
try:
transformed_params["start_date"] = datetime.strptime(
transformed_params["start_date"], "%Y-%m-%d"
).date()
except TypeError:
pass
if params.get("end_date") is None:
transformed_params["end_date"] = now
else:
try:
transformed_params["end_date"] = datetime.strptime(
transformed_params["end_date"], "%Y-%m-%d"
).date()
except TypeError:
pass
return YFinanceStockHistoricalQueryParams(**params)
@@ -125,12 +111,14 @@ class YFinanceStockHistoricalFetcher(
group_by=query.group_by,
)
query.end_date = (
datetime.now().date() if query.end_date is None else query.end_date
)
days = (
1
if query.interval in ["1m", "2m", "5m", "15m", "30m", "60m", "1h", "90m"]
else 0
)
if query.start_date:
data.set_index("date", inplace=True)
data.index = to_datetime(data.index)
@@ -139,7 +127,7 @@ class YFinanceStockHistoricalFetcher(
end_date_dt = datetime.combine(query.end_date, datetime.min.time())
data = data[
(data.index >= start_date_dt - timedelta(days=days))
(data.index >= start_date_dt + timedelta(days=days))
& (data.index <= end_date_dt)
]

View File

@@ -12,7 +12,7 @@ interactions:
uri: https://query2.finance.yahoo.com/v8/finance/chart/ES=F?interval=1d&range=1d
response:
body:
string: '{"chart":{"result":[{"meta":{"currency":"USD","symbol":"ES=F","exchangeName":"CME","instrumentType":"FUTURE","firstTradeDate":969249600,"regularMarketTime":1695154507,"gmtoffset":-14400,"timezone":"EDT","exchangeTimezoneName":"America/New_York","regularMarketPrice":4492.0,"chartPreviousClose":4501.5,"priceHint":2,"currentTradingPeriod":{"pre":{"timezone":"EDT","start":1695096000,"end":1695096000,"gmtoffset":-14400},"regular":{"timezone":"EDT","start":1695096000,"end":1695182340,"gmtoffset":-14400},"post":{"timezone":"EDT","start":1695182340,"end":1695182340,"gmtoffset":-14400}},"dataGranularity":"1d","range":"1d","validRanges":["1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max"]},"timestamp":[1695154507],"indicators":{"quote":[{"volume":[1362345],"low":[4462.25],"open":[4503.25],"high":[4509.5],"close":[4492.0]}],"adjclose":[{"adjclose":[4492.0]}]}}],"error":null}}'
string: '{"chart":{"result":[{"meta":{"currency":"USD","symbol":"ES=F","exchangeName":"CME","instrumentType":"FUTURE","firstTradeDate":969249600,"regularMarketTime":1695311200,"gmtoffset":-14400,"timezone":"EDT","exchangeTimezoneName":"America/New_York","regularMarketPrice":4401.5,"chartPreviousClose":4447.0,"priceHint":2,"currentTradingPeriod":{"pre":{"timezone":"EDT","start":1695268800,"end":1695268800,"gmtoffset":-14400},"regular":{"timezone":"EDT","start":1695268800,"end":1695355140,"gmtoffset":-14400},"post":{"timezone":"EDT","start":1695355140,"end":1695355140,"gmtoffset":-14400}},"dataGranularity":"1d","range":"1d","validRanges":["1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max"]},"timestamp":[1695311200],"indicators":{"quote":[{"low":[4395.0],"close":[4401.5],"volume":[993779],"open":[4445.0],"high":[4447.0]}],"adjclose":[{"adjclose":[4401.5]}]}}],"error":null}}'
headers:
Age:
- '1'
@@ -33,11 +33,11 @@ interactions:
cache-control:
- public, max-age=10, stale-while-revalidate=20
content-length:
- '887'
- '884'
content-type:
- application/json;charset=utf-8
date:
- Tue, 19 Sep 2023 20:25:10 GMT
- Thu, 21 Sep 2023 15:56:39 GMT
server:
- ATS
vary:
@@ -47,11 +47,11 @@ interactions:
x-envoy-upstream-service-time:
- '3'
x-request-id:
- 22d61e32-a17f-48d7-9fb0-71eae2b09a8b
- f2c31509-0b1d-46ea-ada0-f23aa55a3a6d
x-yahoo-request-id:
- 6sv8ta5igk0t7
- 45o2061igopto
y-rid:
- 6sv8ta5igk0t7
- 45o2061igopto
status:
code: 200
message: OK
@@ -68,45 +68,7 @@ interactions:
uri: https://query2.finance.yahoo.com/v8/finance/chart/ES=F?events=div%2Csplits%2CcapitalGains&includePrePost=False&interval=1d&period1=MOCK_PERIOD_1&period2=MOCK_PERIOD_2
response:
body:
string: !!binary |
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
HBcv9g+73dPTfwCWLfeX3hYAAA==
string: '{"chart":{"result":[{"meta":{"currency":"USD","symbol":"ES=F","exchangeName":"CME","instrumentType":"FUTURE","firstTradeDate":969249600,"regularMarketTime":1695311200,"gmtoffset":-14400,"timezone":"EDT","exchangeTimezoneName":"America/New_York","regularMarketPrice":4401.5,"chartPreviousClose":3846.0,"priceHint":2,"currentTradingPeriod":{"pre":{"timezone":"EDT","start":1695268800,"end":1695268800,"gmtoffset":-14400},"regular":{"timezone":"EDT","start":1695268800,"end":1695355140,"gmtoffset":-14400},"post":{"timezone":"EDT","start":1695355140,"end":1695355140,"gmtoffset":-14400}},"dataGranularity":"1d","range":"","validRanges":["1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max"]},"timestamp":[1672722000,1672808400,1672894800,1672981200,1673240400,1673326800],"indicators":{"quote":[{"low":[3814.5,3836.5,3822.5,3819.0,3909.75,3891.5],"high":[3906.75,3896.25,3885.5,3928.75,3973.25,3943.75],"close":[3846.0,3874.5,3829.0,3915.5,3913.75,3940.75],"volume":[1782473,1912155,1679973,2026154,1697887,1520955],"open":[3895.0,3842.75,3871.0,3833.0,3918.5,3914.5]}],"adjclose":[{"adjclose":[3846.0,3874.5,3829.0,3915.5,3913.75,3940.75]}]}}],"error":null}}'
headers:
Age:
- '0'
@@ -126,14 +88,12 @@ interactions:
- 1; mode=block
cache-control:
- public, max-age=10, stale-while-revalidate=20
content-encoding:
- gzip
content-length:
- '2128'
- '1164'
content-type:
- application/json;charset=utf-8
date:
- Tue, 19 Sep 2023 20:25:11 GMT
- Thu, 21 Sep 2023 15:56:40 GMT
server:
- ATS
vary:
@@ -141,13 +101,13 @@ interactions:
x-envoy-decorator-operation:
- finance-chart-api--mtls-production-sg3.finance-k8s.svc.yahoo.local:4080/*
x-envoy-upstream-service-time:
- '4'
- '5'
x-request-id:
- 7fb98b29-170b-4a5b-8681-fec9e0bd7701
- 3ec9768d-f778-4c28-93d2-a4b5e4a44c4b
x-yahoo-request-id:
- 73p22n9igk0t7
- 3uh35g9igopto
y-rid:
- 73p22n9igk0t7
- 3uh35g9igopto
status:
code: 200
message: OK

View File

@@ -12,7 +12,7 @@ interactions:
uri: https://query2.finance.yahoo.com/v8/finance/chart/AAPL?interval=1d&range=1d
response:
body:
string: '{"chart":{"result":[{"meta":{"currency":"USD","symbol":"AAPL","exchangeName":"NMS","instrumentType":"EQUITY","firstTradeDate":345479400,"regularMarketTime":1695153601,"gmtoffset":-14400,"timezone":"EDT","exchangeTimezoneName":"America/New_York","regularMarketPrice":179.07,"chartPreviousClose":177.97,"priceHint":2,"currentTradingPeriod":{"pre":{"timezone":"EDT","start":1695110400,"end":1695130200,"gmtoffset":-14400},"regular":{"timezone":"EDT","start":1695130200,"end":1695153600,"gmtoffset":-14400},"post":{"timezone":"EDT","start":1695153600,"end":1695168000,"gmtoffset":-14400}},"dataGranularity":"1d","range":"1d","validRanges":["1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max"]},"timestamp":[1695153601],"indicators":{"quote":[{"low":[177.1300048828125],"high":[179.6199951171875],"close":[179.07000732421875],"volume":[50447573],"open":[177.52000427246094]}],"adjclose":[{"adjclose":[179.07000732421875]}]}}],"error":null}}'
string: '{"chart":{"result":[{"meta":{"currency":"USD","symbol":"AAPL","exchangeName":"NMS","instrumentType":"EQUITY","firstTradeDate":345479400,"regularMarketTime":1695311644,"gmtoffset":-14400,"timezone":"EDT","exchangeTimezoneName":"America/New_York","regularMarketPrice":175.55,"chartPreviousClose":175.49,"priceHint":2,"currentTradingPeriod":{"pre":{"timezone":"EDT","start":1695283200,"end":1695303000,"gmtoffset":-14400},"regular":{"timezone":"EDT","start":1695303000,"end":1695326400,"gmtoffset":-14400},"post":{"timezone":"EDT","start":1695326400,"end":1695340800,"gmtoffset":-14400}},"dataGranularity":"1d","range":"1d","validRanges":["1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max"]},"timestamp":[1695311644],"indicators":{"quote":[{"high":[176.3000030517578],"volume":[25881397],"open":[174.5500030517578],"close":[175.5500030517578],"low":[174.4987030029297]}],"adjclose":[{"adjclose":[175.5500030517578]}]}}],"error":null}}'
headers:
Age:
- '0'
@@ -33,25 +33,25 @@ interactions:
cache-control:
- public, max-age=10, stale-while-revalidate=20
content-length:
- '944'
- '941'
content-type:
- application/json;charset=utf-8
date:
- Tue, 19 Sep 2023 20:25:10 GMT
- Thu, 21 Sep 2023 15:54:06 GMT
server:
- ATS
vary:
- Origin,Accept-Encoding
x-envoy-decorator-operation:
- finance-chart-api--mtls-production-sg3.finance-k8s.svc.yahoo.local:4080/*
- finance-chart-api--mtls-baseline-production-sg3.finance-k8s.svc.yahoo.local:4080/*
x-envoy-upstream-service-time:
- '33'
- '39'
x-request-id:
- c0083e5d-f8c2-4594-bb25-0d5e33faec2c
- cb29f7e9-ce57-469d-8605-dacad0e978ca
x-yahoo-request-id:
- 6ma49o9igk0t6
- 3e5oi45igopou
y-rid:
- 6ma49o9igk0t6
- 3e5oi45igopou
status:
code: 200
message: OK
@@ -69,105 +69,111 @@ interactions:
response:
body:
string: !!binary |
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
AAA=
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
headers:
Age:
- '0'
@@ -194,21 +200,21 @@ interactions:
content-type:
- application/json;charset=utf-8
date:
- Tue, 19 Sep 2023 20:25:10 GMT
- Thu, 21 Sep 2023 15:54:06 GMT
server:
- ATS
vary:
- Origin,Accept-Encoding
x-envoy-decorator-operation:
- finance-chart-api--mtls-production-sg3.finance-k8s.svc.yahoo.local:4080/*
- finance-chart-api--mtls-baseline-production-sg3.finance-k8s.svc.yahoo.local:4080/*
x-envoy-upstream-service-time:
- '39'
- '40'
x-request-id:
- 6a0d0767-0772-4292-91e5-11ba0afb8d9f
- af4778c0-800f-4a0e-969d-1ad6abcd7fbd
x-yahoo-request-id:
- 7mc4bm5igk0t6
- 44k8ok5igopou
y-rid:
- 7mc4bm5igk0t6
- 44k8ok5igopou
status:
code: 200
message: OK

View File

@@ -71,7 +71,11 @@ def test_y_finance_major_indices_historical_fetcher(credentials=test_credentials
@pytest.mark.record_http
def test_y_finance_stock_historical_fetcher(credentials=test_credentials):
params = {"symbol": "AAPL", "start_date": "2023-01-15", "end_date": "2023-01-25"}
params = {
"symbol": "AAPL",
"start_date": date(2023, 1, 1),
"end_date": date(2023, 1, 10),
}
fetcher = YFinanceStockHistoricalFetcher()
result = fetcher.test(params, credentials)
@@ -80,7 +84,11 @@ def test_y_finance_stock_historical_fetcher(credentials=test_credentials):
@pytest.mark.record_http
def test_y_finance_futures_historical_fetcher(credentials=test_credentials):
params = {"symbol": "ES", "start_date": date(2023, 1, 1), "end_date": "2023-05-10"}
params = {
"symbol": "ES",
"start_date": date(2023, 1, 1),
"end_date": date(2023, 1, 10),
}
fetcher = YFinanceFuturesHistoricalFetcher()
result = fetcher.test(params, credentials)
@@ -90,7 +98,11 @@ def test_y_finance_futures_historical_fetcher(credentials=test_credentials):
@pytest.mark.record_http
@pytest.mark.skip(reason="Blows up on download errors for specific dates.")
def test_y_finance_futures_curve_fetcher(credentials=test_credentials):
params = {"symbol": "ES", "start_date": date(2023, 1, 1), "end_date": "2023-05-10"}
params = {
"symbol": "ES",
"start_date": date(2023, 1, 1),
"end_date": date(2023, 1, 10),
}
fetcher = YFinanceFuturesCurveFetcher()
result = fetcher.test(params, credentials)

View File

@@ -7,7 +7,8 @@ from datetime import (
)
from typing import List, Optional, Set, Union
from pydantic import Field, PositiveFloat, validator
from dateutil import parser
from pydantic import Field, NonNegativeFloat, PositiveFloat, validator
from openbb_provider.abstract.data import Data
from openbb_provider.abstract.query_params import QueryParams
@@ -38,12 +39,15 @@ class CryptoHistoricalQueryParams(QueryParams):
class CryptoHistoricalData(Data):
"""Crypto end of day price Data."""
date: Union[dateType, datetime] = Field(
description=DATA_DESCRIPTIONS.get("date", "")
)
date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", ""))
open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", ""))
high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", ""))
low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", ""))
close: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("close", ""))
volume: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("volume", ""))
volume: NonNegativeFloat = Field(description=DATA_DESCRIPTIONS.get("volume", ""))
vwap: Optional[PositiveFloat] = Field(description=DATA_DESCRIPTIONS.get("vwap", ""))
@validator("date", pre=True, check_fields=False)
def date_validate(cls, v): # pylint: disable=E0213
"""Return formatted datetime."""
return parser.isoparse(str(v))

View File

@@ -7,6 +7,7 @@ from datetime import (
)
from typing import List, Optional, Set, Union
from dateutil import parser
from pydantic import Field, NonNegativeFloat, PositiveFloat, validator
from openbb_provider.abstract.data import Data
@@ -38,12 +39,15 @@ class ForexHistoricalQueryParams(QueryParams):
class ForexHistoricalData(Data):
"""Forex end of day price Data."""
date: Union[dateType, datetime] = Field(
description=DATA_DESCRIPTIONS.get("date", "")
)
date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", ""))
open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", ""))
high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", ""))
low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", ""))
close: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("close", ""))
volume: NonNegativeFloat = Field(description=DATA_DESCRIPTIONS.get("volume", ""))
vwap: Optional[PositiveFloat] = Field(description=DATA_DESCRIPTIONS.get("vwap", ""))
@validator("date", pre=True, check_fields=False)
def date_validate(cls, v): # pylint: disable=E0213
"""Return formatted datetime."""
return parser.isoparse(str(v))

View File

@@ -7,6 +7,7 @@ from datetime import (
)
from typing import List, Optional, Set, Union
from dateutil import parser
from pydantic import Field, NonNegativeInt, PositiveFloat, validator
from openbb_provider.abstract.data import Data
@@ -36,9 +37,7 @@ class MajorIndicesHistoricalQueryParams(QueryParams):
class MajorIndicesHistoricalData(Data):
"""Major Indices end of day price data."""
date: Union[dateType, datetime] = Field(
description=DATA_DESCRIPTIONS.get("date", "")
)
date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", ""))
open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", ""))
high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", ""))
low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", ""))
@@ -46,3 +45,8 @@ class MajorIndicesHistoricalData(Data):
volume: Optional[NonNegativeInt] = Field(
description=DATA_DESCRIPTIONS.get("volume", "")
)
@validator("date", pre=True, check_fields=False)
def date_validate(cls, v): # pylint: disable=E0213
"""Return formatted datetime."""
return parser.isoparse(str(v))