From a5d2e833c47160e2373de4e3b054e9e6dd5e00d1 Mon Sep 17 00:00:00 2001 From: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:57:27 +0200 Subject: [PATCH] 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 --- .gitignore | 6 +- .../charting/integration/test_charting_api.py | 97 ++++++++--- .../openbb_charting/core/openbb_figure.py | 27 ++- .../crypto/integration/test_crypto_api.py | 13 +- .../crypto/integration/test_crypto_python.py | 15 +- .../economy/integration/test_economy_api.py | 48 +++--- .../integration/test_economy_python.py | 80 +++++---- .../forex/integration/test_forex_api.py | 32 ++-- .../forex/integration/test_forex_python.py | 35 ++-- .../futures/integration/test_futures_api.py | 8 +- .../integration/test_futures_python.py | 10 +- .../news/integration/test_news_api.py | 4 +- .../stocks/integration/test_stocks_api.py | 100 ++++++----- .../stocks/integration/test_stocks_python.py | 43 +++-- .../extensions/ta/openbb_ta/ta_router.py | 4 +- .../tests/utils/integration_tests_testers.py | 4 +- .../standard_models/global_news.py | 6 +- .../standard_models/income_statement.py | 2 +- .../standard_models/options_chains.py | 9 +- .../provider/openbb_provider/utils/helpers.py | 20 ++- .../openbb_benzinga/models/global_news.py | 3 - .../cboe/openbb_cboe/models/options_chains.py | 19 ++- .../cboe/openbb_cboe/models/stock_search.py | 3 +- .../cboe/openbb_cboe/utils/helpers.py | 2 +- .../fmp/openbb_fmp/models/global_news.py | 6 +- .../fmp/openbb_fmp/models/stock_news.py | 29 ++-- .../test_fmp_stock_news_fetcher.yaml | 156 ++++++++---------- .../fred/openbb_fred/models/us_yield_curve.py | 21 ++- .../openbb_intrinio/models/balance_sheet.py | 1 - .../openbb_intrinio/models/fred_historical.py | 2 - .../openbb_intrinio/models/options_chains.py | 12 +- .../oecd/openbb_oecd/utils/helpers.py | 20 +-- .../openbb_polygon/models/forex_pairs.py | 2 +- .../openbb_polygon/models/stock_news.py | 13 +- .../openbb_yfinance/models/balance_sheet.py | 7 +- .../openbb_yfinance/models/cash_flow.py | 6 +- .../models/futures_historical.py | 3 + .../models/income_statement.py | 3 +- 38 files changed, 495 insertions(+), 376 deletions(-) diff --git a/.gitignore b/.gitignore index 9eb3b9ed2e7..3c14d22020c 100644 --- a/.gitignore +++ b/.gitignore @@ -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/* \ No newline at end of file diff --git a/openbb_platform/extensions/charting/integration/test_charting_api.py b/openbb_platform/extensions/charting/integration/test_charting_api.py index 7e0815f67e0..cb87ada8de8 100644 --- a/openbb_platform/extensions/charting/integration/test_charting_api.py +++ b/openbb_platform/extensions/charting/integration/test_charting_api.py @@ -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"] diff --git a/openbb_platform/extensions/charting/openbb_charting/core/openbb_figure.py b/openbb_platform/extensions/charting/openbb_charting/core/openbb_figure.py index b1592635f4a..1511d6faf2c 100644 --- a/openbb_platform/extensions/charting/openbb_charting/core/openbb_figure.py +++ b/openbb_platform/extensions/charting/openbb_charting/core/openbb_figure.py @@ -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 diff --git a/openbb_platform/extensions/crypto/integration/test_crypto_api.py b/openbb_platform/extensions/crypto/integration/test_crypto_api.py index 9dc6f14aca2..adeb82197e2 100644 --- a/openbb_platform/extensions/crypto/integration/test_crypto_api.py +++ b/openbb_platform/extensions/crypto/integration/test_crypto_api.py @@ -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", } ), ( diff --git a/openbb_platform/extensions/crypto/integration/test_crypto_python.py b/openbb_platform/extensions/crypto/integration/test_crypto_python.py index f621443a166..070ffbf4df0 100644 --- a/openbb_platform/extensions/crypto/integration/test_crypto_python.py +++ b/openbb_platform/extensions/crypto/integration/test_crypto_python.py @@ -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", diff --git a/openbb_platform/extensions/economy/integration/test_economy_api.py b/openbb_platform/extensions/economy/integration/test_economy_api.py index af175cf83ff..c4bf967dc80 100644 --- a/openbb_platform/extensions/economy/integration/test_economy_api.py +++ b/openbb_platform/extensions/economy/integration/test_economy_api.py @@ -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", } ) ], diff --git a/openbb_platform/extensions/economy/integration/test_economy_python.py b/openbb_platform/extensions/economy/integration/test_economy_python.py index c217a28595d..615eeec9a2f 100644 --- a/openbb_platform/extensions/economy/integration/test_economy_python.py +++ b/openbb_platform/extensions/economy/integration/test_economy_python.py @@ -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 diff --git a/openbb_platform/extensions/forex/integration/test_forex_api.py b/openbb_platform/extensions/forex/integration/test_forex_api.py index d54c76f7fb0..d3ab3b14abc 100644 --- a/openbb_platform/extensions/forex/integration/test_forex_api.py +++ b/openbb_platform/extensions/forex/integration/test_forex_api.py @@ -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, diff --git a/openbb_platform/extensions/forex/integration/test_forex_python.py b/openbb_platform/extensions/forex/integration/test_forex_python.py index 5d2beb30d5e..89861140fe5 100644 --- a/openbb_platform/extensions/forex/integration/test_forex_python.py +++ b/openbb_platform/extensions/forex/integration/test_forex_python.py @@ -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", diff --git a/openbb_platform/extensions/futures/integration/test_futures_api.py b/openbb_platform/extensions/futures/integration/test_futures_api.py index 2ad25d8f258..21ddc3d0ee6 100644 --- a/openbb_platform/extensions/futures/integration/test_futures_api.py +++ b/openbb_platform/extensions/futures/integration/test_futures_api.py @@ -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 diff --git a/openbb_platform/extensions/futures/integration/test_futures_python.py b/openbb_platform/extensions/futures/integration/test_futures_python.py index b04a4b6ef53..7581dc0a4bb 100644 --- a/openbb_platform/extensions/futures/integration/test_futures_python.py +++ b/openbb_platform/extensions/futures/integration/test_futures_python.py @@ -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 diff --git a/openbb_platform/extensions/news/integration/test_news_api.py b/openbb_platform/extensions/news/integration/test_news_api.py index d7bca14967d..b1f4b004b94 100644 --- a/openbb_platform/extensions/news/integration/test_news_api.py +++ b/openbb_platform/extensions/news/integration/test_news_api.py @@ -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, } ), ( diff --git a/openbb_platform/extensions/stocks/integration/test_stocks_api.py b/openbb_platform/extensions/stocks/integration/test_stocks_api.py index fc7a508c3dc..cd7766ed239 100644 --- a/openbb_platform/extensions/stocks/integration/test_stocks_api.py +++ b/openbb_platform/extensions/stocks/integration/test_stocks_api.py @@ -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"}), ], diff --git a/openbb_platform/extensions/stocks/integration/test_stocks_python.py b/openbb_platform/extensions/stocks/integration/test_stocks_python.py index 482da26c329..e89ef2149f7 100644 --- a/openbb_platform/extensions/stocks/integration/test_stocks_python.py +++ b/openbb_platform/extensions/stocks/integration/test_stocks_python.py @@ -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( diff --git a/openbb_platform/extensions/ta/openbb_ta/ta_router.py b/openbb_platform/extensions/ta/openbb_ta/ta_router.py index 6ebf6fdd98a..4a115d84aa2 100644 --- a/openbb_platform/extensions/ta/openbb_ta/ta_router.py +++ b/openbb_platform/extensions/ta/openbb_ta/ta_router.py @@ -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. diff --git a/openbb_platform/extensions/tests/utils/integration_tests_testers.py b/openbb_platform/extensions/tests/utils/integration_tests_testers.py index 76bab8b1408..d85e8ce1d2e 100644 --- a/openbb_platform/extensions/tests/utils/integration_tests_testers.py +++ b/openbb_platform/extensions/tests/utils/integration_tests_testers.py @@ -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"][ diff --git a/openbb_platform/platform/provider/openbb_provider/standard_models/global_news.py b/openbb_platform/platform/provider/openbb_provider/standard_models/global_news.py index 3779d93cc88..0b29a18d2e7 100644 --- a/openbb_platform/platform/provider/openbb_provider/standard_models/global_news.py +++ b/openbb_platform/platform/provider/openbb_provider/standard_models/global_news.py @@ -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.") diff --git a/openbb_platform/platform/provider/openbb_provider/standard_models/income_statement.py b/openbb_platform/platform/provider/openbb_provider/standard_models/income_statement.py index e413f5b2759..2dfe6022a12 100644 --- a/openbb_platform/platform/provider/openbb_provider/standard_models/income_statement.py +++ b/openbb_platform/platform/provider/openbb_provider/standard_models/income_statement.py @@ -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( diff --git a/openbb_platform/platform/provider/openbb_provider/standard_models/options_chains.py b/openbb_platform/platform/provider/openbb_provider/standard_models/options_chains.py index 8ad30b2ba2a..2fa8f844f9b 100644 --- a/openbb_platform/platform/provider/openbb_provider/standard_models/options_chains.py +++ b/openbb_platform/platform/provider/openbb_provider/standard_models/options_chains.py @@ -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." ) diff --git a/openbb_platform/platform/provider/openbb_provider/utils/helpers.py b/openbb_platform/platform/provider/openbb_provider/utils/helpers.py index 54524750b9d..ae60edb8015 100644 --- a/openbb_platform/platform/provider/openbb_provider/utils/helpers.py +++ b/openbb_platform/platform/provider/openbb_provider/utils/helpers.py @@ -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: diff --git a/openbb_platform/providers/benzinga/openbb_benzinga/models/global_news.py b/openbb_platform/providers/benzinga/openbb_benzinga/models/global_news.py index 37b686f293d..3c9695cf4c5 100644 --- a/openbb_platform/providers/benzinga/openbb_benzinga/models/global_news.py +++ b/openbb_platform/providers/benzinga/openbb_benzinga/models/global_news.py @@ -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.", diff --git a/openbb_platform/providers/cboe/openbb_cboe/models/options_chains.py b/openbb_platform/providers/cboe/openbb_cboe/models/options_chains.py index c43b416df4f..2da066e7576 100644 --- a/openbb_platform/providers/cboe/openbb_cboe/models/options_chains.py +++ b/openbb_platform/providers/cboe/openbb_cboe/models/options_chains.py @@ -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() diff --git a/openbb_platform/providers/cboe/openbb_cboe/models/stock_search.py b/openbb_platform/providers/cboe/openbb_cboe/models/stock_search.py index 30aa8102028..9bf7dfa45c6 100644 --- a/openbb_platform/providers/cboe/openbb_cboe/models/stock_search.py +++ b/openbb_platform/providers/cboe/openbb_cboe/models/stock_search.py @@ -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") diff --git a/openbb_platform/providers/cboe/openbb_cboe/utils/helpers.py b/openbb_platform/providers/cboe/openbb_cboe/utils/helpers.py index 00f79a67810..861eaf51f18 100644 --- a/openbb_platform/providers/cboe/openbb_cboe/utils/helpers.py +++ b/openbb_platform/providers/cboe/openbb_cboe/utils/helpers.py @@ -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: diff --git a/openbb_platform/providers/fmp/openbb_fmp/models/global_news.py b/openbb_platform/providers/fmp/openbb_fmp/models/global_news.py index 9f1d4f1f36f..65e36bceabf 100644 --- a/openbb_platform/providers/fmp/openbb_fmp/models/global_news.py +++ b/openbb_platform/providers/fmp/openbb_fmp/models/global_news.py @@ -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] diff --git a/openbb_platform/providers/fmp/openbb_fmp/models/stock_news.py b/openbb_platform/providers/fmp/openbb_fmp/models/stock_news.py index 8747f7f7cf9..d9a8ada077d 100644 --- a/openbb_platform/providers/fmp/openbb_fmp/models/stock_news.py +++ b/openbb_platform/providers/fmp/openbb_fmp/models/stock_news.py @@ -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 diff --git a/openbb_platform/providers/fmp/tests/record/http/test_fmp_fetchers/test_fmp_stock_news_fetcher.yaml b/openbb_platform/providers/fmp/tests/record/http/test_fmp_fetchers/test_fmp_stock_news_fetcher.yaml index fd867a646be..c13d317516b 100644 --- a/openbb_platform/providers/fmp/tests/record/http/test_fmp_fetchers/test_fmp_stock_news_fetcher.yaml +++ b/openbb_platform/providers/fmp/tests/record/http/test_fmp_fetchers/test_fmp_stock_news_fetcher.yaml @@ -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: diff --git a/openbb_platform/providers/fred/openbb_fred/models/us_yield_curve.py b/openbb_platform/providers/fred/openbb_fred/models/us_yield_curve.py index d1bb47c8ec8..79bb30b0eab 100644 --- a/openbb_platform/providers/fred/openbb_fred/models/us_yield_curve.py +++ b/openbb_platform/providers/fred/openbb_fred/models/us_yield_curve.py @@ -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}) diff --git a/openbb_platform/providers/intrinio/openbb_intrinio/models/balance_sheet.py b/openbb_platform/providers/intrinio/openbb_intrinio/models/balance_sheet.py index f9ec010b462..77c60d76256 100644 --- a/openbb_platform/providers/intrinio/openbb_intrinio/models/balance_sheet.py +++ b/openbb_platform/providers/intrinio/openbb_intrinio/models/balance_sheet.py @@ -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: diff --git a/openbb_platform/providers/intrinio/openbb_intrinio/models/fred_historical.py b/openbb_platform/providers/intrinio/openbb_intrinio/models/fred_historical.py index 8b443660017..2e0fbedc966 100644 --- a/openbb_platform/providers/intrinio/openbb_intrinio/models/fred_historical.py +++ b/openbb_platform/providers/intrinio/openbb_intrinio/models/fred_historical.py @@ -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" diff --git a/openbb_platform/providers/intrinio/openbb_intrinio/models/options_chains.py b/openbb_platform/providers/intrinio/openbb_intrinio/models/options_chains.py index e48e4689e71..aa62101648a 100644 --- a/openbb_platform/providers/intrinio/openbb_intrinio/models/options_chains.py +++ b/openbb_platform/providers/intrinio/openbb_intrinio/models/options_chains.py @@ -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) diff --git a/openbb_platform/providers/oecd/openbb_oecd/utils/helpers.py b/openbb_platform/providers/oecd/openbb_oecd/utils/helpers.py index 5b2f285a43e..448bf8763c9 100644 --- a/openbb_platform/providers/oecd/openbb_oecd/utils/helpers.py +++ b/openbb_platform/providers/oecd/openbb_oecd/utils/helpers.py @@ -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 = {} diff --git a/openbb_platform/providers/polygon/openbb_polygon/models/forex_pairs.py b/openbb_platform/providers/polygon/openbb_polygon/models/forex_pairs.py index c5b27d73bd4..93104cbb452 100644 --- a/openbb_platform/providers/polygon/openbb_polygon/models/forex_pairs.py +++ b/openbb_platform/providers/polygon/openbb_polygon/models/forex_pairs.py @@ -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 diff --git a/openbb_platform/providers/polygon/openbb_polygon/models/stock_news.py b/openbb_platform/providers/polygon/openbb_polygon/models/stock_news.py index 3482583dae9..863996a42dc 100644 --- a/openbb_platform/providers/polygon/openbb_polygon/models/stock_news.py +++ b/openbb_platform/providers/polygon/openbb_polygon/models/stock_news.py @@ -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] diff --git a/openbb_platform/providers/yfinance/openbb_yfinance/models/balance_sheet.py b/openbb_platform/providers/yfinance/openbb_yfinance/models/balance_sheet.py index 06eec88b2ad..6f665928911 100644 --- a/openbb_platform/providers/yfinance/openbb_yfinance/models/balance_sheet.py +++ b/openbb_platform/providers/yfinance/openbb_yfinance/models/balance_sheet.py @@ -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: diff --git a/openbb_platform/providers/yfinance/openbb_yfinance/models/cash_flow.py b/openbb_platform/providers/yfinance/openbb_yfinance/models/cash_flow.py index a00cf17f32c..cae3695cfb1 100644 --- a/openbb_platform/providers/yfinance/openbb_yfinance/models/cash_flow.py +++ b/openbb_platform/providers/yfinance/openbb_yfinance/models/cash_flow.py @@ -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: diff --git a/openbb_platform/providers/yfinance/openbb_yfinance/models/futures_historical.py b/openbb_platform/providers/yfinance/openbb_yfinance/models/futures_historical.py index 12f50f3855e..2d4cc54f75f 100644 --- a/openbb_platform/providers/yfinance/openbb_yfinance/models/futures_historical.py +++ b/openbb_platform/providers/yfinance/openbb_yfinance/models/futures_historical.py @@ -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 diff --git a/openbb_platform/providers/yfinance/openbb_yfinance/models/income_statement.py b/openbb_platform/providers/yfinance/openbb_yfinance/models/income_statement.py index 3ecd0303edc..4988f1cfeab 100644 --- a/openbb_platform/providers/yfinance/openbb_yfinance/models/income_statement.py +++ b/openbb_platform/providers/yfinance/openbb_yfinance/models/income_statement.py @@ -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: