openbb-charting 2.0.3__tar.gz → 2.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/PKG-INFO +4 -4
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/__init__.py +53 -22
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/charting_router.py +175 -23
- openbb_charting-2.1.1/openbb_charting/core/assets/plotly-2.32.0.min.js +8 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/backend.py +8 -14
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/openbb_figure.py +4 -2
- openbb_charting-2.1.1/openbb_charting/core/plotly.html +3621 -0
- openbb_charting-2.1.1/openbb_charting/core/table.html +166 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/query_params.py +17 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/utils/helpers.py +19 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/utils/relative_rotation.py +4 -7
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/pyproject.toml +3 -4
- openbb_charting-2.0.3/openbb_charting/core/assets/plotly-2.24.2.min.js +0 -8
- openbb_charting-2.0.3/openbb_charting/core/plotly.html +0 -5285
- openbb_charting-2.0.3/openbb_charting/core/table.html +0 -326
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/README.md +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/builder.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/__init__.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/assets/Terminal_icon.png +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/chart_style.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/config/__init__.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/config/openbb_styles.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/dummy_backend.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/__init__.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/base.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/data_classes.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/plugins/__init__.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/plugins/custom_indicators_plugin.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/plugins/momentum_plugin.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/plugins/overlap_plugin.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/plugins/trend_indicators_plugin.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/plugins/volatility_plugin.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/plugins/volume_plugin.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/ta_class.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/plotly_ta/ta_helpers.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/core/to_chart.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/styles/__init__.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/styles/colors.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/styles/default/dark.pltstyle.json +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/styles/default/light.pltstyle.json +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/styles/default/tables.pltstyle.json +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/utils/__init__.py +0 -0
- {openbb_charting-2.0.3 → openbb_charting-2.1.1}/openbb_charting/utils/generic_charts.py +0 -0
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: openbb-charting
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Charting extension for OpenBB
|
|
5
|
+
License: AGPL-3.0-only
|
|
5
6
|
Author: OpenBB Team
|
|
6
7
|
Author-email: hello@openbb.co
|
|
7
8
|
Requires-Python: >=3.8,<3.12
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
8
10
|
Classifier: Programming Language :: Python :: 3
|
|
9
11
|
Classifier: Programming Language :: Python :: 3.8
|
|
10
12
|
Classifier: Programming Language :: Python :: 3.9
|
|
11
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
15
|
Requires-Dist: nbformat (>=5.9.2,<6.0.0)
|
|
14
|
-
Requires-Dist: openbb-core (>=1.
|
|
16
|
+
Requires-Dist: openbb-core (>=1.2.3,<2.0.0)
|
|
15
17
|
Requires-Dist: pandas-ta (>=0.3.14b,<0.4.0)
|
|
16
18
|
Requires-Dist: plotly (>=5.17.0,<6.0.0)
|
|
17
19
|
Requires-Dist: pywry (>=0.6.1,<0.7.0)
|
|
18
|
-
Requires-Dist: reportlab (>=4.0.4,<5.0.0)
|
|
19
20
|
Requires-Dist: scipy (>=1.10.0,<2.0.0)
|
|
20
21
|
Requires-Dist: statsmodels (>=0.14.0,<0.15.0)
|
|
21
|
-
Requires-Dist: svglib (>=1.5.1,<2.0.0)
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
|
|
24
24
|
# OpenBB Charting extension
|
|
@@ -35,13 +35,12 @@ warnings.filterwarnings(
|
|
|
35
35
|
"ignore", category=UserWarning, module="openbb_core.app.model.extension", lineno=47
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
ext = Extension(name="charting")
|
|
38
|
+
ext = Extension(name="charting", description="Create custom charts from OBBject data.")
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
@ext.obbject_accessor
|
|
42
42
|
class Charting:
|
|
43
|
-
"""
|
|
44
|
-
Charting extension.
|
|
43
|
+
"""Charting extension.
|
|
45
44
|
|
|
46
45
|
Methods
|
|
47
46
|
-------
|
|
@@ -61,6 +60,8 @@ class Charting:
|
|
|
61
60
|
Create a line chart from external data.
|
|
62
61
|
create_bar_chart
|
|
63
62
|
Create a bar chart, on a single x-axis with one or more values for the y-axis, from external data.
|
|
63
|
+
toggle_chart_style
|
|
64
|
+
Toggle the chart style, of an existing chart, between light and dark mode.
|
|
64
65
|
"""
|
|
65
66
|
|
|
66
67
|
def __init__(self, obbject):
|
|
@@ -77,8 +78,8 @@ class Charting:
|
|
|
77
78
|
|
|
78
79
|
@classmethod
|
|
79
80
|
def indicators(cls):
|
|
80
|
-
"""
|
|
81
|
-
|
|
81
|
+
"""Return an instance of the IndicatorsParams class, containing all available indicators and their parameters.
|
|
82
|
+
|
|
82
83
|
Without assigning to a variable, it will print the the information to the console.
|
|
83
84
|
"""
|
|
84
85
|
return IndicatorsParams()
|
|
@@ -90,7 +91,6 @@ class Charting:
|
|
|
90
91
|
|
|
91
92
|
def _handle_backend(self) -> Backend:
|
|
92
93
|
"""Create and start the backend."""
|
|
93
|
-
|
|
94
94
|
create_backend(self._charting_settings)
|
|
95
95
|
backend = get_backend()
|
|
96
96
|
backend.start(debug=self._charting_settings.debug_mode)
|
|
@@ -99,15 +99,14 @@ class Charting:
|
|
|
99
99
|
@staticmethod
|
|
100
100
|
def _get_chart_function(route: str) -> Callable:
|
|
101
101
|
"""Given a route, it returns the chart function. The module must contain the given route."""
|
|
102
|
-
|
|
103
102
|
if route is None:
|
|
104
103
|
raise ValueError("OBBject was initialized with no function route.")
|
|
105
104
|
adjusted_route = route.replace("/", "_")[1:]
|
|
106
105
|
return getattr(charting_router, adjusted_route)
|
|
107
106
|
|
|
108
107
|
def get_params(self) -> ChartParams:
|
|
109
|
-
"""
|
|
110
|
-
|
|
108
|
+
"""Return the ChartQueryParams class for the function the OBBject was created from.
|
|
109
|
+
|
|
111
110
|
Without assigning to a variable, it will print the docstring to the console.
|
|
112
111
|
"""
|
|
113
112
|
if self._obbject._route is None: # pylint: disable=protected-access
|
|
@@ -126,7 +125,7 @@ class Charting:
|
|
|
126
125
|
) -> Tuple[pd.DataFrame, bool]:
|
|
127
126
|
"""Convert supplied data to a DataFrame."""
|
|
128
127
|
has_data = (
|
|
129
|
-
isinstance(data, (Data, pd.DataFrame, pd.Series)) and not data.empty
|
|
128
|
+
isinstance(data, (Data, pd.DataFrame, pd.Series)) and not data.empty # type: ignore
|
|
130
129
|
) or (bool(data))
|
|
131
130
|
index = (
|
|
132
131
|
data.index.name
|
|
@@ -234,6 +233,7 @@ class Charting:
|
|
|
234
233
|
same_axis=same_axis,
|
|
235
234
|
**kwargs,
|
|
236
235
|
)
|
|
236
|
+
fig = self._set_chart_style(fig)
|
|
237
237
|
if render:
|
|
238
238
|
return fig.show(**kwargs)
|
|
239
239
|
|
|
@@ -298,7 +298,6 @@ class Charting:
|
|
|
298
298
|
OpenBBFigure
|
|
299
299
|
The OpenBBFigure object.
|
|
300
300
|
"""
|
|
301
|
-
|
|
302
301
|
fig = bar_chart(
|
|
303
302
|
data=data,
|
|
304
303
|
x=x,
|
|
@@ -313,7 +312,7 @@ class Charting:
|
|
|
313
312
|
bar_kwargs=bar_kwargs,
|
|
314
313
|
layout_kwargs=layout_kwargs,
|
|
315
314
|
)
|
|
316
|
-
|
|
315
|
+
fig = self._set_chart_style(fig)
|
|
317
316
|
if render:
|
|
318
317
|
return fig.show(**kwargs)
|
|
319
318
|
|
|
@@ -322,7 +321,6 @@ class Charting:
|
|
|
322
321
|
# pylint: disable=inconsistent-return-statements
|
|
323
322
|
def show(self, render: bool = True, **kwargs):
|
|
324
323
|
"""Display chart and save it to the OBBject."""
|
|
325
|
-
|
|
326
324
|
try:
|
|
327
325
|
charting_function = self._get_chart_function(
|
|
328
326
|
self._obbject._route # pylint: disable=protected-access
|
|
@@ -345,14 +343,17 @@ class Charting:
|
|
|
345
343
|
_kwargs = kwargs.pop("kwargs")
|
|
346
344
|
kwargs.update(_kwargs.get("chart_params", {}))
|
|
347
345
|
fig, content = charting_function(**kwargs)
|
|
346
|
+
fig = self._set_chart_style(fig)
|
|
347
|
+
content = fig.show(external=True, **kwargs).to_plotly_json()
|
|
348
348
|
self._obbject.chart = Chart(
|
|
349
349
|
fig=fig, content=content, format=charting_router.CHART_FORMAT
|
|
350
350
|
)
|
|
351
351
|
if render:
|
|
352
352
|
fig.show(**kwargs)
|
|
353
|
-
except Exception:
|
|
353
|
+
except Exception: # pylint: disable=W0718
|
|
354
354
|
try:
|
|
355
355
|
fig = self.create_line_chart(data=self._obbject.results, render=False, **kwargs) # type: ignore
|
|
356
|
+
fig = self._set_chart_style(fig)
|
|
356
357
|
content = fig.show(external=True, **kwargs).to_plotly_json() # type: ignore
|
|
357
358
|
self._obbject.chart = Chart(
|
|
358
359
|
fig=fig, content=content, format=charting_router.CHART_FORMAT
|
|
@@ -389,11 +390,10 @@ class Charting:
|
|
|
389
390
|
render: bool = True,
|
|
390
391
|
**kwargs,
|
|
391
392
|
):
|
|
392
|
-
"""
|
|
393
|
-
|
|
393
|
+
"""Create an OpenBBFigure with user customizations (if any) and save it to the OBBject.
|
|
394
|
+
|
|
394
395
|
This function is used to populate, or re-populate, the OBBject with a chart using the data within
|
|
395
396
|
the OBBject or external data supplied via the `data` parameter.
|
|
396
|
-
|
|
397
397
|
This function modifies the original OBBject by overwriting the existing chart.
|
|
398
398
|
|
|
399
399
|
Parameters
|
|
@@ -459,6 +459,9 @@ class Charting:
|
|
|
459
459
|
)
|
|
460
460
|
kwargs["provider"] = self._obbject.provider # pylint: disable=protected-access
|
|
461
461
|
kwargs["extra"] = self._obbject.extra # pylint: disable=protected-access
|
|
462
|
+
kwargs["extra_params"] = kwargs["extra"]["metadata"].arguments.get(
|
|
463
|
+
"extra_params"
|
|
464
|
+
)
|
|
462
465
|
if "kwargs" in kwargs:
|
|
463
466
|
_kwargs = kwargs.pop("kwargs")
|
|
464
467
|
kwargs.update(_kwargs.get("chart_params", {}))
|
|
@@ -467,27 +470,55 @@ class Charting:
|
|
|
467
470
|
self.show(data=data_as_df, render=render, **kwargs)
|
|
468
471
|
else:
|
|
469
472
|
self.show(**kwargs, render=render)
|
|
470
|
-
except Exception:
|
|
473
|
+
except Exception: # pylint: disable=W0718
|
|
471
474
|
try:
|
|
472
475
|
fig = self.create_line_chart(data=data_as_df, render=False, **kwargs)
|
|
476
|
+
fig = self._set_chart_style(fig)
|
|
473
477
|
content = fig.show(external=True, **kwargs).to_plotly_json() # type: ignore
|
|
474
478
|
self._obbject.chart = Chart(
|
|
475
479
|
fig=fig, content=content, format=charting_router.CHART_FORMAT
|
|
476
480
|
)
|
|
477
481
|
if render:
|
|
478
482
|
return fig.show(**kwargs) # type: ignore
|
|
479
|
-
except Exception as e:
|
|
483
|
+
except Exception as e: # pylint: disable=W0718
|
|
480
484
|
raise RuntimeError(
|
|
481
485
|
"Failed to automatically create a generic chart with the data provided."
|
|
482
486
|
) from e
|
|
483
487
|
|
|
488
|
+
def _set_chart_style(self, figure: Figure):
|
|
489
|
+
"""Set the user preference for light or dark mode."""
|
|
490
|
+
style = self._charting_settings.chart_style # pylint: disable=protected-access
|
|
491
|
+
font_color = "black" if style == "light" else "white"
|
|
492
|
+
paper_bgcolor = "white" if style == "light" else "black"
|
|
493
|
+
figure = figure.update_layout(
|
|
494
|
+
dict( # pylint: disable=R1735
|
|
495
|
+
font_color=font_color, paper_bgcolor=paper_bgcolor
|
|
496
|
+
)
|
|
497
|
+
)
|
|
498
|
+
return figure
|
|
499
|
+
|
|
500
|
+
def toggle_chart_style(self): # pylint: disable=protected-access
|
|
501
|
+
"""Toggle the chart style between light and dark mode."""
|
|
502
|
+
if not hasattr(self._obbject.chart, "fig"):
|
|
503
|
+
raise ValueError(
|
|
504
|
+
"Error: No chart has been created. Please create a chart first."
|
|
505
|
+
)
|
|
506
|
+
current = self._charting_settings.chart_style
|
|
507
|
+
new = "light" if current == "dark" else "dark"
|
|
508
|
+
self._charting_settings.chart_style = new
|
|
509
|
+
figure = self._obbject.chart.fig # type: ignore[union-attr]
|
|
510
|
+
updated_figure = self._set_chart_style(figure)
|
|
511
|
+
self._obbject.chart.fig = updated_figure # type: ignore[union-attr]
|
|
512
|
+
self._obbject.chart.content = updated_figure.show( # type: ignore[union-attr]
|
|
513
|
+
external=True
|
|
514
|
+
).to_plotly_json()
|
|
515
|
+
|
|
484
516
|
def table(
|
|
485
517
|
self,
|
|
486
518
|
data: Optional[Union[pd.DataFrame, pd.Series]] = None,
|
|
487
519
|
title: str = "",
|
|
488
520
|
):
|
|
489
|
-
"""
|
|
490
|
-
Display an interactive table.
|
|
521
|
+
"""Display an interactive table.
|
|
491
522
|
|
|
492
523
|
Parameters
|
|
493
524
|
----------
|
|
@@ -510,7 +541,7 @@ class Charting:
|
|
|
510
541
|
or self._obbject._route, # pylint: disable=protected-access
|
|
511
542
|
theme=self._charting_settings.table_style,
|
|
512
543
|
)
|
|
513
|
-
except Exception as e:
|
|
544
|
+
except Exception as e: # pylint: disable=W0718
|
|
514
545
|
warn(f"Failed to show figure with backend. {e}")
|
|
515
546
|
|
|
516
547
|
else:
|
|
@@ -8,6 +8,7 @@ from warnings import warn
|
|
|
8
8
|
import pandas as pd
|
|
9
9
|
from openbb_core.app.model.charts.chart import ChartFormat
|
|
10
10
|
from openbb_core.app.utils import basemodel_to_df
|
|
11
|
+
from openbb_core.provider.abstract.data import Data
|
|
11
12
|
from plotly.graph_objs import Figure
|
|
12
13
|
|
|
13
14
|
from openbb_charting.core.chart_style import ChartStyle
|
|
@@ -19,6 +20,7 @@ from openbb_charting.utils import relative_rotation
|
|
|
19
20
|
from openbb_charting.utils.generic_charts import bar_chart
|
|
20
21
|
from openbb_charting.utils.helpers import (
|
|
21
22
|
calculate_returns,
|
|
23
|
+
duration_sorter,
|
|
22
24
|
heikin_ashi,
|
|
23
25
|
should_share_axis,
|
|
24
26
|
z_score_standardization,
|
|
@@ -31,7 +33,6 @@ def equity_price_performance(
|
|
|
31
33
|
**kwargs,
|
|
32
34
|
) -> Tuple[Union[OpenBBFigure, Figure], Dict[str, Any]]: # noqa: PLR0912
|
|
33
35
|
"""Equity Price Performance Chart."""
|
|
34
|
-
|
|
35
36
|
if "data" in kwargs and isinstance(kwargs["data"], pd.DataFrame):
|
|
36
37
|
data = kwargs["data"]
|
|
37
38
|
elif "data" in kwargs and isinstance(kwargs["data"], list):
|
|
@@ -83,7 +84,7 @@ def equity_price_performance(
|
|
|
83
84
|
limit = kwargs.pop("limit", 10)
|
|
84
85
|
chart_df = chart_df.head(limit) # type: ignore
|
|
85
86
|
|
|
86
|
-
layout_kwargs: Dict[str, Any] = kwargs
|
|
87
|
+
layout_kwargs: Dict[str, Any] = kwargs.get("layout_kwargs", {})
|
|
87
88
|
|
|
88
89
|
title = (
|
|
89
90
|
f"{kwargs.pop('title')}" if "title" in kwargs else "Equity Price Performance"
|
|
@@ -140,7 +141,6 @@ def etf_price_performance(
|
|
|
140
141
|
|
|
141
142
|
def etf_holdings(**kwargs) -> Tuple[Union[OpenBBFigure, Figure], Dict[str, Any]]:
|
|
142
143
|
"""Equity Compare Groups Chart."""
|
|
143
|
-
|
|
144
144
|
if "data" in kwargs and isinstance(kwargs["data"], pd.DataFrame):
|
|
145
145
|
data = kwargs["data"]
|
|
146
146
|
elif "data" in kwargs and isinstance(kwargs["data"], list):
|
|
@@ -198,7 +198,6 @@ def equity_price_historical( # noqa: PLR0912
|
|
|
198
198
|
**kwargs,
|
|
199
199
|
) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
200
200
|
"""Equity Price Historical Chart."""
|
|
201
|
-
|
|
202
201
|
if "data" in kwargs and isinstance(kwargs["data"], pd.DataFrame):
|
|
203
202
|
data = kwargs["data"]
|
|
204
203
|
elif "data" in kwargs and isinstance(kwargs["data"], list):
|
|
@@ -406,7 +405,7 @@ def equity_price_historical( # noqa: PLR0912
|
|
|
406
405
|
name=data[col].name,
|
|
407
406
|
mode="lines",
|
|
408
407
|
hovertemplate=hovertemplate,
|
|
409
|
-
line=dict(width=
|
|
408
|
+
line=dict(width=2, color=LARGE_CYCLER[i % len(LARGE_CYCLER)]),
|
|
410
409
|
yaxis=yaxis,
|
|
411
410
|
)
|
|
412
411
|
|
|
@@ -512,7 +511,6 @@ def crypto_price_historical(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
|
512
511
|
|
|
513
512
|
def _ta_ma(**kwargs):
|
|
514
513
|
"""Plot moving average helper."""
|
|
515
|
-
|
|
516
514
|
index = (
|
|
517
515
|
kwargs.get("index")
|
|
518
516
|
if "index" in kwargs and kwargs.get("index") is not None
|
|
@@ -587,8 +585,6 @@ def _ta_ma(**kwargs):
|
|
|
587
585
|
ma_df = ma_df.dropna()
|
|
588
586
|
data = data.iloc[-len(ma_df) :]
|
|
589
587
|
|
|
590
|
-
color = 0
|
|
591
|
-
|
|
592
588
|
if (
|
|
593
589
|
"candles" in kwargs
|
|
594
590
|
and kwargs.get("candles") is True
|
|
@@ -600,7 +596,7 @@ def _ta_ma(**kwargs):
|
|
|
600
596
|
else:
|
|
601
597
|
ma_df[f"{target}".title()] = data[target]
|
|
602
598
|
|
|
603
|
-
for col in ma_df.columns:
|
|
599
|
+
for i, col in enumerate(ma_df.columns):
|
|
604
600
|
name = col.replace("_", " ")
|
|
605
601
|
fig.add_scatter(
|
|
606
602
|
x=ma_df.index,
|
|
@@ -608,10 +604,9 @@ def _ta_ma(**kwargs):
|
|
|
608
604
|
name=name,
|
|
609
605
|
mode="lines",
|
|
610
606
|
hovertemplate=f"{name}: %{{y}}<extra></extra>",
|
|
611
|
-
line=dict(width=1, color=LARGE_CYCLER[
|
|
607
|
+
line=dict(width=1, color=LARGE_CYCLER[i]),
|
|
612
608
|
showlegend=True,
|
|
613
609
|
)
|
|
614
|
-
color += 1
|
|
615
610
|
|
|
616
611
|
fig.update_layout(
|
|
617
612
|
title=dict(text=title, x=0.5, font=dict(size=16)),
|
|
@@ -685,7 +680,6 @@ def technical_zlma(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
|
685
680
|
|
|
686
681
|
def technical_aroon(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
687
682
|
"""Technical Aroon Chart."""
|
|
688
|
-
|
|
689
683
|
if "data" in kwargs and isinstance(kwargs["data"], pd.DataFrame):
|
|
690
684
|
data = kwargs["data"]
|
|
691
685
|
else:
|
|
@@ -726,7 +720,6 @@ def technical_aroon(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
|
726
720
|
|
|
727
721
|
def technical_macd(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
728
722
|
"""Plot moving average convergence divergence chart."""
|
|
729
|
-
|
|
730
723
|
if "data" in kwargs and isinstance(kwargs["data"], pd.DataFrame):
|
|
731
724
|
data = kwargs["data"]
|
|
732
725
|
else:
|
|
@@ -765,7 +758,6 @@ def technical_macd(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
|
765
758
|
|
|
766
759
|
def technical_adx(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
767
760
|
"""Average directional movement index chart."""
|
|
768
|
-
|
|
769
761
|
if "data" in kwargs and isinstance(kwargs["data"], pd.DataFrame):
|
|
770
762
|
data = kwargs["data"]
|
|
771
763
|
else:
|
|
@@ -801,7 +793,6 @@ def technical_adx(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
|
801
793
|
|
|
802
794
|
def technical_rsi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
803
795
|
"""Relative strength index chart."""
|
|
804
|
-
|
|
805
796
|
if "data" in kwargs and isinstance(kwargs["data"], pd.DataFrame):
|
|
806
797
|
data = kwargs["data"]
|
|
807
798
|
else:
|
|
@@ -875,7 +866,6 @@ def technical_cones(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
|
875
866
|
"orange",
|
|
876
867
|
"blue",
|
|
877
868
|
]
|
|
878
|
-
color = 0
|
|
879
869
|
|
|
880
870
|
fig = OpenBBFigure()
|
|
881
871
|
|
|
@@ -883,7 +873,7 @@ def technical_cones(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
|
883
873
|
|
|
884
874
|
text_color = "black" if ChartStyle().plt_style == "light" else "white"
|
|
885
875
|
|
|
886
|
-
for col in df_ta.columns:
|
|
876
|
+
for i, col in enumerate(df_ta.columns):
|
|
887
877
|
fig.add_scatter(
|
|
888
878
|
x=df_ta.index,
|
|
889
879
|
y=df_ta[col],
|
|
@@ -891,11 +881,10 @@ def technical_cones(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
|
891
881
|
mode="lines+markers",
|
|
892
882
|
hovertemplate=f"{col}: %{{y}}<extra></extra>",
|
|
893
883
|
marker=dict(
|
|
894
|
-
color=colors[
|
|
884
|
+
color=colors[i],
|
|
895
885
|
size=11,
|
|
896
886
|
),
|
|
897
887
|
)
|
|
898
|
-
color += 1
|
|
899
888
|
|
|
900
889
|
fig.set_title(title)
|
|
901
890
|
|
|
@@ -1064,7 +1053,7 @@ def economy_fred_series( # noqa: PLR0912
|
|
|
1064
1053
|
if normalize:
|
|
1065
1054
|
y1title = None
|
|
1066
1055
|
title = f"{title} - Normalized" if title else "Normalized"
|
|
1067
|
-
bar_mode = kwargs
|
|
1056
|
+
bar_mode = kwargs.get("barmode", "group")
|
|
1068
1057
|
fig = bar_chart(
|
|
1069
1058
|
df_ta.reset_index(),
|
|
1070
1059
|
"date",
|
|
@@ -1119,7 +1108,7 @@ def economy_fred_series( # noqa: PLR0912
|
|
|
1119
1108
|
name=df_ta[col].name,
|
|
1120
1109
|
mode="lines",
|
|
1121
1110
|
hovertemplate=f"{df_ta[col].name}: %{{y}}<extra></extra>",
|
|
1122
|
-
line=dict(width=
|
|
1111
|
+
line=dict(width=2, color=LARGE_CYCLER[i % len(LARGE_CYCLER)]),
|
|
1123
1112
|
yaxis="y1" if kwargs.get("same_axis") is True else yaxes,
|
|
1124
1113
|
)
|
|
1125
1114
|
|
|
@@ -1228,7 +1217,6 @@ def technical_relative_rotation(
|
|
|
1228
1217
|
**kwargs: Any,
|
|
1229
1218
|
) -> Tuple["OpenBBFigure", Dict[str, Any]]:
|
|
1230
1219
|
"""Relative Rotation Chart."""
|
|
1231
|
-
|
|
1232
1220
|
ratios_df = basemodel_to_df(kwargs["obbject_item"].rs_ratios, index="date") # type: ignore
|
|
1233
1221
|
momentum_df = basemodel_to_df(kwargs["obbject_item"].rs_momentum, index="date") # type: ignore
|
|
1234
1222
|
benchmark_symbol = kwargs["obbject_item"].benchmark # type: ignore
|
|
@@ -1253,7 +1241,7 @@ def technical_relative_rotation(
|
|
|
1253
1241
|
ratios_df, momentum_df, benchmark_symbol, study, date # type: ignore
|
|
1254
1242
|
)
|
|
1255
1243
|
|
|
1256
|
-
figure = OpenBBFigure(fig)
|
|
1244
|
+
figure = OpenBBFigure(fig) # pylint: disable=E0606
|
|
1257
1245
|
font_color = "black" if ChartStyle().plt_style == "light" else "white"
|
|
1258
1246
|
figure.update_layout(
|
|
1259
1247
|
paper_bgcolor="rgba(0,0,0,0)",
|
|
@@ -1292,3 +1280,167 @@ def technical_relative_rotation(
|
|
|
1292
1280
|
content = figure.to_plotly_json()
|
|
1293
1281
|
|
|
1294
1282
|
return figure, content
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
def fixedincome_government_yield_curve( # noqa: PLR0912
|
|
1286
|
+
**kwargs,
|
|
1287
|
+
) -> Tuple[OpenBBFigure, Dict[str, Any]]:
|
|
1288
|
+
"""Government Yield Curve Chart."""
|
|
1289
|
+
data = kwargs.get("data", None)
|
|
1290
|
+
df: pd.DataFrame = pd.DataFrame()
|
|
1291
|
+
if data:
|
|
1292
|
+
if isinstance(data, pd.DataFrame) and not data.empty: # noqa: SIM108
|
|
1293
|
+
df = data
|
|
1294
|
+
elif isinstance(data, (list, Data)):
|
|
1295
|
+
df = basemodel_to_df(data, index=None) # type: ignore
|
|
1296
|
+
else:
|
|
1297
|
+
pass
|
|
1298
|
+
else:
|
|
1299
|
+
df = pd.DataFrame([d.model_dump() for d in kwargs["obbject_item"]]) # type: ignore
|
|
1300
|
+
|
|
1301
|
+
if df.empty:
|
|
1302
|
+
raise ValueError("Error: No data to plot.")
|
|
1303
|
+
|
|
1304
|
+
if "maturity" not in df.columns:
|
|
1305
|
+
raise ValueError("Error: Maturity column not found in the data.")
|
|
1306
|
+
|
|
1307
|
+
if "rate" not in df.columns:
|
|
1308
|
+
raise ValueError("Error: Rate column not found in the data.")
|
|
1309
|
+
|
|
1310
|
+
if "date" not in df.columns:
|
|
1311
|
+
raise ValueError("Error: Date column not found in the data.")
|
|
1312
|
+
|
|
1313
|
+
provider = kwargs.get("provider")
|
|
1314
|
+
df["date"] = df["date"].astype(str)
|
|
1315
|
+
maturities = duration_sorter(df["maturity"].unique().tolist())
|
|
1316
|
+
|
|
1317
|
+
# Use the supplied colors, if any.
|
|
1318
|
+
colors = kwargs.get("colors", [])
|
|
1319
|
+
if not colors:
|
|
1320
|
+
colors = LARGE_CYCLER
|
|
1321
|
+
color_count = 0
|
|
1322
|
+
|
|
1323
|
+
figure = OpenBBFigure().create_subplots(shared_xaxes=True)
|
|
1324
|
+
figure.update_layout(ChartStyle().plotly_template.get("layout", {}))
|
|
1325
|
+
|
|
1326
|
+
def create_fig(figure, df, dates, color_count, country: Optional[str] = None):
|
|
1327
|
+
"""Create a scatter for each date in the data."""
|
|
1328
|
+
for date in dates:
|
|
1329
|
+
color = colors[color_count % len(colors)]
|
|
1330
|
+
plot_df = df[df["date"] == date].copy()
|
|
1331
|
+
plot_df["rate"] = plot_df["rate"].apply(lambda x: x * 100)
|
|
1332
|
+
plot_df = (
|
|
1333
|
+
plot_df.drop(columns=["date"])
|
|
1334
|
+
.set_index("maturity")
|
|
1335
|
+
.filter(items=maturities, axis=0)
|
|
1336
|
+
.reset_index()
|
|
1337
|
+
.rename(columns={"maturity": "Maturity", "rate": "Yield"})
|
|
1338
|
+
)
|
|
1339
|
+
plot_df["Maturity"] = [
|
|
1340
|
+
(
|
|
1341
|
+
d.split("_")[1] + " " + d.split("_")[0].title()
|
|
1342
|
+
if d != "long_term"
|
|
1343
|
+
else "Long Term"
|
|
1344
|
+
)
|
|
1345
|
+
for d in plot_df["Maturity"]
|
|
1346
|
+
]
|
|
1347
|
+
figure.add_scatter(
|
|
1348
|
+
x=plot_df["Maturity"],
|
|
1349
|
+
y=plot_df["Yield"],
|
|
1350
|
+
# fill=fill,
|
|
1351
|
+
mode="lines+markers",
|
|
1352
|
+
name=f"{country} - {date}" if country else date,
|
|
1353
|
+
line=dict(width=3, color=color),
|
|
1354
|
+
marker=dict(size=10, color=color),
|
|
1355
|
+
hovertemplate=(
|
|
1356
|
+
"Maturity: %{x}<br>Yield: %{y}%<extra></extra>"
|
|
1357
|
+
if len(dates) == 1
|
|
1358
|
+
else "%{fullData.name}<br>Maturity: %{x}<br>Yield: %{y}%<extra></extra>"
|
|
1359
|
+
),
|
|
1360
|
+
)
|
|
1361
|
+
color_count += 1
|
|
1362
|
+
return figure, color_count
|
|
1363
|
+
|
|
1364
|
+
dates = df.date.unique().tolist()
|
|
1365
|
+
figure, color_count = create_fig(figure, df, dates, color_count)
|
|
1366
|
+
extra_params = kwargs.get("extra_params")
|
|
1367
|
+
extra_params = (
|
|
1368
|
+
extra_params if isinstance(extra_params, dict) else extra_params.__dict__
|
|
1369
|
+
)
|
|
1370
|
+
# Set the title for the chart
|
|
1371
|
+
country: str = ""
|
|
1372
|
+
if provider in ("federal_reserve", "fmp"):
|
|
1373
|
+
country = "United States"
|
|
1374
|
+
elif provider == "ecb":
|
|
1375
|
+
curve_type = extra_params.get("yield_curve_type", "").replace("_", " ").title()
|
|
1376
|
+
grade = extra_params.get("rating", "").replace("_", " ")
|
|
1377
|
+
grade = grade.upper() if grade == "aaa" else "All Ratings"
|
|
1378
|
+
country = f"Euro Area ({grade}) {curve_type}"
|
|
1379
|
+
elif provider == "fred":
|
|
1380
|
+
curve_type = extra_params.get("yield_curve_type", "")
|
|
1381
|
+
curve_type = (
|
|
1382
|
+
"Real Rates"
|
|
1383
|
+
if curve_type == "real"
|
|
1384
|
+
else curve_type.replace("_", " ").title()
|
|
1385
|
+
)
|
|
1386
|
+
country = f"United States {curve_type}"
|
|
1387
|
+
elif provider == "econdb":
|
|
1388
|
+
country = extra_params.get("country", "")
|
|
1389
|
+
country = country.replace("_", " ").title() if country else "United States"
|
|
1390
|
+
country = country + " " if country else ""
|
|
1391
|
+
title = kwargs.get("title", "")
|
|
1392
|
+
if not title:
|
|
1393
|
+
title = f"{country}Yield Curve"
|
|
1394
|
+
if len(dates) == 1:
|
|
1395
|
+
title = f"{country} Yield Curve - {dates[0]}"
|
|
1396
|
+
|
|
1397
|
+
# Update the layout of the figure.
|
|
1398
|
+
figure.update_layout(
|
|
1399
|
+
title=dict(text=title, x=0.5, font=dict(size=20)),
|
|
1400
|
+
plot_bgcolor="rgba(255,255,255,0)",
|
|
1401
|
+
xaxis=dict(
|
|
1402
|
+
title="Maturity",
|
|
1403
|
+
ticklen=0,
|
|
1404
|
+
showgrid=False,
|
|
1405
|
+
type="category",
|
|
1406
|
+
categoryorder="array",
|
|
1407
|
+
categoryarray=(
|
|
1408
|
+
[
|
|
1409
|
+
(
|
|
1410
|
+
d.split("_")[1] + " " + d.split("_")[0].title()
|
|
1411
|
+
if d != "long_term"
|
|
1412
|
+
else "Long Term"
|
|
1413
|
+
)
|
|
1414
|
+
for d in maturities
|
|
1415
|
+
]
|
|
1416
|
+
),
|
|
1417
|
+
),
|
|
1418
|
+
yaxis=dict(
|
|
1419
|
+
title="Yield (%)",
|
|
1420
|
+
ticklen=0,
|
|
1421
|
+
showgrid=True,
|
|
1422
|
+
gridcolor="rgba(128,128,128,0.3)",
|
|
1423
|
+
),
|
|
1424
|
+
legend=dict(
|
|
1425
|
+
orientation="v",
|
|
1426
|
+
yanchor="top",
|
|
1427
|
+
xanchor="right",
|
|
1428
|
+
y=0.95,
|
|
1429
|
+
x=0,
|
|
1430
|
+
xref="paper",
|
|
1431
|
+
font=dict(size=12),
|
|
1432
|
+
bgcolor="rgba(0,0,0,0)",
|
|
1433
|
+
),
|
|
1434
|
+
margin=dict(
|
|
1435
|
+
b=25,
|
|
1436
|
+
t=10,
|
|
1437
|
+
),
|
|
1438
|
+
)
|
|
1439
|
+
|
|
1440
|
+
layout_kwargs = kwargs.get("layout_kwargs", {})
|
|
1441
|
+
if layout_kwargs:
|
|
1442
|
+
figure.update_layout(layout_kwargs)
|
|
1443
|
+
|
|
1444
|
+
content = figure.show(external=True)
|
|
1445
|
+
|
|
1446
|
+
return figure, content
|