summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanglewood <85772166+deeleeramone@users.noreply.github.com>2024-03-16 07:49:59 -0700
committerGitHub <noreply@github.com>2024-03-16 14:49:59 +0000
commit72ccf4ed11ae33e0f8e675a83004ae493a53bdfa (patch)
tree520df9c85c76a4aa8b515a532747c3e72898de4e
parenta3fc04c42b984784d1cd9d3e0e085a72e9642d7e (diff)
[Feature] Volatility Cones Chart (#6226)
* add volatility cones to charting extension * test params
-rw-r--r--openbb_platform/extensions/technical/integration/test_technical_api.py2
-rw-r--r--openbb_platform/extensions/technical/integration/test_technical_python.py2
-rw-r--r--openbb_platform/extensions/technical/openbb_technical/helpers.py24
-rw-r--r--openbb_platform/extensions/technical/openbb_technical/technical_router.py22
-rw-r--r--openbb_platform/extensions/technical/tests/test_technical_helpers.py2
-rw-r--r--openbb_platform/obbject_extensions/charting/integration/test_charting_api.py32
-rw-r--r--openbb_platform/obbject_extensions/charting/integration/test_charting_python.py27
-rw-r--r--openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py46
-rw-r--r--openbb_platform/obbject_extensions/charting/openbb_charting/charting_router.py112
9 files changed, 220 insertions, 49 deletions
diff --git a/openbb_platform/extensions/technical/integration/test_technical_api.py b/openbb_platform/extensions/technical/integration/test_technical_api.py
index 9c3f7f11ec9..2fec3060fa0 100644
--- a/openbb_platform/extensions/technical/integration/test_technical_api.py
+++ b/openbb_platform/extensions/technical/integration/test_technical_api.py
@@ -900,7 +900,7 @@ def test_technical_cg(params, data_type):
"index": "date",
"lower_q": "0.3",
"upper_q": "0.7",
- "model": "Parkinson",
+ "model": "parkinson",
"is_crypto": "True",
"trading_periods": "",
},
diff --git a/openbb_platform/extensions/technical/integration/test_technical_python.py b/openbb_platform/extensions/technical/integration/test_technical_python.py
index c060b3ec0d4..ab51354904e 100644
--- a/openbb_platform/extensions/technical/integration/test_technical_python.py
+++ b/openbb_platform/extensions/technical/integration/test_technical_python.py
@@ -844,7 +844,7 @@ def test_technical_cg(params, data_type, obb):
"index": "date",
"lower_q": "0.3",
"upper_q": "0.7",
- "model": "Parkinson",
+ "model": "parkinson",
"is_crypto": "True",
"trading_periods": "",
},
diff --git a/openbb_platform/extensions/technical/openbb_technical/helpers.py b/openbb_platform/extensions/technical/openbb_technical/helpers.py
index c6a22f3fd17..948a32fede3 100644
--- a/openbb_platform/extensions/technical/openbb_technical/helpers.py
+++ b/openbb_platform/extensions/technical/openbb_technical/helpers.py
@@ -383,12 +383,12 @@ def calculate_cones(
upper_q: float,
is_crypto: bool,
model: Literal[
- "STD",
- "Parkinson",
- "Garman-Klass",
- "Hodges-Tompkins",
- "Rogers-Satchell",
- "Yang-Zhang",
+ "std",
+ "parkinson",
+ "garman_klass",
+ "hodges_tompkins",
+ "rogers_satchell",
+ "yang_zhang",
],
trading_periods: Optional[int] = None,
) -> pd.DataFrame:
@@ -415,12 +415,12 @@ def calculate_cones(
data = data.sort_index(ascending=True)
model_functions = {
- "STD": standard_deviation,
- "Parkinson": parkinson,
- "Garman-Klass": garman_klass,
- "Hodges-Tompkins": hodges_tompkins,
- "Rogers-Satchell": rogers_satchell,
- "Yang-Zhang": yang_zhang,
+ "std": standard_deviation,
+ "parkinson": parkinson,
+ "garman_klass": garman_klass,
+ "hodges_tompkins": hodges_tompkins,
+ "rogers_satchell": rogers_satchell,
+ "yang_zhang": yang_zhang,
}
for window in windows:
diff --git a/openbb_platform/extensions/technical/openbb_technical/technical_router.py b/openbb_platform/extensions/technical/openbb_technical/technical_router.py
index 69405a3e3fe..e4faa20ef90 100644
--- a/openbb_platform/extensions/technical/openbb_technical/technical_router.py
+++ b/openbb_platform/extensions/technical/openbb_technical/technical_router.py
@@ -1530,10 +1530,10 @@ def cg(
methods=["POST"],
examples=[
PythonEx(
- description="Get the cones indicator.",
+ description="Realized Volatility Cones.",
code=[
- "stock_data = obb.equity.price.historical(symbol='TSLA', start_date='2023-01-01', provider='fmp')",
- "cones_data = obb.technical.cones(data=stock_data.results, lower_q=0.25, upper_q=0.75, model='STD')",
+ "stock_data = obb.equity.price.historical(symbol='TSLA', start_date='2023-01-01', provider='yfinance')",
+ "cones_data = obb.technical.cones(data=stock_data.results, lower_q=0.25, upper_q=0.75, model='std')",
],
),
APIEx(parameters={"data": APIEx.mock_data("timeseries")}),
@@ -1545,13 +1545,13 @@ def cones(
lower_q: float = 0.25,
upper_q: float = 0.75,
model: Literal[
- "STD",
- "Parkinson",
- "Garman-Klass",
- "Hodges-Tompkins",
- "Rogers-Satchell",
- "Yang-Zhang",
- ] = "STD",
+ "std",
+ "parkinson",
+ "garman_klass",
+ "hodges_tompkins",
+ "rogers_satchell",
+ "yang_zhang",
+ ] = "std",
is_crypto: bool = False,
trading_periods: Optional[int] = None,
) -> OBBject[List[Data]]:
@@ -1581,7 +1581,7 @@ def cones(
The lower quantile value for calculations
upper_q : float, optional
The upper quantile value for calculations
- model : Literal["STD", "Parkinson", "Garman-Klass", "Hodges-Tompkins", "Rogers-Satchell", "Yang-Zhang"], optional
+ model : Literal["std", "parkinson", "garman_klass", "hodges_tompkins", "rogers_satchell", "yang_zhang"], optional
The model used to calculate realized volatility
Standard deviation measures how widely returns are dispersed from the average return.
diff --git a/openbb_platform/extensions/technical/tests/test_technical_helpers.py b/openbb_platform/extensions/technical/tests/test_technical_helpers.py
index c653e6d98e0..82ef6919fed 100644
--- a/openbb_platform/extensions/technical/tests/test_technical_helpers.py
+++ b/openbb_platform/extensions/technical/tests/test_technical_helpers.py
@@ -81,7 +81,7 @@ def test_calculate_cones_with_mock_data(mock_data):
lower_q=0.1,
upper_q=0.9,
is_crypto=False,
- model="STD",
+ model="std",
)
assert not result.empty
diff --git a/openbb_platform/obbject_extensions/charting/integration/test_charting_api.py b/openbb_platform/obbject_extensions/charting/integration/test_charting_api.py
index d5df11fdc63..2c5a8cbb47e 100644
--- a/openbb_platform/obbject_extensions/charting/integration/test_charting_api.py
+++ b/openbb_platform/obbject_extensions/charting/integration/test_charting_api.py
@@ -402,3 +402,35 @@ def test_charting_technical_zlma(params, headers):
assert chart
assert not fig
assert list(chart.keys()) == ["content", "format"]
+
+
+@parametrize(
+ "params",
+ [
+ (
+ {
+ "data": "",
+ "model": "yang_zhang",
+ "chart": True,
+ }
+ )
+ ],
+)
+@pytest.mark.integration
+def test_charting_technical_cones(params, headers):
+ """Test chart ta cones."""
+ params = {p: v for p, v in params.items() if v}
+ body = json.dumps(get_equity_data())
+
+ query_str = get_querystring(params, [])
+ url = f"http://0.0.0.0:8000/api/v1/technical/cones?{query_str}"
+ result = requests.post(url, headers=headers, timeout=10, data=body)
+ assert isinstance(result, requests.Response)
+ assert result.status_code == 200
+
+ chart = result.json()["chart"]
+ fig = chart.pop("fig", {})
+
+ assert chart
+ assert not fig
+ assert list(chart.keys()) == ["content", "format"]
diff --git a/openbb_platform/obbject_extensions/charting/integration/test_charting_python.py b/openbb_platform/obbject_extensions/charting/integration/test_charting_python.py
index 68d4e6a18b2..f15df117905 100644
--- a/openbb_platform/obbject_extensions/charting/integration/test_charting_python.py
+++ b/openbb_platform/obbject_extensions/charting/integration/test_charting_python.py
@@ -348,3 +348,30 @@ def test_charting_technical_zlma(params, obb):
assert len(result.results) > 0
assert result.chart.content
assert isinstance(result.chart.fig, OpenBBFigure)
+
+
+@parametrize(
+ "params",
+ [
+ (
+ {
+ "data": "",
+ "model": "yang_zhang",
+ "chart": True,
+ }
+ )
+ ],
+)
+@pytest.mark.integration
+def test_charting_technical_cones(params, obb):
+ """Test chart ta cones."""
+ params = {p: v for p, v in params.items() if v}
+
+ params["data"] = get_equity_data()
+
+ result = obb.technical.cones(**params)
+ assert result
+ assert isinstance(result, OBBject)
+ assert len(result.results) > 0
+ assert result.chart.content
+ assert isinstance(result.chart.fig, OpenBBFigure)
diff --git a/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py b/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py
index ebf29aa54f7..8ca3641e437 100644
--- a/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py
+++ b/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py
@@ -6,7 +6,6 @@ from typing import (
Dict,
List,
Optional,
- Tuple,
Union,
)
@@ -18,7 +17,7 @@ from openbb_core.app.utils import basemodel_to_df, convert_to_basemodel
from openbb_core.provider.abstract.data import Data
from openbb_charting import charting_router
-from openbb_charting.core.to_chart import ChartIndicators, OpenBBFigure, to_chart
+from openbb_charting.core.to_chart import ChartIndicators, to_chart
from openbb_charting.utils.helpers import get_charting_functions
ext = Extension(name="charting")
@@ -49,8 +48,8 @@ class Charting:
self._obbject: OBBject = obbject
self._charting_settings = ChartingSettings(
- user_settings=self._obbject._user_settings,
- system_settings=self._obbject._system_settings,
+ user_settings=self._obbject._user_settings, # type: ignore
+ system_settings=self._obbject._system_settings, # type: ignore
)
self._handle_backend()
@@ -185,17 +184,28 @@ class Charting:
if has_data
else self._obbject.to_dataframe()
)
- fig, content = to_chart(
- data_as_df,
- indicators=indicators,
- symbol=symbol,
- candles=candles,
- volume=volume,
- prepost=prepost,
- volume_ticks_x=volume_ticks_x,
- )
- self._obbject.chart = Chart(
- fig=fig, content=content, format=charting_router.CHART_FORMAT
- )
- if render:
- fig.show(**kwargs)
+ try:
+ fig, content = to_chart(
+ data_as_df,
+ indicators=indicators,
+ symbol=symbol,
+ candles=candles,
+ volume=volume,
+ prepost=prepost,
+ volume_ticks_x=volume_ticks_x,
+ )
+
+ self._obbject.chart = Chart(
+ fig=fig, content=content, format=charting_router.CHART_FORMAT
+ )
+ if render:
+ fig.show(**kwargs)
+
+ except Exception:
+ try:
+ if has_data:
+ self.show(data=data_as_df, symbol=symbol, render=render, **kwargs)
+ else:
+ self.show(**kwargs)
+ except Exception as e:
+ raise RuntimeError("Could not create chart from the OBBject.") from e
diff --git a/openbb_platform/obbject_extensions/charting/openbb_charting/charting_router.py b/openbb_platform/obbject_extensions/charting/openbb_charting/charting_router.py
index a80c1af8ad9..f13ba058623 100644
--- a/openbb_platform/obbject_extensions/charting/openbb_charting/charting_router.py
+++ b/openbb_platform/obbject_extensions/charting/openbb_charting/charting_router.py
@@ -1,18 +1,20 @@
"""Charting router."""
-from typing import TYPE_CHECKING, Any, Dict, Tuple
+from typing import Any, Dict, Tuple
+import pandas as pd
from openbb_core.app.model.charts.chart import ChartFormat
from openbb_core.app.utils import basemodel_to_df
-from .core.plotly_ta.ta_class import PlotlyTA
+from openbb_charting.core.chart_style import ChartStyle
+from openbb_charting.core.openbb_figure import OpenBBFigure
+from openbb_charting.core.plotly_ta.ta_class import PlotlyTA
CHART_FORMAT = ChartFormat.plotly
-if TYPE_CHECKING:
- from .core.openbb_figure import OpenBBFigure
+# if TYPE_CHECKING:
- # from .core.openbb_figure_table import OpenBBFigureTable
+# from .core.openbb_figure_table import OpenBBFigureTable
def equity_price_historical(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
@@ -58,6 +60,7 @@ def _ta_ma(ma_type: str, **kwargs):
False,
volume=False,
)
+ fig.update_layout(ChartStyle().plotly_template.get("layout", {}))
content = fig.show(external=True).to_plotly_json()
return fig, content
@@ -174,3 +177,102 @@ def technical_ema(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
"""Exponential moving average chart."""
ma_type = "ema"
return _ta_ma(ma_type, **kwargs)
+
+
+def technical_cones(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
+ """Volatility Cones Chart."""
+
+ data = kwargs.get("data")
+
+ if isinstance(data, pd.DataFrame) and not data.empty and "window" in data.columns:
+ df_ta = data.set_index("window")
+ else:
+ df_ta = basemodel_to_df(kwargs["obbject_item"], index="window")
+
+ df_ta.columns = [col.title().replace("_", " ") for col in df_ta.columns]
+
+ # Check if the data is formatted as expected.
+ if not all(col in df_ta.columns for col in ["Realized", "Min", "Median", "Max"]):
+ raise ValueError("Data supplied does not match the expected format.")
+
+ model = (
+ str(kwargs.get("model"))
+ .replace("std", "Standard Deviation")
+ .replace("_", "-")
+ .title()
+ if kwargs.get("model")
+ else "Standard Deviation"
+ )
+
+ symbol = str(kwargs.get("symbol")) + " - " if kwargs.get("symbol") else ""
+
+ title = (
+ str(kwargs.get("title"))
+ if kwargs.get("title")
+ else f"{symbol}Realized Volatility Cones - {model} Model"
+ )
+
+ colors = [
+ "green",
+ "red",
+ "burlywood",
+ "grey",
+ "orange",
+ "blue",
+ ]
+ color = 0
+
+ fig = OpenBBFigure()
+
+ fig.update_layout(ChartStyle().plotly_template.get("layout", {}))
+
+ text_color = "black" if ChartStyle().plt_style == "light" else "white"
+
+ for col in df_ta.columns:
+ fig.add_scatter(
+ x=df_ta.index,
+ y=df_ta[col],
+ name=col,
+ mode="lines+markers",
+ hovertemplate=f"{col}: %{{y}}<extra></extra>",
+ marker=dict(
+ color=colors[color],
+ size=11,
+ ),
+ )
+ color += 1
+
+ fig.set_title(title)
+
+ fig.update_layout(
+ paper_bgcolor="rgba(0,0,0,0)",
+ plot_bgcolor="rgba(0,0,0,0)",
+ font=dict(color=text_color),
+ legend=dict(
+ orientation="h",
+ yanchor="bottom",
+ xanchor="right",
+ y=1.02,
+ x=1,
+ bgcolor="rgba(0,0,0,0)",
+ ),
+ yaxis=dict(
+ ticklen=0,
+ ),
+ xaxis=dict(
+ type="category",
+ tickmode="array",
+ ticklen=0,
+ tickvals=df_ta.index,
+ ticktext=df_ta.index,
+ title_text="Period",
+ showgrid=False,
+ zeroline=False,
+ ),
+ margin=dict(l=20, r=20, b=20),
+ dragmode="pan",
+ )
+
+ content = fig.to_plotly_json()
+
+ return fig, content