summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanglewood <85772166+deeleeramone@users.noreply.github.com>2024-07-03 11:59:10 -0700
committerGitHub <noreply@github.com>2024-07-03 11:59:10 -0700
commitd9abc6b04490760bf8a4b99f6420838313c02d06 (patch)
tree2f7c710b6eebfc05667b7ef72178e77c16312eca
parentde85d38edf6f66d7b76e9c52a0f0762bd5409c56 (diff)
parent0bd6cd0a1e7401fde7947c7f2bac5ec10a914cc6 (diff)
Merge branch 'develop' into feature/openbb-store
-rw-r--r--.github/scripts/noxfile.py4
-rw-r--r--.github/workflows/test-unit-cli.yml2
-rw-r--r--.github/workflows/test-unit-platform.yml2
-rw-r--r--cli/pyproject.toml2
-rw-r--r--openbb_platform/core/openbb_core/app/static/utils/decorators.py2
-rw-r--r--openbb_platform/core/openbb_core/app/utils.py10
-rw-r--r--openbb_platform/core/openbb_core/provider/standard_models/futures_curve.py15
-rw-r--r--openbb_platform/core/tests/app/test_command_runner.py17
-rw-r--r--openbb_platform/dev_install.py2
-rw-r--r--openbb_platform/extensions/derivatives/integration/test_derivatives_api.py18
-rw-r--r--openbb_platform/extensions/derivatives/integration/test_derivatives_python.py4
-rw-r--r--openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_views.py219
-rw-r--r--openbb_platform/extensions/derivatives/openbb_derivatives/futures/futures_router.py2
-rw-r--r--openbb_platform/extensions/devtools/poetry.lock7
-rw-r--r--openbb_platform/extensions/devtools/pyproject.toml2
-rw-r--r--openbb_platform/extensions/econometrics/poetry.lock104
-rw-r--r--openbb_platform/extensions/econometrics/pyproject.toml2
-rw-r--r--openbb_platform/extensions/quantitative/poetry.lock503
-rw-r--r--openbb_platform/extensions/quantitative/pyproject.toml1
-rw-r--r--openbb_platform/extensions/technical/poetry.lock5
-rw-r--r--openbb_platform/extensions/technical/pyproject.toml2
-rw-r--r--openbb_platform/extensions/tests/utils/helpers.py35
-rw-r--r--openbb_platform/obbject_extensions/charting/integration/test_charting_api.py36
-rw-r--r--openbb_platform/obbject_extensions/charting/integration/test_charting_python.py29
-rw-r--r--openbb_platform/obbject_extensions/charting/openbb_charting/charting.py12
-rw-r--r--openbb_platform/obbject_extensions/charting/openbb_charting/query_params.py2
-rw-r--r--openbb_platform/obbject_extensions/charting/poetry.lock406
-rw-r--r--openbb_platform/obbject_extensions/charting/pyproject.toml1
-rw-r--r--openbb_platform/providers/cboe/openbb_cboe/models/futures_curve.py64
-rw-r--r--openbb_platform/providers/cboe/openbb_cboe/utils/vix.py326
-rw-r--r--openbb_platform/providers/cboe/tests/record/http/test_cboe_fetchers/test_cboe_futures_curve_fetcher_urllib3_v1.yaml7180
-rw-r--r--openbb_platform/providers/cboe/tests/record/http/test_cboe_fetchers/test_cboe_futures_curve_fetcher_urllib3_v2.yaml7261
-rw-r--r--openbb_platform/providers/cboe/tests/test_cboe_fetchers.py2
-rw-r--r--openbb_platform/providers/intrinio/openbb_intrinio/models/options_snapshots.py2
-rw-r--r--openbb_platform/providers/polygon/openbb_polygon/models/currency_snapshots.py4
-rw-r--r--openbb_platform/providers/yfinance/openbb_yfinance/models/futures_curve.py49
-rw-r--r--openbb_platform/providers/yfinance/openbb_yfinance/utils/helpers.py343
-rw-r--r--openbb_platform/pyproject.toml2
38 files changed, 15943 insertions, 736 deletions
diff --git a/.github/scripts/noxfile.py b/.github/scripts/noxfile.py
index 5bc4af2ce43..7e315394f11 100644
--- a/.github/scripts/noxfile.py
+++ b/.github/scripts/noxfile.py
@@ -13,7 +13,7 @@ CLI_DIR = ROOT_DIR / "cli"
CLI_TESTS = CLI_DIR / "tests"
-@nox.session(python=["3.9", "3.10", "3.11"])
+@nox.session(python=["3.9", "3.10", "3.11", "3.12"])
def unit_test_platform(session):
"""Run the test suite."""
session.install("poetry", "toml")
@@ -31,7 +31,7 @@ def unit_test_platform(session):
)
-@nox.session(python=["3.9", "3.10", "3.11"])
+@nox.session(python=["3.9", "3.10", "3.11", "3.12"])
def unit_test_cli(session):
"""Run the test suite."""
session.install("poetry", "toml")
diff --git a/.github/workflows/test-unit-cli.yml b/.github/workflows/test-unit-cli.yml
index fece7a9397a..fb0562c40ac 100644
--- a/.github/workflows/test-unit-cli.yml
+++ b/.github/workflows/test-unit-cli.yml
@@ -19,7 +19,7 @@ jobs:
matrix:
python_version:
- ["3.9", "3.10", "3.11"]
+ ["3.9", "3.10", "3.11", "3.12"]
steps:
- name: Checkout code
uses: actions/checkout@v4
diff --git a/.github/workflows/test-unit-platform.yml b/.github/workflows/test-unit-platform.yml
index 0e8f9002091..a22cc673ea9 100644
--- a/.github/workflows/test-unit-platform.yml
+++ b/.github/workflows/test-unit-platform.yml
@@ -19,7 +19,7 @@ jobs:
matrix:
python_version:
- ["3.9", "3.10", "3.11"]
+ ["3.9", "3.10", "3.11", "3.12"]
steps:
- name: Checkout code
uses: actions/checkout@v4
diff --git a/cli/pyproject.toml b/cli/pyproject.toml
index 5aaf5c6c875..afdb8041189 100644
--- a/cli/pyproject.toml
+++ b/cli/pyproject.toml
@@ -14,7 +14,7 @@ documentation = "https://docs.openbb.co/terminal"
openbb = 'openbb_cli.cli:main'
[tool.poetry.dependencies]
-python = "^3.9,<3.12"
+python = "^3.9,<3.13"
# OpenBB dependencies
openbb = { version = "^4.2.3", extras = ["all"] }
diff --git a/openbb_platform/core/openbb_core/app/static/utils/decorators.py b/openbb_platform/core/openbb_core/app/static/utils/decorators.py
index d82f90d5a6b..0b2ea4f0ddd 100644
--- a/openbb_platform/core/openbb_core/app/static/utils/decorators.py
+++ b/openbb_platform/core/openbb_core/app/static/utils/decorators.py
@@ -57,7 +57,7 @@ def exception_handler(func: Callable[P, R]) -> Callable[P, R]:
while tb.tb_next is not None:
tb = tb.tb_next
- if isinstance(e, ValidationError):
+ if isinstance(e, ValidationError) and "Data" not in e.title:
error_list = []
validation_error = f"{e.error_count()} validations error(s)"
for err in e.errors(include_url=False):
diff --git a/openbb_platform/core/openbb_core/app/utils.py b/openbb_platform/core/openbb_core/app/utils.py
index 452300c4334..366fcb6e447 100644
--- a/openbb_platform/core/openbb_core/app/utils.py
+++ b/openbb_platform/core/openbb_core/app/utils.py
@@ -21,12 +21,16 @@ def basemodel_to_df(
) -> pd.DataFrame:
"""Convert list of BaseModel to a Pandas DataFrame."""
if isinstance(data, list):
- df = pd.DataFrame([d.model_dump() for d in data])
+ df = pd.DataFrame(
+ [d.model_dump(exclude_none=True, exclude_unset=True) for d in data]
+ )
else:
try:
- df = pd.DataFrame(data.model_dump())
+ df = pd.DataFrame(data.model_dump(exclude_none=True, exclude_unset=True))
except ValueError:
- df = pd.DataFrame(data.model_dump(), index=["values"])
+ df = pd.DataFrame(
+ data.model_dump(exclude_none=True, exclude_unset=True), index=["values"]
+ )
if "is_multiindex" in df.columns:
col_names = ast.literal_eval(df.multiindex_names.unique()[0])
diff --git a/openbb_platform/core/openbb_core/provider/standard_models/futures_curve.py b/openbb_platform/core/openbb_core/provider/standard_models/futures_curve.py
index 266de2e79e2..2afd1cf8a60 100644
--- a/openbb_platform/core/openbb_core/provider/standard_models/futures_curve.py
+++ b/openbb_platform/core/openbb_core/provider/standard_models/futures_curve.py
@@ -1,7 +1,7 @@
"""Futures Curve Standard Model."""
from datetime import date as dateType
-from typing import Optional
+from typing import Optional, Union
from pydantic import Field, field_validator
@@ -17,14 +17,14 @@ class FuturesCurveQueryParams(QueryParams):
"""Futures Curve Query."""
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
- date: Optional[dateType] = Field(
+ date: Optional[Union[dateType, str]] = Field(
default=None,
description=QUERY_DESCRIPTIONS.get("date", ""),
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
- def to_upper(cls, v: str) -> str:
+ def to_upper(cls, v):
"""Convert field to uppercase."""
return v.upper()
@@ -32,7 +32,12 @@ class FuturesCurveQueryParams(QueryParams):
class FuturesCurveData(Data):
"""Futures Curve Data."""
+ date: Optional[dateType] = Field(
+ default=None, description=DATA_DESCRIPTIONS.get("date", "")
+ )
expiration: str = Field(description="Futures expiration month.")
- price: Optional[float] = Field(
- default=None, description=DATA_DESCRIPTIONS.get("close", "")
+ price: float = Field(
+ default=None,
+ description="The price of the futures contract.",
+ json_schema_extra={"x-unit_measurement": "currency"},
)
diff --git a/openbb_platform/core/tests/app/test_command_runner.py b/openbb_platform/core/tests/app/test_command_runner.py
index 7c20059c100..ac792869aa2 100644
--- a/openbb_platform/core/tests/app/test_command_runner.py
+++ b/openbb_platform/core/tests/app/test_command_runner.py
@@ -254,14 +254,12 @@ def test_parameters_builder_build(mock_func, execution_context):
}
-@patch("openbb_core.app.command_runner.LoggingService")
-def test_command_runner(_):
+def test_command_runner():
"""Test command runner."""
assert CommandRunner()
-@patch("openbb_core.app.command_runner.LoggingService")
-def test_command_runner_properties(mock_logging_service):
+def test_command_runner_properties():
"""Test properties."""
sys = SystemSettings()
user = UserSettings()
@@ -272,7 +270,6 @@ def test_command_runner_properties(mock_logging_service):
assert runner.system_settings == sys
assert runner.user_settings == user
assert runner.command_map == cmd_map
- assert mock_logging_service.called_once()
@patch("openbb_core.app.command_runner.LoggingService")
@@ -353,14 +350,14 @@ async def test_static_command_runner_execute_func(
mock_chart.return_value = None
result = await StaticCommandRunner._execute_func(
- "mock/route", (1, 2, 3, 4), execution_context, mock_func, {}
+ "mock/route", (1, 2, 3, 4), execution_context, mock_func, {"chart": True}
)
assert result.results == [1, 2, 3, 4]
- assert mock_logging_service.called_once()
- assert mock_parameters_builder_build.called_once()
- assert mock_command.called_once()
- assert mock_chart.called_once()
+ mock_logging_service.assert_called_once()
+ mock_parameters_builder_build.assert_called_once()
+ mock_command.assert_called_once()
+ mock_chart.assert_called_once()
def test_static_command_runner_chart():
diff --git a/openbb_platform/dev_install.py b/openbb_platform/dev_install.py
index d6b578f289a..364320e0d27 100644
--- a/openbb_platform/dev_install.py
+++ b/openbb_platform/dev_install.py
@@ -15,7 +15,7 @@ CLI_LOCK = CLI_PATH / "poetry.lock"
LOCAL_DEPS = """
[tool.poetry.dependencies]
-python = ">=3.9,<3.12"
+python = ">=3.9,<3.13"
openbb-devtools = { path = "./extensions/devtools", develop = true, markers = "python_version >= '3.10'" }
openbb-core = { path = "./core", develop = true }
diff --git a/openbb_platform/extensions/derivatives/integration/test_derivatives_api.py b/openbb_platform/extensions/derivatives/integration/test_derivatives_api.py
index 97e9a3551eb..bc753851c3b 100644
--- a/openbb_platform/extensions/derivatives/integration/test_derivatives_api.py
+++ b/openbb_platform/extensions/derivatives/integration/test_derivatives_api.py
@@ -128,8 +128,20 @@ def test_derivatives_futures_historical(params, headers):
@parametrize(
"params",
[
- ({"provider": "cboe", "symbol": "VX", "date": None}),
- ({"provider": "yfinance", "symbol": "ES", "date": "2023-08-01"}),
+ (
+ {
+ "provider": "yfinance",
+ "symbol": "ES",
+ "date": None,
+ }
+ ),
+ (
+ {
+ "provider": "cboe",
+ "symbol": "VX_EOD",
+ "date": "2024-06-25",
+ }
+ ),
],
)
@pytest.mark.integration
@@ -139,7 +151,7 @@ def test_derivatives_futures_curve(params, headers):
query_str = get_querystring(params, [])
url = f"http://0.0.0.0:8000/api/v1/derivatives/futures/curve?{query_str}"
- result = requests.get(url, headers=headers, timeout=60)
+ result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
diff --git a/openbb_platform/extensions/derivatives/integration/test_derivatives_python.py b/openbb_platform/extensions/derivatives/integration/test_derivatives_python.py
index 4e04039bc7a..004e2727087 100644
--- a/openbb_platform/extensions/derivatives/integration/test_derivatives_python.py
+++ b/openbb_platform/extensions/derivatives/integration/test_derivatives_python.py
@@ -115,8 +115,8 @@ def test_derivatives_futures_historical(params, obb):
@parametrize(
"params",
[
- ({"symbol": "VX", "provider": "cboe", "date": None}),
- ({"provider": "yfinance", "symbol": "ES", "date": "2023-08-01"}),
+ ({"provider": "yfinance", "symbol": "ES", "date": None}),
+ ({"provider": "cboe", "symbol": "VX", "date": "2024-06-25"}),
],
)
@pytest.mark.integration
diff --git a/openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_views.py b/openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_views.py
index c83b090e206..abe2fedba31 100644
--- a/openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_views.py
+++ b/openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_views.py
@@ -1,9 +1,9 @@
"""Views for the Derivatives Extension."""
-from typing import Any, Dict, Tuple
+from typing import TYPE_CHECKING, Any, Dict, Tuple
-from openbb_charting.charts.price_historical import price_historical
-from openbb_charting.core.openbb_figure import OpenBBFigure
+if TYPE_CHECKING:
+ from openbb_charting.core.openbb_figure import OpenBBFigure
class DerivativesViews:
@@ -12,6 +12,215 @@ class DerivativesViews:
@staticmethod
def derivatives_futures_historical( # noqa: PLR0912
**kwargs,
- ) -> Tuple[OpenBBFigure, Dict[str, Any]]:
- """Get Derivatives Price Historical Chart."""
+ ) -> Tuple["OpenBBFigure", Dict[str, Any]]:
+ """Get Derivatives Futures Historical Chart."""
+ # pylint: disable=import-outside-toplevel
+ from openbb_charting.charts.price_historical import price_historical
+
return price_historical(**kwargs)
+
+ @staticmethod
+ def derivatives_futures_curve( # noqa: PLR0912
+ **kwargs,
+ ) -> Tuple["OpenBBFigure", Dict[str, Any]]:
+ """Futures curve chart. All parameters are optional, and are kwargs.
+ Parameters can be directly accessed from the function end point by
+ entering as a nested dictionary to the 'chart_params' key.
+
+ From the API, `chart_params` must be passed as a JSON in the request body with `extra_params`.
+
+ If using the chart post-request, the parameters are passed directly
+ as `key=value` pairs in the `charting.to_chart` or `charting.show` methods.
+
+ Parameters
+ ----------
+ data : Optional[Union[List[Data], DataFrame]]
+ Data for the chart. Required fields are: 'expiration' and 'price'.
+ Multiple dates will be plotted on the same chart.
+ If not supplied, the original OBBject.results will be used.
+ If a DataFrame is supplied, flat data is expected, without a set index.
+ title: Optional[str]
+ Title for the chart. If not supplied, a default title will be used.
+ colors: Optional[List[str]]
+ List of colors to use for the chart. If not supplied, the default colorway will be used.
+ Colors should be in hex format, or named Plotly colors. Invalid colors will raise a Plotly error.
+ layout_kwargs: Optional[Dict[str, Any]]
+ Additional layout parameters for the chart, passed directly to `figure.update_layout` before output.
+ See Plotly documentation for available options.
+
+ Returns
+ -------
+ Tuple[OpenBBFigure, Dict[str, Any]]
+ Tuple with the OpenBBFigure object, and the JSON-serialized content.
+ If using the API, only the JSON content will be returned.
+
+ Examples
+ --------
+ ```python
+ from openbb import obb
+ data = obb.derivatives.futures.curve(symbol="vx", provider="cboe", date=["2020-03-31", "2024-06-28"], chart=True)
+ data.show()
+ ```
+
+ Redraw the chart, from the same data, with a custom colorway and title:
+
+ ```python
+ data.charting.to_chart(colors=["green", "red"], title="VIX Futures Curve - 2020 vs. 2024")
+ ```
+ """
+ # pylint: disable=import-outside-toplevel
+ from openbb_charting.core.chart_style import ChartStyle
+ from openbb_charting.core.openbb_figure import OpenBBFigure
+ from openbb_charting.styles.colors import LARGE_CYCLER
+ from openbb_core.app.model.abstract.error import OpenBBError
+ from openbb_core.provider.abstract.data import Data
+ from pandas import DataFrame, to_datetime
+
+ data = kwargs.get("data", None)
+ symbol = kwargs.get("standard_params", {}).get("symbol", "")
+ df: DataFrame = DataFrame()
+ if data:
+ if isinstance(data, DataFrame) and not data.empty: # noqa: SIM108
+ df = data
+ elif isinstance(data, (list, Data)):
+ df = DataFrame([d.model_dump(exclude_none=True, exclude_unset=True) for d in data]) # type: ignore
+ else:
+ pass
+ else:
+ df = DataFrame(
+ [
+ d.model_dump(exclude_none=True, exclude_unset=True) # type: ignore
+ for d in kwargs["obbject_item"]
+ ]
+ if isinstance(kwargs.get("obbject_item"), list)
+ else kwargs["obbject_item"].model_dump(exclude_none=True, exclude_unset=True) # type: ignore
+ )
+
+ if df.empty:
+ raise OpenBBError("Error: No data to plot.")
+
+ if "expiration" not in df.columns:
+ raise OpenBBError("Expiration field not found in the data.")
+
+ if "price" not in df.columns:
+ raise ValueError("Price field not found in the data.")
+
+ provider = kwargs.get("provider", "")
+
+ df["expiration"] = to_datetime(df["expiration"], errors="ignore").dt.strftime(
+ "%b-%Y"
+ )
+
+ if (
+ provider == "cboe"
+ and "date" in df.columns
+ and len(df["date"].unique()) > 1
+ and "symbol" in df.columns
+ ):
+ df["expiration"] = df.symbol
+
+ # Use a complete list of expirations to categorize the x-axis across all dates.
+ expirations = df["expiration"].unique().tolist()
+
+ # Use the supplied colors, if any.
+ colors = kwargs.get("colors", [])
+ if not colors:
+ colors = LARGE_CYCLER
+ color_count = 0
+
+ figure = OpenBBFigure().create_subplots(shared_xaxes=True)
+ figure.update_layout(ChartStyle().plotly_template.get("layout", {}))
+
+ def create_fig(figure, df, dates, color_count):
+ """Create a scatter for each date in the data."""
+ for date in dates:
+ color = colors[color_count % len(colors)]
+ plot_df = (
+ df[df["date"].astype(str) == date].copy()
+ if "date" in df.columns
+ else df.copy()
+ )
+ plot_df