openbb-charting 2.0.0__tar.gz → 2.0.2__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.
Files changed (36) hide show
  1. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/PKG-INFO +2 -2
  2. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/__init__.py +33 -20
  3. openbb_charting-2.0.2/openbb_charting/charting_router.py +538 -0
  4. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/backend.py +7 -1
  5. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/openbb_figure.py +1 -5
  6. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/base.py +16 -8
  7. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/data_classes.py +33 -23
  8. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/ta_class.py +12 -6
  9. openbb_charting-2.0.2/openbb_charting/query_params.py +105 -0
  10. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/pyproject.toml +2 -2
  11. openbb_charting-2.0.0/openbb_charting/charting_router.py +0 -176
  12. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/README.md +0 -0
  13. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/builder.py +0 -0
  14. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/__init__.py +0 -0
  15. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/assets/Terminal_icon.png +0 -0
  16. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/assets/plotly-2.24.2.min.js +0 -0
  17. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/chart_style.py +0 -0
  18. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/config/__init__.py +0 -0
  19. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/config/openbb_styles.py +0 -0
  20. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/dummy_backend.py +0 -0
  21. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly.html +0 -0
  22. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/__init__.py +0 -0
  23. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/plugins/custom_indicators_plugin.py +0 -0
  24. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/plugins/momentum_plugin.py +0 -0
  25. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/plugins/overlap_plugin.py +0 -0
  26. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/plugins/trend_indicators_plugin.py +0 -0
  27. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/plugins/volatility_plugin.py +0 -0
  28. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/plugins/volume_plugin.py +0 -0
  29. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/plotly_ta/ta_helpers.py +0 -0
  30. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/table.html +0 -0
  31. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/core/to_chart.py +0 -0
  32. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/styles/default/dark.pltstyle.json +0 -0
  33. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/styles/default/light.pltstyle.json +0 -0
  34. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/styles/default/tables.pltstyle.json +0 -0
  35. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/utils/__init__.py +0 -0
  36. {openbb_charting-2.0.0 → openbb_charting-2.0.2}/openbb_charting/utils/helpers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openbb-charting
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: Charting extension for OpenBB
5
5
  Author: OpenBB Team
6
6
  Author-email: hello@openbb.co
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Requires-Dist: nbformat (>=5.9.2,<6.0.0)
14
- Requires-Dist: openbb-core (>=1.1.2,<2.0.0)
14
+ Requires-Dist: openbb-core (>=1.1.5,<2.0.0)
15
15
  Requires-Dist: pandas-ta (>=0.3.14b,<0.4.0)
16
16
  Requires-Dist: plotly (>=5.17.0,<6.0.0)
17
17
  Requires-Dist: pywry (>=0.6.1,<0.7.0)
@@ -6,7 +6,6 @@ from typing import (
6
6
  Dict,
7
7
  List,
8
8
  Optional,
9
- Tuple,
10
9
  Union,
11
10
  )
12
11
 
@@ -18,7 +17,7 @@ from openbb_core.app.utils import basemodel_to_df, convert_to_basemodel
18
17
  from openbb_core.provider.abstract.data import Data
19
18
 
20
19
  from openbb_charting import charting_router
21
- from openbb_charting.core.to_chart import ChartIndicators, OpenBBFigure, to_chart
20
+ from openbb_charting.core.to_chart import ChartIndicators, to_chart
22
21
  from openbb_charting.utils.helpers import get_charting_functions
23
22
 
24
23
  ext = Extension(name="charting")
@@ -49,8 +48,8 @@ class Charting:
49
48
 
50
49
  self._obbject: OBBject = obbject
51
50
  self._charting_settings = ChartingSettings(
52
- user_settings=self._obbject._user_settings,
53
- system_settings=self._obbject._system_settings,
51
+ user_settings=self._obbject._user_settings, # type: ignore
52
+ system_settings=self._obbject._system_settings, # type: ignore
54
53
  )
55
54
  self._handle_backend()
56
55
 
@@ -91,7 +90,9 @@ class Charting:
91
90
  kwargs["standard_params"] = (
92
91
  self._obbject._standard_params.__dict__ # pylint: disable=protected-access
93
92
  )
94
-
93
+ kwargs["provider"] = self._obbject.provider # pylint: disable=protected-access
94
+ kwargs["extra"] = self._obbject.extra # pylint: disable=protected-access
95
+ kwargs["warnings"] = self._obbject.warnings # pylint: disable=protected-access
95
96
  fig, content = charting_function(**kwargs)
96
97
  self._obbject.chart = Chart(
97
98
  fig=fig, content=content, format=charting_router.CHART_FORMAT
@@ -178,24 +179,36 @@ class Charting:
178
179
  index = (
179
180
  data.index.name
180
181
  if has_data and isinstance(data, (pd.DataFrame, pd.Series))
181
- else ""
182
+ else None
182
183
  )
183
184
  data_as_df: pd.DataFrame = (
184
185
  basemodel_to_df(convert_to_basemodel(data), index=index)
185
186
  if has_data
186
187
  else self._obbject.to_dataframe()
187
188
  )
188
- fig, content = to_chart(
189
- data_as_df,
190
- indicators=indicators,
191
- symbol=symbol,
192
- candles=candles,
193
- volume=volume,
194
- prepost=prepost,
195
- volume_ticks_x=volume_ticks_x,
196
- )
197
- self._obbject.chart = Chart(
198
- fig=fig, content=content, format=charting_router.CHART_FORMAT
199
- )
200
- if render:
201
- fig.show(**kwargs)
189
+ if "date" in data_as_df.columns:
190
+ data_as_df = data_as_df.set_index("date")
191
+ try:
192
+ fig, content = to_chart(
193
+ data_as_df,
194
+ indicators=indicators,
195
+ symbol=symbol,
196
+ candles=candles,
197
+ volume=volume,
198
+ prepost=prepost,
199
+ volume_ticks_x=volume_ticks_x,
200
+ )
201
+ self._obbject.chart = Chart(
202
+ fig=fig, content=content, format=charting_router.CHART_FORMAT
203
+ )
204
+ if render:
205
+ fig.show(**kwargs)
206
+
207
+ except Exception:
208
+ try:
209
+ if has_data:
210
+ self.show(data=data_as_df, symbol=symbol, render=render, **kwargs)
211
+ else:
212
+ self.show(**kwargs)
213
+ except Exception as e:
214
+ raise RuntimeError("Could not create chart from the OBBject.") from e
@@ -0,0 +1,538 @@
1
+ """Charting router."""
2
+
3
+ import json
4
+ from typing import Any, Dict, Optional, Tuple, Union
5
+
6
+ import pandas as pd
7
+ from openbb_core.app.model.charts.chart import ChartFormat
8
+ from openbb_core.app.utils import basemodel_to_df
9
+
10
+ from openbb_charting.core.chart_style import ChartStyle
11
+ from openbb_charting.core.openbb_figure import OpenBBFigure
12
+ from openbb_charting.core.plotly_ta.ta_class import PlotlyTA
13
+ from openbb_charting.query_params import (
14
+ FredSeriesChartQueryParams,
15
+ TechnicalConesChartQueryParams,
16
+ )
17
+
18
+ CHART_FORMAT = ChartFormat.plotly
19
+
20
+ # if TYPE_CHECKING:
21
+
22
+ # from .core.openbb_figure_table import OpenBBFigureTable
23
+
24
+
25
+ def equity_price_historical(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
26
+ """Equity price chart."""
27
+
28
+ def handle_indicators(ma):
29
+ """Handle indicators."""
30
+ k = {}
31
+ if ma:
32
+ k["rma"] = dict(length=ma)
33
+ return k
34
+
35
+ data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
36
+ standard_params = kwargs["standard_params"]
37
+ ma = standard_params.get("ma", None)
38
+ prepost = standard_params.get("prepost", False)
39
+ symbol = standard_params.get("symbol", "")
40
+
41
+ ta = PlotlyTA()
42
+ fig = ta.plot(
43
+ data,
44
+ indicators=dict(**handle_indicators(ma)),
45
+ symbol=f"{symbol} historical data",
46
+ prepost=prepost,
47
+ )
48
+ content = fig.show(external=True).to_plotly_json()
49
+
50
+ return fig, content
51
+
52
+
53
+ def _ta_ma(ma_type: str, **kwargs):
54
+ """Plot moving average helper."""
55
+ data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
56
+ window = kwargs.get("window", 50)
57
+ offset = kwargs.get("offset", 0)
58
+ symbol = kwargs.get("symbol", "")
59
+
60
+ ta = PlotlyTA()
61
+ fig = ta.plot(
62
+ data,
63
+ {f"{ma_type.lower()}": dict(length=window, offset=offset)},
64
+ f"{symbol.upper()} {ma_type.upper()}",
65
+ False,
66
+ volume=False,
67
+ )
68
+ fig.update_layout(ChartStyle().plotly_template.get("layout", {}))
69
+ content = fig.show(external=True).to_plotly_json()
70
+
71
+ return fig, content
72
+
73
+
74
+ def technical_zlma(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
75
+ """Zero lag moving average chart."""
76
+ ma_type = "zlma"
77
+ return _ta_ma(ma_type, **kwargs)
78
+
79
+
80
+ def technical_aroon(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
81
+ """Aroon chart."""
82
+ data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
83
+ length = kwargs.get("length", 25)
84
+ scalar = kwargs.get("scalar", 100)
85
+ symbol = kwargs.get("symbol", "")
86
+
87
+ ta = PlotlyTA()
88
+ fig = ta.plot(
89
+ data,
90
+ dict(aroon=dict(length=length, scalar=scalar)),
91
+ f"Aroon on {symbol}",
92
+ False,
93
+ volume=False,
94
+ )
95
+ content = fig.show(external=True).to_plotly_json()
96
+
97
+ return fig, content
98
+
99
+
100
+ def technical_sma(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
101
+ """Plot simple moving average chart."""
102
+ ma_type = "sma"
103
+ return _ta_ma(ma_type, **kwargs)
104
+
105
+
106
+ def technical_macd(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
107
+ """Plot moving average convergence divergence chart."""
108
+ data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
109
+ fast = kwargs.get("fast", 12)
110
+ slow = kwargs.get("slow", 26)
111
+ signal = kwargs.get("signal", 9)
112
+ symbol = kwargs.get("symbol", "")
113
+
114
+ ta = PlotlyTA()
115
+ fig = ta.plot(
116
+ data,
117
+ dict(macd=dict(fast=fast, slow=slow, signal=signal)),
118
+ f"{symbol.upper()} MACD",
119
+ False,
120
+ volume=False,
121
+ )
122
+ content = fig.show(external=True).to_plotly_json()
123
+
124
+ return fig, content
125
+
126
+
127
+ def technical_hma(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
128
+ """Hull moving average chart."""
129
+ ma_type = "hma"
130
+ return _ta_ma(ma_type, **kwargs)
131
+
132
+
133
+ def technical_adx(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
134
+ """Average directional movement index chart."""
135
+ data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
136
+ length = kwargs.get("length", 14)
137
+ scalar = kwargs.get("scalar", 100.0)
138
+ drift = kwargs.get("drift", 1)
139
+ symbol = kwargs.get("symbol", "")
140
+
141
+ ta = PlotlyTA()
142
+ fig = ta.plot(
143
+ data,
144
+ dict(adx=dict(length=length, scalar=scalar, drift=drift)),
145
+ f"Average Directional Movement Index (ADX) {symbol}",
146
+ False,
147
+ volume=False,
148
+ )
149
+ content = fig.show(external=True).to_plotly_json()
150
+
151
+ return fig, content
152
+
153
+
154
+ def technical_wma(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
155
+ """Weighted moving average chart."""
156
+ ma_type = "wma"
157
+ return _ta_ma(ma_type, **kwargs)
158
+
159
+
160
+ def technical_rsi(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
161
+ """Relative strength index chart."""
162
+ data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
163
+ window = kwargs.get("window", 14)
164
+ scalar = kwargs.get("scalar", 100.0)
165
+ drift = kwargs.get("drift", 1)
166
+ symbol = kwargs.get("symbol", "")
167
+
168
+ ta = PlotlyTA()
169
+ fig = ta.plot(
170
+ data,
171
+ dict(rsi=dict(length=window, scalar=scalar, drift=drift)),
172
+ f"{symbol.upper()} RSI {window}",
173
+ False,
174
+ volume=False,
175
+ )
176
+ content = fig.show(external=True).to_plotly_json()
177
+
178
+ return fig, content
179
+
180
+
181
+ def technical_ema(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
182
+ """Exponential moving average chart."""
183
+ ma_type = "ema"
184
+ return _ta_ma(ma_type, **kwargs)
185
+
186
+
187
+ def technical_cones(
188
+ **kwargs: TechnicalConesChartQueryParams,
189
+ ) -> Tuple["OpenBBFigure", Dict[str, Any]]:
190
+ """Volatility Cones Chart."""
191
+ data = kwargs.get("data")
192
+
193
+ if isinstance(data, pd.DataFrame) and not data.empty and "window" in data.columns:
194
+ df_ta = data.set_index("window")
195
+ else:
196
+ df_ta = basemodel_to_df(kwargs["obbject_item"], index="window") # type: ignore
197
+
198
+ df_ta.columns = [col.title().replace("_", " ") for col in df_ta.columns]
199
+
200
+ # Check if the data is formatted as expected.
201
+ if not all(col in df_ta.columns for col in ["Realized", "Min", "Median", "Max"]):
202
+ raise ValueError("Data supplied does not match the expected format.")
203
+
204
+ model = (
205
+ str(kwargs.get("model"))
206
+ .replace("std", "Standard Deviation")
207
+ .replace("_", "-")
208
+ .title()
209
+ if kwargs.get("model")
210
+ else "Standard Deviation"
211
+ )
212
+
213
+ symbol = str(kwargs.get("symbol")) + " - " if kwargs.get("symbol") else ""
214
+
215
+ title = (
216
+ str(kwargs.get("title"))
217
+ if kwargs.get("title")
218
+ else f"{symbol}Realized Volatility Cones - {model} Model"
219
+ )
220
+
221
+ colors = [
222
+ "green",
223
+ "red",
224
+ "burlywood",
225
+ "grey",
226
+ "orange",
227
+ "blue",
228
+ ]
229
+ color = 0
230
+
231
+ fig = OpenBBFigure()
232
+
233
+ fig.update_layout(ChartStyle().plotly_template.get("layout", {}))
234
+
235
+ text_color = "black" if ChartStyle().plt_style == "light" else "white"
236
+
237
+ for col in df_ta.columns:
238
+ fig.add_scatter(
239
+ x=df_ta.index,
240
+ y=df_ta[col],
241
+ name=col,
242
+ mode="lines+markers",
243
+ hovertemplate=f"{col}: %{{y}}<extra></extra>",
244
+ marker=dict(
245
+ color=colors[color],
246
+ size=11,
247
+ ),
248
+ )
249
+ color += 1
250
+
251
+ fig.set_title(title)
252
+
253
+ fig.update_layout(
254
+ paper_bgcolor="rgba(0,0,0,0)",
255
+ plot_bgcolor="rgba(0,0,0,0)",
256
+ font=dict(color=text_color),
257
+ legend=dict(
258
+ orientation="h",
259
+ yanchor="bottom",
260
+ xanchor="right",
261
+ y=1.02,
262
+ x=1,
263
+ bgcolor="rgba(0,0,0,0)",
264
+ ),
265
+ yaxis=dict(
266
+ ticklen=0,
267
+ ),
268
+ xaxis=dict(
269
+ type="category",
270
+ tickmode="array",
271
+ ticklen=0,
272
+ tickvals=df_ta.index,
273
+ ticktext=df_ta.index,
274
+ title_text="Period",
275
+ showgrid=False,
276
+ zeroline=False,
277
+ ),
278
+ margin=dict(l=20, r=20, b=20),
279
+ dragmode="pan",
280
+ )
281
+
282
+ content = fig.to_plotly_json()
283
+
284
+ return fig, content
285
+
286
+
287
+ def economy_fred_series(
288
+ **kwargs: Union[Any, FredSeriesChartQueryParams],
289
+ ) -> Tuple["OpenBBFigure", Dict[str, Any]]:
290
+ """FRED Series Chart."""
291
+ ytitle_dict = {
292
+ "chg": "Change",
293
+ "ch1": "Change From Year Ago",
294
+ "pch": "Percent Change",
295
+ "pc1": "Percent Change From Year Ago",
296
+ "pca": "Compounded Annual Rate Of Change",
297
+ "cch": "Continuously Compounded Rate Of Change",
298
+ "cca": "Continuously Compounded Annual Rate Of Change",
299
+ "log": "Natural Log",
300
+ }
301
+
302
+ colors = [
303
+ "#1f77b4",
304
+ "#7f7f7f",
305
+ "#ff7f0e",
306
+ "#2ca02c",
307
+ "#d62728",
308
+ "#9467bd",
309
+ "#8c564b",
310
+ "#e377c2",
311
+ "#7f7f7f",
312
+ "#bcbd22",
313
+ "#17becf",
314
+ ]
315
+
316
+ provider = kwargs.get("provider")
317
+
318
+ if provider != "fred":
319
+ raise RuntimeError(
320
+ f"This charting method does not support {provider}. Supported providers: fred."
321
+ )
322
+
323
+ columns = basemodel_to_df(kwargs["obbject_item"], index=None).columns.to_list() # type: ignore
324
+
325
+ allow_unsafe = kwargs.get("allow_unsafe", False)
326
+ dropnan = kwargs.get("dropna", True)
327
+ normalize = kwargs.get("normalize", False)
328
+
329
+ data_cols = []
330
+ data = kwargs.get("data")
331
+
332
+ if isinstance(data, pd.DataFrame) and not data.empty:
333
+ data_cols = data.columns.to_list()
334
+ df_ta = data
335
+
336
+ else:
337
+ df_ta = basemodel_to_df(kwargs["obbject_item"], index="date") # type: ignore
338
+
339
+ # Check for unsupported external data injection.
340
+ if allow_unsafe is False and data_cols:
341
+ for data_col in data_cols:
342
+ if data_col not in columns:
343
+ raise RuntimeError(
344
+ f"Column '{data_col}' was not found in the original data."
345
+ + " External data injection is not supported unless `allow_unsafe = True`."
346
+ )
347
+
348
+ # Align the data so each column has the same index and length.
349
+ if dropnan:
350
+ df_ta = df_ta.dropna(how="any")
351
+
352
+ if df_ta.empty or len(df_ta) < 2:
353
+ raise ValueError(
354
+ "No data is left after dropping NaN values. Try setting `dropnan = False`,"
355
+ + " or use the `frequency` parameter on request ."
356
+ )
357
+
358
+ columns = df_ta.columns.to_list()
359
+
360
+ def z_score_standardization(data: pd.Series) -> pd.Series:
361
+ """Z-Score Standardization Method."""
362
+ return (data - data.mean()) / data.std()
363
+
364
+ if normalize:
365
+ df_ta = df_ta.apply(z_score_standardization)
366
+
367
+ # Extract the metadata from the warnings.
368
+ warnings = kwargs.get("warnings")
369
+ metadata = json.loads(warnings[0].message) if warnings else {} # type: ignore
370
+
371
+ # Check if the request was transformed by the FRED API.
372
+ params = kwargs["extra_params"] if kwargs.get("extra_params") else {}
373
+ has_params = hasattr(params, "transform") and params.transform is not None # type: ignore
374
+
375
+ # Get a unique list of all units of measurement in the DataFrame.
376
+ y_units = list({metadata.get(col).get("units") for col in columns if col in metadata}) # type: ignore
377
+
378
+ if len(y_units) > 2 and has_params is False and allow_unsafe is True:
379
+ raise RuntimeError(
380
+ "This method supports up to 2 y-axis units."
381
+ + " Please use the 'transform' parameter, in the data request,"
382
+ + " to compare all series on the same scale, or set `normalize = True`."
383
+ + " Override this error by setting `allow_unsafe = True`."
384
+ )
385
+
386
+ y1_units = y_units[0] if y_units else None
387
+ y1title = y1_units
388
+ y2title = y_units[1] if len(y_units) > 1 else None
389
+ xtitle = ""
390
+
391
+ # If the request was transformed, the y-axis will be shared under these conditions.
392
+ if has_params and any(
393
+ i in params.transform for i in ["pc1", "pch", "pca", "cch", "cca", "log"] # type: ignore
394
+ ):
395
+ y1title = "Log" if params.transform == "Log" else "Percent" # type: ignore
396
+ y2title = None
397
+
398
+ # Set the title for the chart.
399
+ title: str = ""
400
+ if isinstance(kwargs, dict) and title in kwargs:
401
+ title = kwargs["title"]
402
+ else:
403
+ if metadata.get(columns[0]):
404
+ title = metadata.get(columns[0]).get("title") if len(columns) == 1 else "FRED Series" # type: ignore
405
+ else:
406
+ title = "FRED Series"
407
+ transform_title = ytitle_dict.get(params.transform) if has_params is True else "" # type: ignore
408
+ title = f"{title} - {transform_title}" if transform_title else title
409
+
410
+ # Define this to use as a check.
411
+ y3title: Optional[str] = ""
412
+
413
+ # Create the figure object with subplots.
414
+ fig = OpenBBFigure().create_subplots(
415
+ rows=1, cols=1, shared_xaxes=True, shared_yaxes=False
416
+ )
417
+ fig.update_layout(ChartStyle().plotly_template.get("layout", {}))
418
+ text_color = "black" if ChartStyle().plt_style == "light" else "white"
419
+
420
+ # For each series in the DataFrame, add a scatter plot.
421
+ for i, col in enumerate(df_ta.columns):
422
+
423
+ # Check if the y-axis should be shared for this series.
424
+ on_y1 = (
425
+ (
426
+ metadata.get(col).get("units") == y1_units
427
+ or y2title is None # type: ignore
428
+ )
429
+ if metadata.get(col)
430
+ else False
431
+ )
432
+ if normalize:
433
+ on_y1 = True
434
+ yaxes = "y2" if not on_y1 else "y1"
435
+ on_y3 = not metadata.get(col) and normalize is False
436
+ if on_y3:
437
+ yaxes = "y3"
438
+ y3title = df_ta[col].name
439
+ fig.add_scatter(
440
+ x=df_ta.index,
441
+ y=df_ta[col],
442
+ name=df_ta[col].name,
443
+ mode="lines",
444
+ hovertemplate=f"{df_ta[col].name}: %{{y}}<extra></extra>",
445
+ line=dict(width=1, color=colors[i % len(colors)]),
446
+ yaxis=yaxes,
447
+ )
448
+
449
+ # Set the y-axis titles, if supplied.
450
+ if kwargs.get("y1title"):
451
+ y1title = kwargs.get("y1title")
452
+ if kwargs.get("y2title") and y2title is not None:
453
+ y2title = kwargs.get("y2title")
454
+ # Set the x-axis title, if suppiled.
455
+ if isinstance(kwargs, dict) and "xtitle" in kwargs:
456
+ xtitle = kwargs["xtitle"]
457
+ # If the data was normalized, set the title to reflect this.
458
+ if normalize:
459
+ y1title = None
460
+ y2title = None
461
+ y3title = None
462
+ title = f"{title} - Normalized" if title else "Normalized"
463
+
464
+ # Now update the layout of the complete figure.
465
+ fig.update_layout(
466
+ title=dict(text=title, x=0.5, font=dict(size=16)),
467
+ paper_bgcolor="rgba(0,0,0,0)",
468
+ plot_bgcolor="rgba(0,0,0,0)",
469
+ font=dict(color=text_color),
470
+ legend=dict(
471
+ orientation="h",
472
+ yanchor="bottom",
473
+ xanchor="right",
474
+ y=1.02,
475
+ x=1,
476
+ bgcolor="rgba(0,0,0,0)",
477
+ ),
478
+ yaxis=(
479
+ dict(
480
+ ticklen=0,
481
+ side="right",
482
+ title=dict(text=y1title, standoff=30, font=dict(size=18)),
483
+ tickfont=dict(size=14),
484
+ anchor="x",
485
+ )
486
+ if y1title
487
+ else None
488
+ ),
489
+ yaxis2=(
490
+ dict(
491
+ overlaying="y",
492
+ side="left",
493
+ ticklen=0,
494
+ showgrid=False,
495
+ title=dict(
496
+ text=y2title if y2title else None, standoff=10, font=dict(size=18)
497
+ ),
498
+ tickfont=dict(size=14),
499
+ anchor="x",
500
+ )
501
+ if y2title
502
+ else None
503
+ ),
504
+ yaxis3=(
505
+ dict(
506
+ overlaying="y",
507
+ side="left",
508
+ ticklen=0,
509
+ position=0,
510
+ showgrid=False,
511
+ showticklabels=True,
512
+ title=(
513
+ dict(text=y3title, standoff=10, font=dict(size=16))
514
+ if y3title
515
+ else None
516
+ ),
517
+ tickfont=dict(size=12, color="rgba(128,128,128,0.75)"),
518
+ anchor="free",
519
+ )
520
+ if y3title
521
+ else None
522
+ ),
523
+ xaxis=dict(
524
+ ticklen=0,
525
+ showgrid=False,
526
+ title=(
527
+ dict(text=xtitle, standoff=30, font=dict(size=18)) if xtitle else None
528
+ ),
529
+ domain=[0.095, 0.95] if y3title else None,
530
+ ),
531
+ margin=dict(r=25, l=25) if normalize is False else None,
532
+ autosize=True,
533
+ dragmode="pan",
534
+ )
535
+
536
+ content = fig.to_plotly_json()
537
+
538
+ return fig, content
@@ -152,7 +152,6 @@ class Backend(PyWry):
152
152
  theme: Optional[str] = None,
153
153
  ) -> dict:
154
154
  """Get the json update for the backend."""
155
-
156
155
  posthog: Dict[str, Any] = dict(collect_logs=self.charting_settings.log_collect)
157
156
  if (
158
157
  self.charting_settings.log_collect
@@ -222,6 +221,11 @@ class Backend(PyWry):
222
221
  self.send_outgoing(outgoing)
223
222
 
224
223
  if export_image and isinstance(export_image, Path):
224
+ if self.loop.is_closed(): # type: ignore[has-type]
225
+ # Create a new event loop
226
+ self.loop = asyncio.new_event_loop()
227
+ asyncio.set_event_loop(self.loop)
228
+
225
229
  self.loop.run_until_complete(self.process_image(export_image))
226
230
 
227
231
  async def process_image(self, export_image: Path):
@@ -507,6 +511,7 @@ if not PLOTLYJS_PATH.exists() and not JUPYTER_NOTEBOOK:
507
511
 
508
512
 
509
513
  def create_backend(charting_settings: Optional["ChartingSettings"] = None):
514
+ """Create the backend."""
510
515
  # # pylint: disable=import-outside-toplevel
511
516
  from openbb_core.app.model.charts.charting_settings import ChartingSettings
512
517
 
@@ -517,6 +522,7 @@ def create_backend(charting_settings: Optional["ChartingSettings"] = None):
517
522
 
518
523
 
519
524
  def get_backend() -> Backend:
525
+ """Get the backend instance."""
520
526
  if BACKEND is None:
521
527
  raise ValueError("Backend not created")
522
528
  return BACKEND
@@ -923,11 +923,7 @@ class OpenBBFigure(go.Figure):
923
923
  # If the backend fails, we just show the figure normally
924
924
  # This is a very rare case, but it's better to have a fallback
925
925
 
926
- if getattr(self._charting_settings, "debug_mode", False):
927
- warn(f"Failed to show figure with backend: {e}")
928
- warn(
929
- f"Failed to show figure with backend: {e}"
930
- ) # remove this line when the above lines are figured out
926
+ warn(f"Failed to show figure with backend. {e}")
931
927
 
932
928
  # We check if any figures were initialized before the backend failed
933
929
  # If so, we show them with the default plotly backend
@@ -1,4 +1,6 @@
1
- from typing import Any, Callable, Dict, Iterator, List, Optional, Type
1
+ """Base class for charting plugins."""
2
+
3
+ from typing import Any, Callable, Dict, Iterator, List, Optional, Type, Union
2
4
 
3
5
  import pandas as pd
4
6
 
@@ -6,7 +8,7 @@ from .data_classes import ChartIndicators, TAIndicator
6
8
 
7
9
 
8
10
  def columns_regex(df_ta: pd.DataFrame, name: str) -> List[str]:
9
- """Return columns that match regex name"""
11
+ """Return columns that match regex name."""
10
12
  column_name = df_ta.filter(regex=rf"{name}(?=[^\d]|$)").columns.tolist()
11
13
 
12
14
  return column_name
@@ -26,6 +28,7 @@ class Indicator:
26
28
  self.attrs = attrs
27
29
 
28
30
  def __call__(self, *args: Any, **kwargs: Any) -> Any:
31
+ """Call the indicator function."""
29
32
  return self.func(*args, **kwargs)
30
33
 
31
34
 
@@ -39,6 +42,7 @@ class PluginMeta(type):
39
42
  __subplots__: List[str] = []
40
43
 
41
44
  def __new__(mcs: Type["PluginMeta"], *args: Any, **kwargs: Any) -> "PluginMeta":
45
+ """Create a new instance of the class."""
42
46
  name, bases, attrs = args
43
47
  indicators: Dict[str, Indicator] = {}
44
48
  cls_attrs: Dict[str, list] = {
@@ -76,6 +80,7 @@ class PluginMeta(type):
76
80
  return new_cls
77
81
 
78
82
  def __iter__(cls: Type["PluginMeta"]) -> Iterator[Indicator]: # type: ignore
83
+ """Iterate over the indicators."""
79
84
  return iter(cls.__indicators__)
80
85
 
81
86
  # pylint: disable=unused-argument
@@ -88,11 +93,11 @@ class PltTA(metaclass=PluginMeta):
88
93
 
89
94
  indicators: ChartIndicators
90
95
  intraday: bool = False
91
- df_stock: pd.DataFrame
92
- df_ta: pd.DataFrame
96
+ df_stock: Union[pd.DataFrame, pd.Series]
97
+ df_ta: Optional[pd.DataFrame] = None
93
98
  df_fib: pd.DataFrame
94
99
  close_column: Optional[str] = "close"
95
- params: Dict[str, TAIndicator] = {}
100
+ params: Optional[Dict[str, TAIndicator]] = {}
96
101
  inchart_colors: List[str] = []
97
102
  show_volume: bool = True
98
103
 
@@ -104,6 +109,7 @@ class PltTA(metaclass=PluginMeta):
104
109
 
105
110
  # pylint: disable=unused-argument
106
111
  def __new__(cls, *args: Any, **kwargs: Any) -> "PltTA":
112
+ """Create a new instance of the class."""
107
113
  if cls is PltTA:
108
114
  raise TypeError("Can't instantiate abstract class Plugin directly")
109
115
  self = super().__new__(cls)
@@ -132,6 +138,7 @@ class PltTA(metaclass=PluginMeta):
132
138
 
133
139
  @property
134
140
  def ma_mode(self) -> List[str]:
141
+ """Moving average mode."""
135
142
  return list(set(self.__ma_mode__))
136
143
 
137
144
  @ma_mode.setter
@@ -139,7 +146,7 @@ class PltTA(metaclass=PluginMeta):
139
146
  self.__ma_mode__ = value
140
147
 
141
148
  def add_plugins(self, plugins: List["PltTA"]) -> None:
142
- """Add plugins to current instance"""
149
+ """Add plugins to current instance."""
143
150
  for plugin in plugins:
144
151
  for item in plugin.__indicators__:
145
152
  # pylint: disable=unnecessary-dunder-call
@@ -161,7 +168,7 @@ class PltTA(metaclass=PluginMeta):
161
168
  getattr(self, attr).extend(value)
162
169
 
163
170
  def remove_plugins(self, plugins: List["PltTA"]) -> None:
164
- """Remove plugins from current instance"""
171
+ """Remove plugins from current instance."""
165
172
  for plugin in plugins:
166
173
  for item in plugin.__indicators__:
167
174
  delattr(self, item.name)
@@ -171,10 +178,11 @@ class PltTA(metaclass=PluginMeta):
171
178
  delattr(self, static_method)
172
179
 
173
180
  def __iter__(self) -> Iterator[Indicator]:
181
+ """Iterate over the indicators."""
174
182
  return iter(self.__indicators__)
175
183
 
176
184
  def get_float_precision(self) -> str:
177
- """Returns f-string precision format"""
185
+ """Returns f-string precision format."""
178
186
  price = self.df_stock[self.close_column].tail(1).values[0]
179
187
  float_precision = (
180
188
  ",.2f" if price > 1.10 else "" if len(str(price)) < 8 else ".6f"
@@ -67,10 +67,6 @@ class TAIndicator:
67
67
  ]
68
68
  args: List[Arguments]
69
69
 
70
- def __post_init__(self):
71
- """Post init."""
72
- self.args = [Arguments(**arg) for arg in self.args]
73
-
74
70
  def __iter__(self):
75
71
  """Return iterator."""
76
72
  return iter(self.args)
@@ -98,14 +94,6 @@ class ChartIndicators:
98
94
 
99
95
  indicators: Optional[List[TAIndicator]] = None
100
96
 
101
- def __post_init__(self):
102
- """Post init."""
103
- self.indicators = (
104
- [TAIndicator(**indicator) for indicator in self.indicators]
105
- if self.indicators
106
- else []
107
- )
108
-
109
97
  def get_indicator(self, name: str) -> Union[TAIndicator, None]:
110
98
  """Return indicator with given name."""
111
99
  output = None
@@ -165,21 +153,43 @@ class ChartIndicators:
165
153
  @staticmethod
166
154
  def get_available_indicators() -> Tuple[str, ...]:
167
155
  """Return tuple of available indicators."""
168
- return list(
156
+ return tuple(
169
157
  TAIndicator.__annotations__["name"].__args__ # pylint: disable=E1101
170
158
  )
171
159
 
172
160
  @classmethod
173
- def from_dict(cls, indicators: Dict[str, Dict[str, Any]]) -> "ChartIndicators":
174
- """Return ChartIndicators from dictionary."""
175
- data = []
176
- for indicator in indicators:
177
- args = []
178
- for arg in indicators[indicator]:
179
- args.append({"label": arg, "values": indicators[indicator][arg]})
180
- data.append({"name": indicator, "args": args})
181
-
182
- return cls(indicators=data) # type: ignore
161
+ def from_dict(
162
+ cls, indicators: Dict[str, Dict[str, List[Dict[str, Any]]]]
163
+ ) -> "ChartIndicators":
164
+ """Return ChartIndicators from dictionary.
165
+
166
+ Example
167
+ -------
168
+ ChartIndicators.from_dict(
169
+ {
170
+ "ad": {
171
+ "args": [
172
+ {
173
+ "label": "AD_LABEL",
174
+ "values": [1, 2, 3],
175
+ }
176
+ ]
177
+ }
178
+ }
179
+ )
180
+ """
181
+ return cls(
182
+ indicators=[
183
+ TAIndicator(
184
+ name=name, # type: ignore[arg-type]
185
+ args=[
186
+ Arguments(label=label, values=values)
187
+ for label, values in args.items()
188
+ ],
189
+ )
190
+ for name, args in indicators.items()
191
+ ]
192
+ )
183
193
 
184
194
  def to_dataframe(
185
195
  self, df_ta: pd.DataFrame, ma_mode: Optional[List[str]] = None
@@ -88,7 +88,7 @@ class PlotlyTA(PltTA):
88
88
 
89
89
  inchart_colors: List[str] = []
90
90
  plugins: List[Type[PltTA]] = []
91
- df_ta: pd.DataFrame = None
91
+ df_ta: Optional[pd.DataFrame] = None
92
92
  close_column: Optional[str] = "close"
93
93
  has_volume: bool = True
94
94
  show_volume: bool = True
@@ -112,11 +112,11 @@ class PlotlyTA(PltTA):
112
112
  # Creates the instance of the class and loads the plugins
113
113
  # We set the global variable to the instance of the class so that
114
114
  # the plugins are only loaded once
115
- PLOTLY_TA = super().__new__(cls)
116
- PLOTLY_TA._locate_plugins(
115
+ PLOTLY_TA = super().__new__(cls) # type: ignore[attr-defined, assignment]
116
+ PLOTLY_TA._locate_plugins( # type: ignore[attr-defined]
117
117
  getattr(cls.charting_settings, "debug_mode", False)
118
118
  )
119
- PLOTLY_TA.add_plugins(PLOTLY_TA.plugins)
119
+ PLOTLY_TA.add_plugins(PLOTLY_TA.plugins) # type: ignore[attr-defined, assignment]
120
120
 
121
121
  return PLOTLY_TA
122
122
 
@@ -180,7 +180,13 @@ class PlotlyTA(PltTA):
180
180
  df_stock = df_stock.to_frame()
181
181
 
182
182
  if not isinstance(indicators, ChartIndicators):
183
- indicators = ChartIndicators.from_dict(indicators or dict(dict()))
183
+ indicators = ChartIndicators.from_dict(indicators or {})
184
+
185
+ # Apply to_datetime to the index in a way that handles daylight savings.
186
+ df_stock.loc[:, "date"] = df_stock.index # type: ignore
187
+ df_stock["date"] = df_stock["date"].apply(pd.to_datetime)
188
+ df_stock.index = df_stock["date"] # type: ignore
189
+ df_stock.drop(columns=["date"], inplace=True)
184
190
 
185
191
  self.indicators = indicators
186
192
  self.intraday = df_stock.index[-2].time() != df_stock.index[-1].time()
@@ -283,7 +289,7 @@ class PlotlyTA(PltTA):
283
289
  def _clear_data(self):
284
290
  """Clear and reset all data to default values."""
285
291
  self.df_stock = None
286
- self.indicators = {}
292
+ self.indicators = ChartIndicators.from_dict({})
287
293
  self.params = None
288
294
  self.intraday = False
289
295
  self.show_volume = True
@@ -0,0 +1,105 @@
1
+ """Charting Extension Query Params."""
2
+
3
+ from typing import List, Optional, Union
4
+
5
+ from openbb_core.provider.abstract.data import Data
6
+ from openbb_core.provider.abstract.query_params import QueryParams
7
+ from pydantic import Field
8
+
9
+
10
+ class FredSeriesChartQueryParams(QueryParams):
11
+ """
12
+ FRED Series Chart Query Params.
13
+
14
+ kwargs
15
+ ------
16
+
17
+ data : List[Data], optional
18
+ Filtered versions of the data contained in the original results.
19
+ Example use is to reduce the number of columns or the length of data to plot.
20
+ To supply additional columns, set `allow_unsafe = True`.
21
+ title : str, optional
22
+ Title of the chart.
23
+ y1title : str, optional
24
+ Right Y-axis title.
25
+ y2title : str, optional
26
+ Left Y-axis title.
27
+ xtitle : str, optional
28
+ X-axis title.
29
+ dropnan: bool, optional (default: True)
30
+ If True, rows containing NaN will be dropped.
31
+ normalize: bool, optional (default: False)
32
+ If True, the data will be normalized and placed on the same axis.
33
+ allow_unsafe: bool, optional (default: False)
34
+ If True, the method will attempt to pass all supplied data to the chart constructor.
35
+ This can result in unexpected behavior.
36
+ """
37
+
38
+ data: Optional[Union[Data, List[Data]]] = Field(
39
+ default=None,
40
+ description="Filtered versions of the data contained in the original `self.results`."
41
+ + " Columns should be the same as the original data."
42
+ + " Example use is to reduce the number of columns or the length of data to plot."
43
+ + " To supply additional columns, set `allow_unsafe = True`.",
44
+ )
45
+ title: Optional[str] = Field(
46
+ default=None,
47
+ description="Title of the chart.",
48
+ )
49
+ y1title: Optional[str] = Field(
50
+ default=None,
51
+ description="Right Y-axis title.",
52
+ )
53
+ y2title: Optional[str] = Field(
54
+ default=None,
55
+ description="Left Y-axis title.",
56
+ )
57
+ xtitle: Optional[str] = Field(
58
+ default=None,
59
+ description="X-axis title.",
60
+ )
61
+ dropnan: bool = Field(
62
+ default=True,
63
+ description="If True, rows containing NaN will be dropped.",
64
+ )
65
+ normalize: bool = Field(
66
+ default=False,
67
+ description="If True, the data will be normalized and placed on the same axis.",
68
+ )
69
+ allow_unsafe: bool = Field(
70
+ default=False,
71
+ description="If True, the method will attempt to pass all supplied data to the chart constructor."
72
+ + " This can result in unexpected behavior.",
73
+ )
74
+
75
+
76
+ class TechnicalConesChartQueryParams(QueryParams):
77
+ """
78
+ Technical Cones Chart Query Params.
79
+
80
+ kwargs
81
+ ------
82
+
83
+ data : List[Data], optional
84
+ Filtered versions of the data contained in the original results.
85
+ Example use is to reduce the number of windows to plot.
86
+ title : str, optional
87
+ Title of the chart.
88
+ symbol: str, optional
89
+ Symbol represented by the data. Used to label the chart.
90
+ """
91
+
92
+ data: Optional[Union[Data, List[Data]]] = Field(
93
+ default=None,
94
+ description="Filtered versions of the data contained in the original results."
95
+ + " Columns should be the same as the original data."
96
+ + " Example use is to reduce the number of columns or the length of data to plot.",
97
+ )
98
+ title: Optional[str] = Field(
99
+ default=None,
100
+ description="Title of the chart.",
101
+ )
102
+ symbol: Optional[str] = Field(
103
+ default=None,
104
+ description="Symbol represented by the data. Used to label the chart.",
105
+ )
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "openbb-charting"
3
- version = "2.0.0"
3
+ version = "2.0.2"
4
4
  description = "Charting extension for OpenBB"
5
5
  authors = ["OpenBB Team <hello@openbb.co>"]
6
6
  readme = "README.md"
@@ -16,7 +16,7 @@ pywry = "^0.6.1"
16
16
  svglib = "^1.5.1"
17
17
  nbformat = "^5.9.2"
18
18
  pandas-ta = "^0.3.14b"
19
- openbb-core = "^1.1.2"
19
+ openbb-core = "^1.1.5"
20
20
 
21
21
  [build-system]
22
22
  requires = ["poetry-core"]
@@ -1,176 +0,0 @@
1
- """Charting router."""
2
-
3
- from typing import TYPE_CHECKING, Any, Dict, Tuple
4
-
5
- from openbb_core.app.model.charts.chart import ChartFormat
6
- from openbb_core.app.utils import basemodel_to_df
7
-
8
- from .core.plotly_ta.ta_class import PlotlyTA
9
-
10
- CHART_FORMAT = ChartFormat.plotly
11
-
12
- if TYPE_CHECKING:
13
- from .core.openbb_figure import OpenBBFigure
14
-
15
- # from .core.openbb_figure_table import OpenBBFigureTable
16
-
17
-
18
- def equity_price_historical(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
19
- """Equity price chart."""
20
-
21
- def handle_indicators(ma):
22
- """Handle indicators."""
23
- k = {}
24
- if ma:
25
- k["rma"] = dict(length=ma)
26
- return k
27
-
28
- data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
29
- standard_params = kwargs["standard_params"]
30
- ma = standard_params.get("ma", None)
31
- prepost = standard_params.get("prepost", False)
32
- symbol = standard_params.get("symbol", "")
33
-
34
- ta = PlotlyTA()
35
- fig = ta.plot(
36
- data,
37
- indicators=dict(**handle_indicators(ma)),
38
- symbol=f"{symbol} historical data",
39
- prepost=prepost,
40
- )
41
- content = fig.show(external=True).to_plotly_json()
42
-
43
- return fig, content
44
-
45
-
46
- def _ta_ma(ma_type: str, **kwargs):
47
- """Plot moving average helper."""
48
- data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
49
- window = kwargs.get("window", 50)
50
- offset = kwargs.get("offset", 0)
51
- symbol = kwargs.get("symbol", "")
52
-
53
- ta = PlotlyTA()
54
- fig = ta.plot(
55
- data,
56
- {f"{ma_type.lower()}": dict(length=window, offset=offset)},
57
- f"{symbol.upper()} {ma_type.upper()}",
58
- False,
59
- volume=False,
60
- )
61
- content = fig.show(external=True).to_plotly_json()
62
-
63
- return fig, content
64
-
65
-
66
- def technical_zlma(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
67
- """Zero lag moving average chart."""
68
- ma_type = "zlma"
69
- return _ta_ma(ma_type, **kwargs)
70
-
71
-
72
- def technical_aroon(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
73
- """Aroon chart."""
74
- data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
75
- length = kwargs.get("length", 25)
76
- scalar = kwargs.get("scalar", 100)
77
- symbol = kwargs.get("symbol", "")
78
-
79
- ta = PlotlyTA()
80
- fig = ta.plot(
81
- data,
82
- dict(aroon=dict(length=length, scalar=scalar)),
83
- f"Aroon on {symbol}",
84
- False,
85
- volume=False,
86
- )
87
- content = fig.show(external=True).to_plotly_json()
88
-
89
- return fig, content
90
-
91
-
92
- def technical_sma(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
93
- """Plot simple moving average chart."""
94
- ma_type = "sma"
95
- return _ta_ma(ma_type, **kwargs)
96
-
97
-
98
- def technical_macd(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
99
- """Plot moving average convergence divergence chart."""
100
- data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
101
- fast = kwargs.get("fast", 12)
102
- slow = kwargs.get("slow", 26)
103
- signal = kwargs.get("signal", 9)
104
- symbol = kwargs.get("symbol", "")
105
-
106
- ta = PlotlyTA()
107
- fig = ta.plot(
108
- data,
109
- dict(macd=dict(fast=fast, slow=slow, signal=signal)),
110
- f"{symbol.upper()} MACD",
111
- False,
112
- volume=False,
113
- )
114
- content = fig.show(external=True).to_plotly_json()
115
-
116
- return fig, content
117
-
118
-
119
- def technical_hma(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
120
- """Hull moving average chart."""
121
- ma_type = "hma"
122
- return _ta_ma(ma_type, **kwargs)
123
-
124
-
125
- def technical_adx(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
126
- """Average directional movement index chart."""
127
- data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
128
- length = kwargs.get("length", 14)
129
- scalar = kwargs.get("scalar", 100.0)
130
- drift = kwargs.get("drift", 1)
131
- symbol = kwargs.get("symbol", "")
132
-
133
- ta = PlotlyTA()
134
- fig = ta.plot(
135
- data,
136
- dict(adx=dict(length=length, scalar=scalar, drift=drift)),
137
- f"Average Directional Movement Index (ADX) {symbol}",
138
- False,
139
- volume=False,
140
- )
141
- content = fig.show(external=True).to_plotly_json()
142
-
143
- return fig, content
144
-
145
-
146
- def technical_wma(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
147
- """Weighted moving average chart."""
148
- ma_type = "wma"
149
- return _ta_ma(ma_type, **kwargs)
150
-
151
-
152
- def technical_rsi(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
153
- """Relative strength index chart."""
154
- data = basemodel_to_df(kwargs["obbject_item"], index=kwargs.get("index", "date"))
155
- window = kwargs.get("window", 14)
156
- scalar = kwargs.get("scalar", 100.0)
157
- drift = kwargs.get("drift", 1)
158
- symbol = kwargs.get("symbol", "")
159
-
160
- ta = PlotlyTA()
161
- fig = ta.plot(
162
- data,
163
- dict(rsi=dict(length=window, scalar=scalar, drift=drift)),
164
- f"{symbol.upper()} RSI {window}",
165
- False,
166
- volume=False,
167
- )
168
- content = fig.show(external=True).to_plotly_json()
169
-
170
- return fig, content
171
-
172
-
173
- def technical_ema(**kwargs) -> Tuple["OpenBBFigure", Dict[str, Any]]:
174
- """Exponential moving average chart."""
175
- ma_type = "ema"
176
- return _ta_ma(ma_type, **kwargs)