openbb-charting 2.3.2__tar.gz → 2.4.0__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 (45) hide show
  1. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/PKG-INFO +7 -7
  2. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/charting.py +74 -12
  3. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/charts/generic_charts.py +185 -15
  4. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/charts/helpers.py +1 -1
  5. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/charts/relative_rotation.py +146 -61
  6. openbb_charting-2.4.0/openbb_charting/core/assets/plotly-3.1.0.min.js +3882 -0
  7. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/backend.py +1 -17
  8. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/openbb_figure.py +32 -64
  9. openbb_charting-2.4.0/openbb_charting/core/plotly.html +4031 -0
  10. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/data_classes.py +3 -1
  11. openbb_charting-2.4.0/openbb_charting/core/table.html +165 -0
  12. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/styles/default/dark.pltstyle.json +1 -20
  13. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/styles/default/light.pltstyle.json +1 -21
  14. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/pyproject.toml +5 -5
  15. openbb_charting-2.3.2/openbb_charting/core/assets/plotly-3.0.0.min.js +0 -3879
  16. openbb_charting-2.3.2/openbb_charting/core/plotly.html +0 -4033
  17. openbb_charting-2.3.2/openbb_charting/core/table.html +0 -173
  18. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/README.md +0 -0
  19. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/__init__.py +0 -0
  20. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/charts/__init__.py +0 -0
  21. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/charts/correlation_matrix.py +0 -0
  22. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/charts/price_historical.py +0 -0
  23. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/charts/price_performance.py +0 -0
  24. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/__init__.py +0 -0
  25. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/assets/Terminal_icon.png +0 -0
  26. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/chart_style.py +0 -0
  27. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/config/__init__.py +0 -0
  28. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/config/openbb_styles.py +0 -0
  29. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/dummy_backend.py +0 -0
  30. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/__init__.py +0 -0
  31. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/base.py +0 -0
  32. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/plugins/__init__.py +0 -0
  33. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/plugins/custom_indicators_plugin.py +0 -0
  34. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/plugins/momentum_plugin.py +0 -0
  35. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/plugins/overlap_plugin.py +0 -0
  36. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/plugins/trend_indicators_plugin.py +0 -0
  37. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/plugins/volatility_plugin.py +0 -0
  38. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/plugins/volume_plugin.py +0 -0
  39. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/ta_class.py +0 -0
  40. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/plotly_ta/ta_helpers.py +0 -0
  41. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/core/to_chart.py +0 -0
  42. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/query_params.py +0 -0
  43. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/styles/__init__.py +0 -0
  44. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/styles/colors.py +0 -0
  45. {openbb_charting-2.3.2 → openbb_charting-2.4.0}/openbb_charting/styles/default/tables.pltstyle.json +0 -0
@@ -1,22 +1,22 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: openbb-charting
3
- Version: 2.3.2
3
+ Version: 2.4.0
4
4
  Summary: Charting extension for OpenBB
5
5
  License: AGPL-3.0-only
6
6
  Author: OpenBB Team
7
7
  Author-email: hello@openbb.co
8
- Requires-Python: >=3.9,<3.13
8
+ Requires-Python: >=3.9.21,<3.14
9
9
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.9
12
11
  Classifier: Programming Language :: Python :: 3.10
13
12
  Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
15
  Provides-Extra: pywry
16
16
  Requires-Dist: nbformat (>=5.10.0,<6.0.0)
17
- Requires-Dist: openbb-core (>=1.4.0,<2.0.0)
18
- Requires-Dist: pandas-ta-openbb (>=0.4.20,<0.5.0)
19
- Requires-Dist: plotly (>=5.24.1,<6.0.0)
17
+ Requires-Dist: openbb-core (>=1.5.1,<2.0.0)
18
+ Requires-Dist: pandas-ta-openbb (>=0.4.22,<0.5.0)
19
+ Requires-Dist: plotly (>=6.3.1,<7.0.0)
20
20
  Requires-Dist: pywry (>=0.6.2,<0.7.0) ; extra == "pywry"
21
21
  Description-Content-Type: text/markdown
22
22
 
@@ -18,6 +18,7 @@ from typing import (
18
18
  from warnings import warn
19
19
 
20
20
  from importlib_metadata import entry_points
21
+ from openbb_core.app.model.abstract.error import OpenBBError
21
22
  from openbb_core.app.model.charts.chart import Chart
22
23
  from openbb_core.app.model.obbject import OBBject
23
24
  from openbb_core.provider.abstract.data import Data
@@ -122,8 +123,8 @@ class Charting:
122
123
 
123
124
  create_backend(self._charting_settings)
124
125
  backend = get_backend()
125
- backend.start(debug=self._charting_settings.debug_mode)
126
- return backend
126
+ backend.start(debug=self._charting_settings.debug_mode) # type: ignore
127
+ return backend # type: ignore
127
128
 
128
129
  def _get_chart_function(self, route: str) -> Callable:
129
130
  """Given a route, it returns the chart function. The module must contain the given route."""
@@ -178,9 +179,9 @@ class Charting:
178
179
  else None
179
180
  )
180
181
  data_as_df: DataFrame = (
181
- basemodel_to_df(convert_to_basemodel(data), index=index)
182
+ basemodel_to_df(convert_to_basemodel(data), index=index) # type: ignore
182
183
  if has_data
183
- else self._obbject.to_dataframe(index=index)
184
+ else self._obbject.to_dataframe(index=index) # type: ignore
184
185
  )
185
186
  if "date" in data_as_df.columns:
186
187
  data_as_df = data_as_df.set_index("date")
@@ -369,6 +370,65 @@ class Charting:
369
370
 
370
371
  return fig
371
372
 
373
+ def create_3d_surface(
374
+ self,
375
+ X: "Series",
376
+ Y: "Series",
377
+ Z: "Series",
378
+ xtitle: Optional[str] = "DTE",
379
+ ytitle: Optional[str] = "Strike",
380
+ ztitle: Optional[str] = "IV",
381
+ colorscale: Optional[Union[str, list]] = None,
382
+ title: Optional[str] = None,
383
+ layout_kwargs: Optional[dict[str, Any]] = None,
384
+ theme: Optional[Literal["dark", "light"]] = None,
385
+ ) -> Union["OpenBBFigure", "Figure"]:
386
+ """Create a 3D surface chart.
387
+
388
+ Parameters
389
+ ----------
390
+ X : pd.Series
391
+ The x-axis data.
392
+ Y : pd.Series
393
+ The y-axis data.
394
+ Z : pd.Series
395
+ The z-axis data.
396
+ xtitle : str, optional
397
+ The title for the x-axis, by default "DTE".
398
+ ytitle : str, optional
399
+ The title for the y-axis, by default "Strike".
400
+ ztitle : str, optional
401
+ The title for the z-axis, by default "IV".
402
+ colorscale : Union[str, list], optional
403
+ The colorscale to use for the surface, by default None.
404
+ title : str, optional
405
+ The title of the chart, by default None.
406
+ layout_kwargs : Optional[dict[str, Any]], optional
407
+ Additional keyword arguments to apply with figure.update_layout(), by default None.
408
+
409
+ Returns
410
+ -------
411
+ OpenBBFigure
412
+ The OpenBBFigure object.
413
+ """
414
+ # pylint: disable=import-outside-toplevel
415
+ from openbb_charting.charts.generic_charts import surface3d
416
+
417
+ fig = surface3d(
418
+ X=X,
419
+ Y=Y,
420
+ Z=Z,
421
+ xtitle=xtitle,
422
+ ytitle=ytitle,
423
+ ztitle=ztitle,
424
+ colorscale=colorscale,
425
+ title=title,
426
+ layout_kwargs=layout_kwargs,
427
+ theme=theme,
428
+ )
429
+ fig = self._set_chart_style(fig)
430
+ return fig
431
+
372
432
  def create_correlation_matrix(
373
433
  self,
374
434
  data: Union[
@@ -417,7 +477,6 @@ class Charting:
417
477
  fig = self._set_chart_style(fig)
418
478
  return fig
419
479
 
420
- # pylint: disable=inconsistent-return-statements
421
480
  def show(self, render: bool = True, **kwargs):
422
481
  """Display chart and save it to the OBBject."""
423
482
  try:
@@ -435,24 +494,27 @@ class Charting:
435
494
  kwargs["provider"] = self._obbject.provider
436
495
  kwargs["extra"] = self._obbject.extra
437
496
  fig, content = charting_function(**kwargs)
438
- fig = self._set_chart_style(fig)
439
- content = fig.show(external=True, **kwargs).to_plotly_json()
440
497
  self._obbject.chart = Chart(fig=fig, content=content, format=self._format)
441
498
  if render:
442
499
  fig.show(**kwargs)
500
+
501
+ except (RuntimeError, OpenBBError) as e:
502
+ raise e from e
503
+
443
504
  except Exception: # pylint: disable=W0718
444
505
  try:
445
506
  fig = self.create_line_chart(data=self._obbject.results, render=False, **kwargs) # type: ignore
446
- fig = self._set_chart_style(fig)
507
+ fig = self._set_chart_style(fig) # type: ignore
447
508
  content = fig.show(external=True, **kwargs).to_plotly_json() # type: ignore
448
509
  self._obbject.chart = Chart(
449
510
  fig=fig, content=content, format=self._format
450
511
  )
451
512
  if render:
452
- return fig.show(**kwargs) # type: ignore
513
+ fig.show(**kwargs) # type: ignore
453
514
  except Exception as e:
454
515
  raise RuntimeError(
455
516
  "Failed to automatically create a generic chart with the data provided."
517
+ + f" -> {e} -> {e.args}"
456
518
  ) from e
457
519
 
458
520
  # pylint: disable=too-many-locals,inconsistent-return-statements
@@ -556,7 +618,7 @@ class Charting:
556
618
  except Exception: # pylint: disable=W0718
557
619
  try:
558
620
  fig = self.create_line_chart(data=data_as_df, render=False, **kwargs)
559
- fig = self._set_chart_style(fig)
621
+ fig = self._set_chart_style(fig) # type: ignore
560
622
  content = fig.show(external=True, **kwargs).to_plotly_json() # type: ignore
561
623
  self._obbject.chart = Chart(
562
624
  fig=fig, content=content, format=self._format
@@ -590,11 +652,11 @@ class Charting:
590
652
  new = "light" if current == "dark" else "dark"
591
653
  self._charting_settings.chart_style = new
592
654
  figure = self._obbject.chart.fig # type: ignore[union-attr]
593
- updated_figure = self._set_chart_style(figure)
655
+ updated_figure = self._set_chart_style(figure) # type: ignore[union-attr]
594
656
  self._obbject.chart.fig = updated_figure # type: ignore[union-attr]
595
657
  self._obbject.chart.content = updated_figure.show( # type: ignore[union-attr]
596
658
  external=True
597
- ).to_plotly_json()
659
+ ).to_plotly_json() # type: ignore[union-attr]
598
660
 
599
661
  @staticmethod
600
662
  def _convert_to_string(x):
@@ -58,7 +58,7 @@ def line_chart( # noqa: PLR0912
58
58
  raise ValueError("Error: Data is a required field.")
59
59
 
60
60
  auto_layout = False
61
- index = (
61
+ index = ( # type: ignore
62
62
  data.index.name
63
63
  if isinstance(data, (DataFrame, Series))
64
64
  else index if index is not None else x if x is not None else "date"
@@ -88,11 +88,13 @@ def line_chart( # noqa: PLR0912
88
88
  if found_index is False:
89
89
  df.set_index(df.iloc[:, 0], inplace=True)
90
90
 
91
+ target = target if target else "close"
92
+
91
93
  if "symbol" in df.columns and len(df.symbol.unique()) > 1:
92
- df = df.pivot(columns="symbol", values=target if target else "close")
94
+ df = df.pivot(columns="symbol", values=target)
93
95
 
94
96
  if "symbol" not in df.columns and target in df.columns:
95
- df = df[[target]]
97
+ df = df[[target]] # type: ignore
96
98
 
97
99
  y = y.split(",") if isinstance(y, str) else y
98
100
 
@@ -104,11 +106,11 @@ def line_chart( # noqa: PLR0912
104
106
  auto_layout = False
105
107
 
106
108
  if returns is True:
107
- df = df.apply(calculate_returns)
109
+ df = df.apply(calculate_returns) # type: ignore
108
110
  auto_layout = False
109
111
 
110
112
  if normalize is True:
111
- df = df.apply(z_score_standardization)
113
+ df = df.apply(z_score_standardization) # type: ignore
112
114
  auto_layout = False
113
115
 
114
116
  if layout_kwargs is None:
@@ -147,7 +149,7 @@ def line_chart( # noqa: PLR0912
147
149
  sorted_columns = diff.sort_values(ascending=False).index
148
150
  if sorted_columns is None or len(sorted_columns) == 0:
149
151
  raise ValueError("Error: expected data with numeric values.")
150
- df = df[sorted_columns]
152
+ df = df[sorted_columns] # type: ignore
151
153
 
152
154
  for i, col in enumerate(df.columns):
153
155
 
@@ -459,7 +461,7 @@ def bar_chart( # noqa: PLR0912
459
461
  "rgba(0,0,0,0)" if text_color == "white" else "rgba(255,255,255,0)"
460
462
  ),
461
463
  plot_bgcolor=(
462
- "rgba(0,0,0,0)" if text_color == "white" else "rbga(255,255,255,0)"
464
+ "rgba(0,0,0,0)" if text_color == "white" else "rgba(255,255,255,0)"
463
465
  ),
464
466
  legend=dict(
465
467
  orientation="v",
@@ -480,8 +482,7 @@ def bar_chart( # noqa: PLR0912
480
482
  ),
481
483
  ticklen=0,
482
484
  showgrid=orientation == "h",
483
- tickformat="<b>%{x}</b>",
484
- tickfont=dict(size=12),
485
+ tickfont=dict(size=12, family="sans-serif"),
485
486
  categoryorder="array" if orientation == "v" else None,
486
487
  categoryarray=bar_df.index if orientation == "v" else None,
487
488
  ),
@@ -589,20 +590,20 @@ def bar_increasing_decreasing( # pylint: disable=W0102
589
590
  except Exception as e:
590
591
  raise ValueError(f"Error: {e}") from e
591
592
 
592
- if not increasing_data.empty:
593
+ if not increasing_data.empty: # type: ignore
593
594
  figure.add_bar(
594
- x=increasing_data.index if orientation == "v" else increasing_data,
595
- y=increasing_data if orientation == "v" else increasing_data.index,
595
+ x=increasing_data.index if orientation == "v" else increasing_data, # type: ignore
596
+ y=increasing_data if orientation == "v" else increasing_data.index, # type: ignore
596
597
  marker=dict(color=colors[0]),
597
598
  orientation=orientation,
598
599
  showlegend=False,
599
600
  width=0.95 / len(keys) * 0.75 if barmode == "group" else 0.95,
600
601
  hoverinfo="y" if orientation == "v" else "x",
601
602
  )
602
- if not decreasing_data.empty:
603
+ if not decreasing_data.empty: # type: ignore
603
604
  figure.add_bar(
604
- x=decreasing_data.index if orientation == "v" else decreasing_data,
605
- y=decreasing_data if orientation == "v" else decreasing_data.index,
605
+ x=decreasing_data.index if orientation == "v" else decreasing_data, # type: ignore
606
+ y=decreasing_data if orientation == "v" else decreasing_data.index, # type: ignore
606
607
  marker=dict(color=colors[1]),
607
608
  orientation=orientation,
608
609
  showlegend=False,
@@ -653,3 +654,172 @@ def bar_increasing_decreasing( # pylint: disable=W0102
653
654
  )
654
655
 
655
656
  return figure
657
+
658
+
659
+ def surface3d(
660
+ X: "Series",
661
+ Y: "Series",
662
+ Z: "Series",
663
+ xtitle: Optional[str] = "DTE",
664
+ ytitle: Optional[str] = "Strike",
665
+ ztitle: Optional[str] = "IV",
666
+ colorscale: Optional[Union[str, list]] = None,
667
+ title: Optional[str] = None,
668
+ layout_kwargs: Optional[dict[str, Any]] = None,
669
+ theme: Optional[Literal["dark", "light"]] = None,
670
+ ) -> Union["OpenBBFigure", "Figure"]:
671
+ """Create a 3D surface chart.
672
+
673
+ Parameters
674
+ ----------
675
+ X : pd.Series
676
+ The x-axis data.
677
+ Y : pd.Series
678
+ The y-axis data.
679
+ Z : pd.Series
680
+ The z-axis data.
681
+ xtitle : str, optional
682
+ The title for the x-axis, by default "DTE".
683
+ ytitle : str, optional
684
+ The title for the y-axis, by default "Strike".
685
+ ztitle : str, optional
686
+ The title for the z-axis, by default "IV".
687
+ colorscale : Union[str, list], optional
688
+ The colorscale to use for the surface, by default None.
689
+ title : str, optional
690
+ The title of the chart, by default None.
691
+ layout_kwargs : Optional[dict[str, Any]], optional
692
+ Additional keyword arguments to apply with figure.update_layout(), by default None.
693
+
694
+ Returns
695
+ -------
696
+ OpenBBFigure
697
+ The OpenBBFigure object.
698
+ """
699
+ # pylint: disable=import-outside-toplevel
700
+ from openbb_core.app.model.abstract.error import OpenBBError # noqa
701
+ from openbb_charting.core.openbb_figure import OpenBBFigure
702
+ from numpy import vstack
703
+ from scipy.spatial import Delaunay
704
+ import numpy as np
705
+
706
+ try:
707
+ points3D = vstack((X, Y, Z)).T
708
+ points2D = points3D[:, :2]
709
+ tri = Delaunay(points2D)
710
+ II, J, K = tri.simplices.T
711
+ except Exception as e:
712
+ raise OpenBBError(f"Not enough points to render 3D: {e}") from e
713
+
714
+ fig = OpenBBFigure(create_backend=False)
715
+ chart_style = ChartStyle()
716
+ if theme:
717
+ chart_style.plt_style = theme
718
+ fig.update_layout(chart_style.plotly_template.get("layout", {}))
719
+ text_color = "white" if chart_style.plt_style == "dark" else "black"
720
+ fig.set_title(f"{title if title and title != 'OpenBB Platform' else ''}")
721
+ fig_kwargs = dict(z=Z, x=X, y=Y, i=II, j=J, k=K, intensity=Z)
722
+ customdata = np.array([[xtitle, ytitle, ztitle]] * len(X))
723
+
724
+ fig.add_mesh3d(
725
+ **fig_kwargs,
726
+ alphahull=0,
727
+ opacity=1,
728
+ contour=dict(color="black", show=True, width=15),
729
+ colorscale=(
730
+ colorscale
731
+ if colorscale
732
+ else [
733
+ [0, "darkred"],
734
+ [0.001, "crimson"],
735
+ [0.005, "red"],
736
+ [0.0075, "orangered"],
737
+ [0.015, "darkorange"],
738
+ [0.025, "orange"],
739
+ [0.04, "goldenrod"],
740
+ [0.055, "gold"],
741
+ [0.11, "magenta"],
742
+ [0.15, "plum"],
743
+ [0.4, "lightblue"],
744
+ [0.7, "royalblue"],
745
+ [0.9, "blue"],
746
+ [1, "darkblue"],
747
+ ]
748
+ ),
749
+ colorbar=dict(
750
+ len=0.66,
751
+ y=0.5,
752
+ thickness=15,
753
+ ),
754
+ customdata=customdata,
755
+ hovertemplate="<b>%{customdata[0]}</b>: %{x} <br>"
756
+ "<b>%{customdata[1]}</b>: %{y} <br>"
757
+ "<b>%{customdata[2]}</b>: %{z}<extra></extra>",
758
+ showscale=True,
759
+ flatshading=True,
760
+ lighting=dict(
761
+ ambient=0.95,
762
+ diffuse=0.9,
763
+ roughness=0.8,
764
+ specular=0.9,
765
+ fresnel=0.001,
766
+ vertexnormalsepsilon=0.0001,
767
+ facenormalsepsilon=0.0001,
768
+ ),
769
+ )
770
+ fig.update_layout(
771
+ scene=dict(
772
+ xaxis=dict(
773
+ backgroundcolor="rgb(94, 94, 94)",
774
+ gridcolor="white",
775
+ showbackground=True,
776
+ zerolinecolor="white",
777
+ title=dict(text=xtitle if xtitle else "DTE", font=dict(size=18)),
778
+ autorange="reversed",
779
+ tickfont=dict(size=12),
780
+ ),
781
+ yaxis=dict(
782
+ backgroundcolor="rgb(94, 94, 94)",
783
+ gridcolor="white",
784
+ showbackground=True,
785
+ zerolinecolor="white",
786
+ title=dict(text=ytitle if ytitle else "Strike", font=dict(size=18)),
787
+ tickfont=dict(size=12),
788
+ ),
789
+ zaxis=dict(
790
+ backgroundcolor="rgb(94, 94, 94)",
791
+ gridcolor="white",
792
+ showbackground=True,
793
+ zerolinecolor="white",
794
+ title=dict(text=ztitle if ztitle else "IV", font=dict(size=18)),
795
+ tickfont=dict(size=12),
796
+ ),
797
+ domain=dict(y=[0.0125, 0.95], x=[0.0125, 1]),
798
+ ),
799
+ title_x=0.5,
800
+ title_y=0.98,
801
+ scene_camera=dict(
802
+ up=dict(x=0, y=0, z=0.75),
803
+ center=dict(x=-0.01, y=0, z=-0.3),
804
+ eye=dict(x=1.75, y=1.75, z=0.69),
805
+ ),
806
+ paper_bgcolor=(
807
+ "rgba(21,21,21,1)" if text_color == "white" else "rgba(255,255,255,1)"
808
+ ),
809
+ plot_bgcolor=(
810
+ "rgba(21,21,21,1)" if text_color == "white" else "rgba(255,255,255,1)"
811
+ ),
812
+ font=dict(color=text_color),
813
+ # margin=dict(t=100, b=10, r=10, l=10),
814
+ )
815
+
816
+ fig.update_scenes(
817
+ aspectmode="manual",
818
+ aspectratio=dict(x=1.5, y=2.0, z=0.75),
819
+ dragmode="turntable",
820
+ )
821
+
822
+ if layout_kwargs:
823
+ fig.update_layout(layout_kwargs, overwrite=False)
824
+
825
+ return fig
@@ -38,7 +38,7 @@ def z_score_standardization(data: "Series") -> "Series":
38
38
 
39
39
  def calculate_returns(data: "Series") -> "Series":
40
40
  """Calculate the returns of a column."""
41
- return ((1 + data.pct_change().dropna()).cumprod() - 1) * 100
41
+ return ((1 + data.pct_change(fill_method=None).fillna(0)).cumprod() - 1) * 100
42
42
 
43
43
 
44
44
  def should_share_axis(