Passing tests (#5560)

* Passing tests

* fix options

* Fix most tests and bugs

* fix forex missing params

* make symbol upper in polygon fx pairs

* fix fixedincome integration tests (#5564)

* Fixing charting api integration tests (#5562)

* fixing charting api integration tests

* black

* fix stocks less five API fails

* fix the 5 failing stocks integration tests

* recapture fmp stock news unit test

* fix fmp forex api test

* couple more forex integration fixes

* changing the request to always use the legacy session. @jmaslek this is was broken for 3.8 and 3.9; with this change apparently all versions work - lemme know if you see any issue with this change

* ruff

* fix ta

* add missing param

* fix testers for the api

* fix globalnews to use images instead of image

* remove unncessary pandas import

* econcal int tests

* update gitignore

* fix get_querystring to support lists

* add missing parameters

---------

Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com>
Co-authored-by: Henrique Joaquim <h.joaquim@campus.fct.unl.pt>
This commit is contained in:
Igor Radovanovic
2023-10-16 12:57:27 +02:00
committed by GitHub
parent ab430a10a7
commit a5d2e833c4
38 changed files with 495 additions and 376 deletions

6
.gitignore vendored
View File

@@ -76,9 +76,11 @@ cache/
# lightning logs
lightning_logs/
# user script to install all extensions and providers
openbb_platform/platform/core/tests/app/model/charts/test_chart_settings.py
# testing artifacts
*/mocked_path
# CA certificates
*.pem
# Platform auto generated files
openbb_platform/openbb/package/*

View File

@@ -63,8 +63,13 @@ def test_chart_stocks_load(params, headers):
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -82,8 +87,12 @@ def test_chart_stocks_multiples(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -101,8 +110,12 @@ def test_chart_stocks_news(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -132,8 +145,12 @@ def test_chart_ta_adx(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -152,8 +169,12 @@ def test_chart_ta_aroon(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -183,8 +204,12 @@ def test_chart_ta_ema(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -214,8 +239,12 @@ def test_chart_ta_hma(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -246,8 +275,12 @@ def test_chart_ta_macd(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -278,8 +311,12 @@ def test_chart_ta_rsi(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -309,8 +346,12 @@ def test_chart_ta_sma(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -340,8 +381,12 @@ def test_chart_ta_wma(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
@pytest.mark.parametrize(
@@ -371,5 +416,9 @@ def test_chart_ta_zlma(params, headers):
assert isinstance(result, requests.Response)
assert result.status_code == 200
assert result.json()["chart"]
assert list(result.json()["chart"].keys()) == ["content", "format"]
chart = result.json()["chart"]
fig = chart.pop("fig", {})
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]

View File

@@ -1222,7 +1222,9 @@ class OpenBBFigure(go.Figure):
return super().to_html(*args, **kwargs)
def to_plotly_json(self, ndarray: bool = False) -> Dict[str, Any]:
def to_plotly_json(
self, ndarray: bool = False, np_nan: bool = False
) -> Dict[str, Any]:
"""
Convert figure to a JSON representation as a Python dict
@@ -1231,9 +1233,13 @@ class OpenBBFigure(go.Figure):
----------
ndarray : `bool`, optional
If the plotly json should contain np.ndarray, by default False
np_nan : `bool`, optional
If the plotly json should contain np.nan, by default False
"""
def remove_ndarrays(data):
"""Remove ndarrays from the plotly json."""
if isinstance(data, dict):
for key, val in data.items():
if isinstance(val, np.ndarray):
@@ -1249,9 +1255,28 @@ class OpenBBFigure(go.Figure):
return data
def remove_nan(data):
"""Remove nan from the plotly json."""
if isinstance(data, dict):
for key, val in data.items():
if isinstance(val, float) and np.isnan(val):
data[key] = None
elif isinstance(val, (dict, list)):
data[key] = remove_nan(val)
elif isinstance(data, list):
for i, val in enumerate(data):
if isinstance(val, float) and np.isnan(val):
data[i] = None
elif isinstance(val, (dict, list)):
data[i] = remove_nan(val)
return data
plotly_json = super().to_plotly_json()
if not ndarray:
plotly_json = remove_ndarrays(plotly_json)
if not np_nan:
plotly_json = remove_nan(plotly_json)
return plotly_json

View File

@@ -18,7 +18,14 @@ def headers():
@pytest.mark.parametrize(
"params",
[
({"symbol": "BTCUSD", "start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"symbol": "BTC-USD",
"start_date": "2023-01-01",
"end_date": "2023-01-06",
"provider": "polygon",
}
),
(
{
"interval": "1min",
@@ -31,7 +38,7 @@ def headers():
),
(
{
"interval": "1day",
"interval": "15min",
"provider": "fmp",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
@@ -72,7 +79,7 @@ def headers():
"provider": "yfinance",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"end_date": "2023-01-08",
}
),
(

View File

@@ -7,7 +7,6 @@ from openbb_core.app.model.obbject import OBBject
@pytest.fixture(scope="session")
def obb(pytestconfig):
"""Fixture to setup obb."""
if pytestconfig.getoption("markexpr") != "not integration":
import openbb
@@ -36,11 +35,11 @@ def obb(pytestconfig):
),
(
{
"interval": "1day",
"interval": "15min",
"provider": "fmp",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"end_date": "2023-01-03",
"timeseries": 1,
}
),
@@ -70,16 +69,6 @@ def obb(pytestconfig):
"end_date": "2023-01-02",
}
),
(
{
"interval": "5m",
"period": "max",
"provider": "yfinance",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
}
),
(
{
"interval": "1d",

View File

@@ -38,14 +38,25 @@ def test_economy_const(params, headers):
[
(
{
"countries": ["portugal", "spain"],
"countries": "spain",
"units": "growth_same",
"frequency": "monthly",
"harmonized": True,
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
)
),
(
{
"countries": ["portugal", "spain"],
"units": "growth_same",
"frequency": "monthly",
"harmonized": True,
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"provider": "fred",
}
),
],
)
@pytest.mark.integration
@@ -62,7 +73,14 @@ def test_economy_cpi(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "DJI", "start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"symbol": "^DJI",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"provider": "fmp",
}
),
(
{
"interval": "1m",
@@ -127,18 +145,6 @@ def test_economy_cpi(params, headers):
"end_date": "2023-06-06",
}
),
(
{
"interval": "1m",
"period": "max",
"prepost": True,
"rounding": True,
"provider": "yfinance",
"symbol": "DJI",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
(
{
"interval": "1d",
@@ -217,7 +223,6 @@ def test_economy_european_index_constituents(params, headers):
@pytest.mark.parametrize(
"params",
[
({}),
({"europe": True, "provider": "cboe"}),
({"provider": "fmp"}),
({"provider": "yfinance"}),
@@ -236,7 +241,7 @@ def test_economy_available_indices(params, headers):
@pytest.mark.parametrize(
"params",
[({})],
[({"provider": "fmp"})],
)
@pytest.mark.integration
def test_economy_risk(params, headers):
@@ -252,13 +257,13 @@ def test_economy_risk(params, headers):
@pytest.mark.parametrize(
"params",
[
({"query": "DJ", "symbol": True}),
({"query": "D", "symbol": True, "provider": "cboe"}),
(
{
"europe": True,
"provider": "cboe",
"query": "DJ",
"symbol": True,
"query": "A",
"symbol": False,
}
),
],
@@ -364,10 +369,11 @@ def test_economy_sp500_multiples(params, headers):
[
(
{
"symbol": "AAPL",
"symbol": "$GDP",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"limit": 100,
"provider": "intrinio",
}
)
],

View File

@@ -24,7 +24,7 @@ def obb(pytestconfig): # pylint: disable=inconsistent-return-statements
],
)
@pytest.mark.integration
def test_economy_const(params):
def test_economy_const(params, obb):
result = obb.economy.const(**params)
assert result
assert isinstance(result, OBBject)
@@ -47,7 +47,7 @@ def test_economy_const(params):
],
)
@pytest.mark.integration
def test_economy_cpi(params):
def test_economy_cpi(params, obb):
result = obb.economy.cpi(**params)
assert result
assert isinstance(result, OBBject)
@@ -59,7 +59,7 @@ def test_economy_cpi(params):
[
(
{
"symbol": "DJI",
"symbol": "AAVE100",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
@@ -128,18 +128,6 @@ def test_economy_cpi(params):
"end_date": "2023-06-06",
}
),
(
{
"interval": "5m",
"period": "max",
"prepost": True,
"rounding": True,
"provider": "yfinance",
"symbol": "DJI",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
(
{
"interval": "1d",
@@ -155,7 +143,7 @@ def test_economy_cpi(params):
],
)
@pytest.mark.integration
def test_economy_index(params):
def test_economy_index(params, obb):
result = obb.economy.index(**params)
assert result
assert isinstance(result, OBBject)
@@ -193,7 +181,7 @@ def test_economy_index(params):
],
)
@pytest.mark.integration
def test_economy_european_index(params):
def test_economy_european_index(params, obb):
result = obb.economy.european_index(**params)
assert result
assert isinstance(result, OBBject)
@@ -207,7 +195,7 @@ def test_economy_european_index(params):
],
)
@pytest.mark.integration
def test_economy_european_index_constituents(params):
def test_economy_european_index_constituents(params, obb):
result = obb.economy.european_index_constituents(**params)
assert result
assert isinstance(result, OBBject)
@@ -224,7 +212,7 @@ def test_economy_european_index_constituents(params):
],
)
@pytest.mark.integration
def test_economy_available_indices(params):
def test_economy_available_indices(params, obb):
result = obb.economy.available_indices(**params)
assert result
assert isinstance(result, OBBject)
@@ -238,7 +226,7 @@ def test_economy_available_indices(params):
],
)
@pytest.mark.integration
def test_economy_risk(params):
def test_economy_risk(params, obb):
result = obb.economy.risk(**params)
assert result
assert isinstance(result, OBBject)
@@ -248,19 +236,19 @@ def test_economy_risk(params):
@pytest.mark.parametrize(
"params",
[
({"query": "DJ", "symbol": True}),
({"query": "D", "symbol": True}),
(
{
"europe": True,
"provider": "cboe",
"query": "AA",
"symbol": True,
"query": "A",
"symbol": False,
}
),
],
)
@pytest.mark.integration
def test_economy_index_search(params):
def test_economy_index_search(params, obb):
result = obb.economy.index_search(**params)
assert result
assert isinstance(result, OBBject)
@@ -274,7 +262,7 @@ def test_economy_index_search(params):
],
)
@pytest.mark.integration
def test_economy_index_snapshots(params):
def test_economy_index_snapshots(params, obb):
result = obb.economy.index_snapshots(**params)
assert result
assert isinstance(result, OBBject)
@@ -288,7 +276,7 @@ def test_economy_index_snapshots(params):
],
)
@pytest.mark.integration
def test_economy_cot_search(params):
def test_economy_cot_search(params, obb):
result = obb.economy.cot_search(**params)
assert result
assert isinstance(result, OBBject)
@@ -315,7 +303,7 @@ def test_economy_cot_search(params):
],
)
@pytest.mark.integration
def test_economy_cot(params):
def test_economy_cot(params, obb):
result = obb.economy.cot(**params)
assert result
assert isinstance(result, OBBject)
@@ -337,7 +325,7 @@ def test_economy_cot(params):
],
)
@pytest.mark.integration
def test_economy_sp500_multiples(params):
def test_economy_sp500_multiples(params, obb):
result = obb.economy.sp500_multiples(**params)
assert result
assert isinstance(result, OBBject)
@@ -350,7 +338,7 @@ def test_economy_sp500_multiples(params):
[
(
{
"symbol": "AAPL",
"symbol": "$GDP",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"limit": 100,
@@ -359,7 +347,7 @@ def test_economy_sp500_multiples(params):
],
)
@pytest.mark.integration
def test_economy_fred_index(params):
def test_economy_fred_index(params, obb):
result = obb.economy.fred_index(**params)
assert result
assert isinstance(result, OBBject)
@@ -369,13 +357,13 @@ def test_economy_fred_index(params):
@pytest.mark.parametrize(
"params",
[
({"units": "usd", "start_date": "2023-01-01", "end_date": "2023-06-06"}),
({"units": "usd", "start_date": "2021-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"provider": "oecd",
"units": "usd",
"start_date": "2023-01-01",
"start_date": "2021-01-01",
"end_date": "2023-06-06",
}
),
@@ -423,7 +411,7 @@ def test_economy_gdpreal(params, obb):
{
"period": "annual",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"end_date": "2025-06-06",
"type": "real",
}
),
@@ -433,7 +421,7 @@ def test_economy_gdpreal(params, obb):
"provider": "oecd",
"period": "annual",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"end_date": "2025-06-06",
"type": "real",
}
),
@@ -447,3 +435,27 @@ def test_economy_gdpforecast(params, obb):
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@pytest.mark.parametrize(
"params",
[
(
{
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"country": "Portugal",
"importance": 1,
"group": "money",
}
),
],
)
@pytest.mark.integration
def test_economy_econcal(params, obb):
params = {p: v for p, v in params.items() if v}
result = obb.economy.econcal(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0

View File

@@ -18,17 +18,16 @@ def headers():
@pytest.mark.parametrize(
"params",
[
({}),
(
{
"symbol": "EURUSD",
"date": "2023-01-01",
"search": "USD",
"provider": "polygon",
"symbol": "USDJPY",
"date": "2023-10-12",
"search": "",
"active": True,
"order": "asc",
"sort": "ticker",
"limit": 1000,
"provider": "polygon",
"sort": "currency_name",
"limit": 100,
}
),
(
@@ -57,7 +56,15 @@ def test_forex_pairs(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "EURUSD", "start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"symbol": "EURUSD",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"interval": "1day",
"provider": "fmp",
}
),
(
{
"interval": "1min",
@@ -67,15 +74,6 @@ def test_forex_pairs(params, headers):
"end_date": "2023-01-02",
}
),
(
{
"interval": "1day",
"provider": "intrinio",
"symbol": "EURUSD",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
(
{
"multiplier": 1,

View File

@@ -1,5 +1,4 @@
"""Test forex extension."""
import datetime
import pytest
from openbb_core.app.model.obbject import OBBject
@@ -18,17 +17,16 @@ def obb(pytestconfig):
@pytest.mark.parametrize(
"params",
[
({}),
(
{
"symbol": "EURUSD",
"date": datetime.date(2023, 9, 25),
"search": "USD",
"provider": "polygon",
"symbol": "USDJPY",
"date": "2023-10-12",
"search": "",
"active": True,
"order": "asc",
"sort": "ticker",
"limit": 1000,
"provider": "polygon",
"sort": "currency_name",
"limit": 100,
}
),
(
@@ -54,7 +52,15 @@ def test_forex_pairs(params, obb):
@pytest.mark.parametrize(
"params",
[
({"symbol": "EURUSD", "start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"symbol": "EURUSD",
"interval": "1day",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"provider": "fmp",
}
),
(
{
"interval": "1min",
@@ -64,15 +70,6 @@ def test_forex_pairs(params, obb):
"end_date": "2023-01-02",
}
),
(
{
"interval": "1day",
"provider": "intrinio",
"symbol": "EURUSD",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
(
{
"multiplier": 1,
@@ -101,7 +98,7 @@ def test_forex_pairs(params, obb):
),
(
{
"interval": "5m",
"interval": "1d",
"period": "max",
"provider": "yfinance",
"symbol": "EURUSD",

View File

@@ -24,12 +24,13 @@ def headers():
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"expiration": "2024-06",
"provider": "yfinance",
}
),
(
{
"interval": "1d",
"period": "1d",
"period": "max",
"prepost": True,
"adjust": True,
"back_adjust": True,
@@ -56,9 +57,8 @@ def test_futures_load(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL", "date": "2023-01-01"}),
({"provider": "cboe", "symbol": "AAPL", "date": "2023-01-01"}),
({"provider": "yfinance", "symbol": "AAPL", "date": "2023-01-01"}),
({"provider": "cboe", "symbol": "VXM", "date": "2023-01-25"}),
({"provider": "yfinance", "symbol": "ES", "date": "2023-08-01"}),
],
)
@pytest.mark.integration

View File

@@ -27,13 +27,13 @@ def obb(pytestconfig):
(
{
"interval": "1d",
"period": "1d",
"period": "max",
"prepost": True,
"adjust": True,
"back_adjust": True,
"provider": "yfinance",
"symbol": "ES",
"start_date": "2023-01-01",
"start_date": "2023-05-05",
"end_date": "2023-06-06",
"expiration": "2024-06",
}
@@ -51,9 +51,9 @@ def test_futures_load(params, obb):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL", "date": "2023-01-01"}),
({"provider": "cboe", "symbol": "AAPL", "date": "2023-01-01"}),
({"provider": "yfinance", "symbol": "AAPL", "date": "2023-01-01"}),
({"symbol": "VX", "date": "2023-01-25"}),
({"provider": "cboe", "symbol": "VXM", "date": "2023-01-25"}),
({"provider": "yfinance", "symbol": "ES", "date": "2023-08-01"}),
],
)
@pytest.mark.integration

View File

@@ -18,7 +18,7 @@ def headers():
@pytest.mark.parametrize(
"params",
[
({"limit": 20}),
({"limit": 20, "provider": "benzinga"}),
(
{
"display": "full",
@@ -42,7 +42,7 @@ def headers():
(
{
"provider": "fmp",
"limit": 20,
"limit": 30,
}
),
(

View File

@@ -1,4 +1,5 @@
import base64
from datetime import time
import pytest
import requests
@@ -18,11 +19,11 @@ def headers():
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL", "period": "annual", "limit": 12}),
({"symbol": "AAPL", "period": "annual", "limit": 12, "provider": "fmp"}),
(
{
"type": "reported",
"year": 2023,
"year": 2022,
"provider": "intrinio",
"symbol": "AAPL",
"period": "annual",
@@ -116,11 +117,11 @@ def test_stocks_fa_cal(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL", "period": "annual", "limit": 12}),
({"symbol": "AAPL", "period": "annual", "limit": 12, "provider": "fmp"}),
(
{
"type": "reported",
"year": 2023,
"year": 2022,
"provider": "intrinio",
"symbol": "AAPL",
"period": "annual",
@@ -129,9 +130,9 @@ def test_stocks_fa_cal(params, headers):
),
(
{
"company_name": "Apple Inc",
"company_name_search": "AAPL",
"sic": "35719904",
"company_name": "Apple Inc.",
"company_name_search": "Apple Inc.",
"sic": "3571",
"include_sources": True,
"order": "asc",
"sort": "filing_date",
@@ -139,16 +140,16 @@ def test_stocks_fa_cal(params, headers):
"symbol": "AAPL",
"period": "annual",
"limit": 12,
"filing_date": "2023-09-15",
"filing_date_lt": "2023-09-20",
"filing_date_lte": "2023-09-20",
"filing_date_gt": "2023-09-10",
"filing_date_gte": "2023-09-10",
"period_of_report_date": "2023-10-15",
"period_of_report_date_lt": "2023-10-20",
"period_of_report_date_lte": "2023-10-20",
"period_of_report_date_gt": "2023-10-10",
"period_of_report_date_gte": "2023-10-10",
"filing_date": "2022-10-27",
"filing_date_lt": "2022-11-01",
"filing_date_lte": "2022-11-01",
"filing_date_gt": "2022-10-10",
"filing_date_gte": "2022-10-10",
"period_of_report_date": "2022-09-24",
"period_of_report_date_lt": "2022-11-01",
"period_of_report_date_lte": "2022-11-01",
"period_of_report_date_gt": "2022-10-10",
"period_of_report_date_gte": "2022-10-10",
}
),
(
@@ -289,11 +290,11 @@ def test_stocks_fa_est(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL", "period": "annual", "limit": 12}),
({"symbol": "AAPL", "period": "annual", "limit": 12, "provider": "fmp"}),
(
{
"type": "reported",
"year": 2023,
"year": 2022,
"provider": "intrinio",
"symbol": "AAPL",
"period": "annual",
@@ -375,10 +376,11 @@ def test_stocks_fa_income_growth(params, headers):
(
{
"symbol": "AAPL",
"transactionType": ["P-Purchase"],
"reportingCik": 1,
"companyCik": 1,
"transactionType": "P-Purchase",
"reportingCik": 3571,
"companyCik": 3571,
"page": 1,
"provider": "fmp",
}
)
],
@@ -396,7 +398,16 @@ def test_stocks_fa_ins(params, headers):
@pytest.mark.parametrize(
"params",
[({"symbol": "AAPL", "include_current_quarter": True, "date": "2023-01-01"})],
[
(
{
"symbol": "AAPL",
"include_current_quarter": True,
"date": "2021-09-30",
"provider": "fmp",
}
)
],
)
@pytest.mark.integration
def test_stocks_fa_ins_own(params, headers):
@@ -622,8 +633,8 @@ def test_stocks_ca_peers(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL"}),
({"date": "2023-01-01", "provider": "intrinio", "symbol": "AAPL"}),
({"symbol": "AAPL", "provider": "cboe"}),
({"date": "2023-01-25", "provider": "intrinio", "symbol": "AAPL"}),
({"provider": "cboe", "symbol": "AAPL"}),
],
)
@@ -641,10 +652,17 @@ def test_stocks_options_chains(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"period": "daily",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"provider": "yfinance",
}
),
(
{
"period": "intraday",
"interval": "60min",
"adjusted": True,
"extended_hours": True,
@@ -654,7 +672,7 @@ def test_stocks_options_chains(params, headers):
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"function_": "TIME_SERIES_DAILY",
"function_": "TIME_SERIES_INTRADAY",
}
),
(
@@ -702,10 +720,10 @@ def test_stocks_options_chains(params, headers):
"interval_size": "30m",
"provider": "intrinio",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"start_time": "00:00:00",
"end_time": "23:59:59",
"start_date": "2023-10-10",
"end_date": "2023-10-12",
"start_time": time(5, 30, 0),
"end_time": time(12, 0, 0),
}
),
(
@@ -715,10 +733,10 @@ def test_stocks_options_chains(params, headers):
"interval_size": "60m",
"provider": "intrinio",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"start_time": "00:00:00",
"end_time": "23:59:59",
"start_date": "2023-10-10",
"end_date": "2023-10-12",
"start_time": time(5, 30, 0),
"end_time": time(12, 0, 0),
}
),
(
@@ -731,7 +749,7 @@ def test_stocks_options_chains(params, headers):
"provider": "polygon",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"end_date": "2023-01-04",
}
),
(
@@ -749,7 +767,7 @@ def test_stocks_options_chains(params, headers):
),
(
{
"interval": "1m",
"interval": "1d",
"period": "max",
"prepost": True,
"actions": True,
@@ -764,7 +782,7 @@ def test_stocks_options_chains(params, headers):
"provider": "yfinance",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"end_date": "2023-01-08",
}
),
(
@@ -803,7 +821,7 @@ def test_stocks_load(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbols": "AAPL,MSFT", "limit": 20}),
({"symbols": "AAPL", "limit": 20, "provider": "benzinga"}),
(
{
"display": "full",
@@ -839,6 +857,7 @@ def test_stocks_load(params, headers):
"provider": "fmp",
"symbols": "AAPL",
"limit": 20,
"page": 1,
}
),
(
@@ -901,7 +920,6 @@ def test_stocks_search(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL"}),
({"source": "iex", "provider": "intrinio", "symbol": "AAPL"}),
({"symbol": "AAPL", "provider": "fmp"}),
],

View File

@@ -1,4 +1,6 @@
"""Test stocks extension."""
from datetime import time
import pytest
from openbb_core.app.model.obbject import OBBject
@@ -20,7 +22,7 @@ def obb(pytestconfig):
(
{
"type": "reported",
"year": 2023,
"year": 2022,
"provider": "intrinio",
"symbol": "AAPL",
"period": "annual",
@@ -118,7 +120,7 @@ def test_stocks_fa_cal(params, obb):
(
{
"type": "reported",
"year": 2023,
"year": 2022,
"provider": "intrinio",
"symbol": "AAPL",
"period": "annual",
@@ -286,7 +288,7 @@ def test_stocks_fa_est(params, obb):
(
{
"type": "reported",
"year": 2023,
"year": 2022,
"provider": "intrinio",
"symbol": "AAPL",
"period": "annual",
@@ -385,7 +387,7 @@ def test_stocks_fa_ins(params, obb):
{
"symbol": "AAPL",
"include_current_quarter": True,
"date": "2023-01-01",
"date": "2021-09-30",
}
),
],
@@ -437,7 +439,6 @@ def test_stocks_fa_overview(params, obb):
result = obb.stocks.fa.overview(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@pytest.mark.parametrize(
@@ -465,7 +466,6 @@ def test_stocks_fa_pt(params, obb):
result = obb.stocks.fa.pt(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@pytest.mark.parametrize(
@@ -592,14 +592,13 @@ def test_stocks_ca_peers(params, obb):
result = obb.stocks.ca.peers(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL"}),
({"date": "2023-01-01", "provider": "intrinio", "symbol": "AAPL"}),
({"date": "2023-01-25", "provider": "intrinio", "symbol": "AAPL"}),
({"provider": "cboe", "symbol": "AAPL"}),
],
)
@@ -623,7 +622,7 @@ def test_stocks_options_chains(params, obb):
),
(
{
"period": "daily",
"period": "intraday",
"interval": "60min",
"adjusted": True,
"extended_hours": True,
@@ -633,7 +632,7 @@ def test_stocks_options_chains(params, obb):
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"function_": "TIME_SERIES_DAILY",
"function_": "TIME_SERIES_INTRADAY",
}
),
(
@@ -681,10 +680,10 @@ def test_stocks_options_chains(params, obb):
"interval_size": "30m",
"provider": "intrinio",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"start_time": "00:00:00",
"end_time": "23:59:59",
"start_date": "2023-10-10",
"end_date": "2023-10-12",
"start_time": time(5, 30, 0),
"end_time": time(12, 0, 0),
}
),
(
@@ -694,10 +693,10 @@ def test_stocks_options_chains(params, obb):
"interval_size": "60m",
"provider": "intrinio",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"start_time": "00:00:00",
"end_time": "23:59:59",
"start_date": "2023-10-10",
"end_date": "2023-10-12",
"start_time": time(5, 30, 0),
"end_time": time(12, 0, 0),
}
),
(
@@ -710,7 +709,7 @@ def test_stocks_options_chains(params, obb):
"provider": "polygon",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"end_date": "2023-01-04",
}
),
(
@@ -728,7 +727,7 @@ def test_stocks_options_chains(params, obb):
),
(
{
"interval": "1m",
"interval": "1d",
"period": "max",
"prepost": True,
"actions": True,
@@ -743,7 +742,7 @@ def test_stocks_options_chains(params, obb):
"provider": "yfinance",
"symbol": "AAPL",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"end_date": "2023-01-08",
}
),
(
@@ -815,6 +814,7 @@ def test_stocks_load(params, obb):
"provider": "fmp",
"symbols": "AAPL",
"limit": 20,
"page": 1,
}
),
(
@@ -881,7 +881,6 @@ def test_stocks_quote(params, obb):
result = obb.stocks.quote(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@pytest.mark.parametrize(

View File

@@ -832,7 +832,7 @@ def ichimoku(
def clenow(
data: List[Data],
index: str = "date",
target: str = "adj_close",
target: str = "close",
period: PositiveInt = 90,
) -> OBBject[List[Data]]:
"""
@@ -845,7 +845,7 @@ def clenow(
index : str, optional
Index column name to use with `data`, by default "date".
target : str, optional
Target column name, by default "adj_close".
Target column name, by default "close".
period : PositiveInt, optional
Number of periods for the momentum, by default 90.

View File

@@ -103,8 +103,8 @@ def check_missing_params(
"""Check if there are any missing params for a command."""
missing_params = []
if not processing:
for test_params in function_params:
if "provider" in test_params:
for i, test_params in enumerate(function_params):
if "provider" in test_params and i != 0:
provider = test_params["provider"]
if provider in command_params:
for expected_param in command_params[provider]["QueryParams"][

View File

@@ -2,7 +2,7 @@
from datetime import datetime
from typing import Optional
from typing import Dict, List, Optional
from pydantic import Field, NonNegativeInt
@@ -23,6 +23,8 @@ class GlobalNewsData(Data):
date: datetime = Field(description="Published date of the news.")
title: str = Field(description="Title of the news.")
image: Optional[str] = Field(default=None, description="Image URL of the news.")
images: Optional[List[Dict[str, str]]] = Field(
default=None, description="Images associated with the news."
)
text: Optional[str] = Field(default=None, description="Text/body of the news.")
url: Optional[str] = Field(default=None, description="URL of the news.")

View File

@@ -60,7 +60,7 @@ class IncomeStatementData(Data):
general_and_administrative_expenses: Optional[StrictInt] = Field(
default=None, description="General and administrative expenses."
)
selling_and_marketing_expenses: float = Field(
selling_and_marketing_expenses: Optional[float] = Field(
default=None, description="Selling and marketing expenses."
)
selling_general_and_administrative_expenses: Optional[StrictInt] = Field(

View File

@@ -27,14 +27,15 @@ class OptionsChainsData(Data):
"""Options Chains Data."""
contract_symbol: str = Field(description="Contract symbol for the option.")
symbol: Optional[str] = Field(description="Underlying symbol for the option.")
symbol: Optional[str] = Field(
description="Underlying symbol for the option.", default=None
)
expiration: dateType = Field(description="Expiration date of the contract.")
strike: float = Field(description="Strike price of the contract.")
option_type: str = Field(description="Call or Put.")
eod_date: dateType = Field(
description="Date for which the options chains are returned."
eod_date: Optional[dateType] = Field(
default=None, description="Date for which the options chains are returned."
)
close: Optional[float] = Field(
default=None, description="Close price for the option that day."
)

View File

@@ -22,10 +22,22 @@ def get_querystring(items: dict, exclude: List[str]) -> str:
str
The querystring.
"""
for item in exclude:
items.pop(item)
params = {k: v for k, v in items.items() if v is not None}
return "&".join([f"{k}={v}" for k, v in params.items()])
for key in exclude:
items.pop(key, None)
query_items = []
for key, value in items.items():
if value is None:
continue
if isinstance(value, list):
for item in value:
query_items.append(f"{key}={item}")
else:
query_items.append(f"{key}={value}")
querystring = "&".join(query_items)
return f"{querystring}" if querystring else ""
def get_user_agent() -> str:

View File

@@ -89,9 +89,6 @@ class BenzingaGlobalNewsData(GlobalNewsData):
id: str = Field(description="ID of the news.")
author: Optional[str] = Field(default=None, description="Author of the news.")
teaser: Optional[str] = Field(description="Teaser of the news.", default=None)
images: Optional[List[Dict[str, str]]] = Field(
default=None, description="Images associated with the news."
)
channels: Optional[str] = Field(
default=None,
description="Channels associated with the news.",

View File

@@ -35,8 +35,12 @@ class CboeOptionsChainsData(OptionsChainsData):
open: float = Field(description="Opening price of the option.")
high: float = Field(description="High price of the option.")
low: float = Field(description="Low price of the option.")
last_trade_price: float = Field(description="Last trade price of the option.")
tick: str = Field(description="Whether the last tick was up or down in price.")
last_trade_price: Optional[float] = Field(
description="Last trade price of the option.", default=None
)
tick: Optional[str] = Field(
description="Whether the last tick was up or down in price.", default=None
)
prev_close: float = Field(description="Previous closing price of the option.")
change: float = Field(description="Change in price of the option.")
change_percent: float = Field(description="Change, in percent, of the option.")
@@ -46,8 +50,8 @@ class CboeOptionsChainsData(OptionsChainsData):
vega: float = Field(description="Vega of the option.")
theta: float = Field(description="Theta of the option.")
rho: float = Field(description="Rho of the option.")
last_trade_timestamp: datetime = Field(
description="Last trade timestamp of the option."
last_trade_timestamp: Optional[datetime] = Field(
description="Last trade timestamp of the option.", default=None
)
dte: int = Field(description="Days to expiration for the option.")
@@ -78,7 +82,6 @@ class CboeOptionsChainsFetcher(
**kwargs: Any,
) -> List[Dict]:
"""Return the raw data from the CBOE endpoint"""
symbol = query.symbol.upper()
INDEXES = get_cboe_index_directory()
@@ -144,7 +147,11 @@ class CboeOptionsChainsFetcher(
temp_ = (temp - now).days + 1
quotes["dte"] = temp_
quotes["last_trade_timestamp"] = pd.to_datetime(quotes["last_trade_timestamp"])
quotes["last_trade_timestamp"] = (
pd.to_datetime(quotes["last_trade_timestamp"], format="%Y-%m-%dT%H:%M:%S")
.fillna(value="-")
.replace("-", None)
)
quotes = quotes.set_index(
keys=["expiration", "strike", "option_type"]
).sort_index()

View File

@@ -25,6 +25,7 @@ class CboeStockSearchData(StockSearchData):
__alias_dict__ = {"name": "Company Name"}
dpm_name: Optional[str] = Field(
default=None,
description="Name of the primary market maker.",
alias="DPM Name",
)
@@ -55,7 +56,7 @@ class CboeStockSearchFetcher(
"""Return the raw data from the CBOE endpoint."""
data = {}
symbols = get_cboe_directory().reset_index()
symbols = get_cboe_directory().reset_index().replace("nan", None)
target = "name" if not query.is_symbol else "symbol"
idx = symbols[target].str.contains(query.query, case=False)
result = symbols[idx].to_dict("records")

View File

@@ -101,7 +101,7 @@ def get_cboe_directory(**kwargs) -> pd.DataFrame:
}
).set_index("symbol")
return CBOE_DIRECTORY
return CBOE_DIRECTORY.astype(str)
def get_cboe_index_directory(**kwargs) -> pd.DataFrame:

View File

@@ -23,7 +23,7 @@ class FMPGlobalNewsQueryParams(GlobalNewsQueryParams):
class FMPGlobalNewsData(GlobalNewsData):
"""FMP Global News Data."""
__alias_dict__ = {"date": "publishedDate"}
__alias_dict__ = {"date": "publishedDate", "images": "image"}
site: str = Field(description="Site of the news.")
@@ -73,4 +73,8 @@ class FMPGlobalNewsFetcher(
@staticmethod
def transform_data(data: List[Dict]) -> List[FMPGlobalNewsData]:
"""Return the transformed data."""
for d in data:
if isinstance(d["image"], str):
d["image"] = [{"url": d["image"]}]
return [FMPGlobalNewsData.model_validate(d) for d in data]

View File

@@ -1,10 +1,8 @@
"""FMP Stock News fetcher."""
from typing import Any, Dict, List, Optional, Union
import math
from typing import Any, Dict, List, Optional
from openbb_fmp.utils.helpers import get_data_many, get_querystring
from openbb_fmp.utils.helpers import get_data_many
from openbb_provider.abstract.fetcher import Fetcher
from openbb_provider.standard_models.stock_news import (
StockNewsData,
@@ -20,6 +18,10 @@ class FMPStockNewsQueryParams(StockNewsQueryParams):
"""
__alias_dict__ = {"symbols": "tickers"}
page: Optional[int] = Field(
default=0,
description="Page number of the results. Use in combination with limit.",
)
class FMPStockNewsData(StockNewsData):
@@ -28,10 +30,13 @@ class FMPStockNewsData(StockNewsData):
__alias_dict__ = {"date": "publishedDate"}
symbol: str = Field(description="Ticker of the fetched news.")
image: Optional[str] = Field(
image: Optional[Union[List[str], str]] = Field(
default=None, description="URL to the image of the news source."
)
site: str = Field(description="Name of the news source.")
images: Optional[Union[List[str], str]] = Field(
default=None, description="URL to the images of the news."
)
class FMPStockNewsFetcher(
@@ -57,18 +62,12 @@ class FMPStockNewsFetcher(
api_key = credentials.get("fmp_api_key") if credentials else ""
base_url = "https://financialmodelingprep.com/api/v3/stock_news"
querystring = get_querystring(query.dict(by_alias=True), [])
pages = math.ceil(query.limit / 20)
data = []
url = f"{base_url}?page={query.page}&tickers={query.symbols}&limit={query.limit}&apikey={api_key}"
response = get_data_many(url, **kwargs)
for page in range(pages):
url = f"{base_url}?{querystring}&page={page}&apikey={api_key}"
response = get_data_many(url, **kwargs)
data.extend(response)
data = sorted(data, key=lambda x: x["publishedDate"], reverse=True)
data = data[: query.limit]
if len(response) > 0:
data = sorted(response, key=lambda x: x["publishedDate"], reverse=True)
return data

View File

@@ -5,7 +5,7 @@ interactions:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
- gzip, deflate, br
Connection:
- keep-alive
method: GET
@@ -13,87 +13,75 @@ interactions:
response:
body:
string: !!binary |
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
oiqHYW846PfnjMQ//Nf/Ax+dqQ3BMAAA
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
YcS5Df4f/uP/AKQcBDeJLgAA
headers:
Access-Control-Allow-Credentials:
- 'true'
@@ -113,9 +101,9 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Tue, 26 Sep 2023 19:06:50 GMT
- Fri, 13 Oct 2023 21:33:39 GMT
ETag:
- W/"30c1-DIC4kmnLx2yBO6GazRdUWPNQ9jQ"
- W/"2e89-yUW5rQLjZuLw87tvcqs2itEGTC8"
Server:
- nginx/1.18.0 (Ubuntu)
Transfer-Encoding:

View File

@@ -58,23 +58,32 @@ class FREDYieldCurveFetcher(
fred = Fred(api_key)
vals = []
value = None
for series in fred_series.values():
data = fred.get_series(series, start_date=start_date, **kwargs)
if date:
# if date is not empty, loop through the data and find the closest value
value = sorted(
sorted_data = sorted(
data,
key=lambda x: abs(
datetime.strptime(x["date"], "%Y-%m-%d").date() - date
key=lambda item: abs(
(date - datetime.strptime(item["date"], "%Y-%m-%d").date()).days
),
)[0]["value"]
)
for item in sorted_data:
if item.get("value") and item["value"] != ".":
value = float(item["value"])
break
else:
# if date is empty, find the most recent date's value
sorted_data = sorted(data, key=lambda x: x["date"], reverse=True)
value = sorted_data[0]["value"] if sorted_data else None
value = float(sorted_data[0]["value"]) if sorted_data else None
if isinstance(value, (float, int)):
vals.append(value)
vals.append(value)
yield_curve_data = []
for maturity, rate in zip(years, vals):
yield_curve_data.append({"maturity": maturity, "rate": rate})

View File

@@ -95,7 +95,6 @@ class IntrinioBalanceSheetFetcher(
@staticmethod
def transform_data(data: List[Dict]) -> List[IntrinioBalanceSheetData]:
"""Return the transformed data."""
transformed_data = []
for item in data:

View File

@@ -45,7 +45,6 @@ class IntrinioFredHistoricalFetcher(
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioFredHistoricalQueryParams:
"""Transform the query params."""
transformed_params = params
now = datetime.now().date()
@@ -64,7 +63,6 @@ class IntrinioFredHistoricalFetcher(
**kwargs: Any,
) -> List[Dict]:
"""Return the raw data from the Intrinio endpoint."""
api_key = credentials.get("intrinio_api_key") if credentials else ""
base_url = "https://api-v2.intrinio.com"

View File

@@ -43,8 +43,10 @@ class IntrinioOptionsChainsData(OptionsChainsData):
@field_validator("expiration", "date", mode="before", check_fields=False)
@classmethod
def date_validate(cls, v): # pylint: disable=E0213
"""Return the datetime object from the date string"""
return parser.parse(v)
"""Return the datetime object from the date string."""
# only pass it to the parser if it is not a datetime object
if isinstance(v, str):
return parser.parse(v)
class IntrinioOptionsChainsFetcher(
@@ -60,8 +62,10 @@ class IntrinioOptionsChainsFetcher(
now = datetime.now().date()
if params.get("date") is None:
transform_params["date"] = (now - timedelta(days=1)).strftime("%Y-%m-%d")
transform_params["date"] = parser.parse(transform_params["date"]).date()
elif isinstance(params["date"], dateType):
transform_params["date"] = params["date"].strftime("%Y-%m-%d")
else:
transform_params["date"] = parser.parse(params["date"]).date()
return IntrinioOptionsChainsQueryParams(**transform_params)

View File

@@ -1,5 +1,4 @@
import ssl
import sys
from io import StringIO
from typing import Any, Dict, Optional
@@ -8,12 +7,6 @@ import urllib3
from openbb_provider import helpers
from pandas import DataFrame, read_csv
if sys.version_info >= (3, 10):
PATCH = True
else:
PATCH = False
# OECD does not play well with newer python. This code block from stackoverflow helps
# to create a custom session:
@@ -43,20 +36,9 @@ def get_legacy_session():
return session
def create_session():
"""Create a session for making requests to the OECD API.
This will create the patched session if the python version is >= 3.10. Otherwise,
it will just use regular requests.
"""
if PATCH:
return get_legacy_session()
return requests.Session()
def fetch_data(url: str, csv_kwargs: Optional[Dict] = None, **kwargs: Any) -> DataFrame:
"""Create a session and fetch data from the OECD API."""
session = create_session()
session = get_legacy_session()
response = helpers.make_request(url, session=session, **kwargs)
if csv_kwargs is None:
csv_kwargs = {}

View File

@@ -108,7 +108,7 @@ class PolygonForexPairsFetcher(
transform_params = params
now = datetime.now().date()
transform_params["symbol"] = (
f"ticker=C:{params.get('symbol')}" if params.get("symbol") else ""
f"ticker=C:{params.get('symbol').upper()}" if params.get("symbol") else ""
)
if params.get("date") is None:
transform_params["start_date"] = now

View File

@@ -36,6 +36,8 @@ class PolygonStockNewsQueryParams(StockNewsQueryParams):
class PolygonPublisher(BaseModel):
"""PolygonPublisher data model."""
favicon_url: str = Field(description="Favicon URL.")
homepage_url: str = Field(description="Homepage URL.")
logo_url: str = Field(description="Logo URL.")
@@ -43,7 +45,7 @@ class PolygonPublisher(BaseModel):
class PolygonStockNewsData(StockNewsData):
"""Source: https://polygon.io/docs/stocks/get_v2_reference_news"""
"""Source: https://polygon.io/docs/stocks/get_v2_reference_news."""
__alias_dict__ = {
"url": "article_url",
@@ -77,7 +79,7 @@ class PolygonStockNewsFetcher(
query: PolygonStockNewsQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> dict:
) -> List[Dict]:
api_key = credentials.get("polygon_api_key") if credentials else ""
base_url = "https://api.polygon.io/v2/reference/news"
@@ -90,6 +92,11 @@ class PolygonStockNewsFetcher(
query.model_dump(by_alias=True), ["published_utc"]
)
query_str += f"&published_utc.{condition}={date}"
else:
query_str = get_querystring(
query.model_dump(by_alias=True), ["published_utc"]
)
query_str += f"&published_utc={date}"
else:
query_str = get_querystring(query.model_dump(by_alias=True), [])
@@ -100,6 +107,6 @@ class PolygonStockNewsFetcher(
@staticmethod
def transform_data(
data: dict,
data: List[Dict],
) -> List[PolygonStockNewsData]:
return [PolygonStockNewsData.model_validate(d) for d in data]

View File

@@ -54,12 +54,13 @@ class YFinanceBalanceSheetFetcher(
) -> List[Dict]:
period = "yearly" if query.period == "annual" else "quarterly" # type: ignore
data = Ticker(query.symbol).get_balance_sheet(
as_dict=True, pretty=False, freq=period
as_dict=False, pretty=False, freq=period
)
if not data:
if data is None:
raise EmptyDataError()
data = data.convert_dtypes().fillna(0).to_dict()
data = [{"date": str(key), **value} for key, value in data.items()]
# To match standardization
for d in data:

View File

@@ -54,12 +54,14 @@ class YFinanceCashFlowStatementFetcher(
) -> List[YFinanceCashFlowStatementData]:
period = "yearly" if query.period == "annual" else "quarterly" # type: ignore
data = Ticker(query.symbol).get_cash_flow(
as_dict=True, pretty=False, freq=period
as_dict=False, pretty=False, freq=period
)
if not data:
if data is None:
raise EmptyDataError()
data = data.fillna(0).to_dict()
data = [{"date": str(key), **value} for key, value in data.items()]
# To match standardization
for d in data:

View File

@@ -72,6 +72,9 @@ class YFinanceFuturesHistoricalFetcher(
if params.get("end_date") is None:
transformed_params["end_date"] = now
else:
transformed_params = params
return YFinanceFuturesHistoricalQueryParams(**transformed_params)
@staticmethod

View File

@@ -50,8 +50,9 @@ class YFinanceIncomeStatementFetcher(
) -> List[YFinanceIncomeStatementData]:
period = "yearly" if query.period == "annual" else "quarterly"
data = Ticker(query.symbol).get_income_stmt(
as_dict=True, pretty=False, freq=period
as_dict=False, pretty=False, freq=period
)
data = data.convert_dtypes().fillna(0).to_dict()
data = [{"date": str(key), **value} for key, value in data.items()]
# To match standardization
for d in data: