quantmod 0.0.5__tar.gz → 0.0.7__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 (46) hide show
  1. {quantmod-0.0.5 → quantmod-0.0.7}/PKG-INFO +12 -11
  2. {quantmod-0.0.5 → quantmod-0.0.7}/README.md +8 -8
  3. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/__init__.py +1 -0
  4. {quantmod-0.0.5/quantmod/derivatives → quantmod-0.0.7/quantmod/charts}/__init__.py +2 -3
  5. quantmod-0.0.7/quantmod/charts/plotting.py +463 -0
  6. quantmod-0.0.7/quantmod/charts/themes.py +142 -0
  7. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/datasets/dataloader.py +19 -12
  8. {quantmod-0.0.5/quantmod/datasets/data → quantmod-0.0.7/quantmod/derivatives}/__init__.py +5 -1
  9. quantmod-0.0.7/quantmod/derivatives/nse.py +280 -0
  10. quantmod-0.0.7/quantmod/models/montecarlo.py +152 -0
  11. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/risk/varbacktest.py +2 -1
  12. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/timeseries/performance.py +15 -5
  13. quantmod-0.0.7/quantmod/version.py +1 -0
  14. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod.egg-info/PKG-INFO +12 -11
  15. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod.egg-info/SOURCES.txt +4 -2
  16. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod.egg-info/requires.txt +3 -2
  17. quantmod-0.0.5/quantmod/derivatives/optionchain.py +0 -249
  18. quantmod-0.0.5/quantmod/models/montecarlo.py +0 -145
  19. quantmod-0.0.5/quantmod/version.py +0 -1
  20. {quantmod-0.0.5 → quantmod-0.0.7}/LICENSE.txt +0 -0
  21. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/_version.py +0 -0
  22. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/datasets/__init__.py +0 -0
  23. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/datasets/data/nifty50.csv +0 -0
  24. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/datasets/data/spx.csv +0 -0
  25. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/indicators/__init__.py +0 -0
  26. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/indicators/indicators.py +0 -0
  27. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/main.py +0 -0
  28. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/markets/__init__.py +0 -0
  29. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/markets/bb.py +0 -0
  30. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/markets/yahoo.py +0 -0
  31. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/models/__init__.py +0 -0
  32. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/models/binomial.py +0 -0
  33. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/models/blackscholes.py +0 -0
  34. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/models/optioninputs.py +0 -0
  35. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/risk/__init__.py +0 -0
  36. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/risk/var.py +0 -0
  37. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/risk/varinputs.py +0 -0
  38. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/timeseries/__init__.py +0 -0
  39. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/timeseries/timeseries.py +0 -0
  40. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod/utils.py +0 -0
  41. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod.egg-info/dependency_links.txt +0 -0
  42. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod.egg-info/entry_points.txt +0 -0
  43. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod.egg-info/not-zip-safe +0 -0
  44. {quantmod-0.0.5 → quantmod-0.0.7}/quantmod.egg-info/top_level.txt +0 -0
  45. {quantmod-0.0.5 → quantmod-0.0.7}/setup.cfg +0 -0
  46. {quantmod-0.0.5 → quantmod-0.0.7}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: quantmod
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: Quantmod Python Package
5
5
  Home-page: https://kannansingaravelu.com/
6
6
  Author: Kannan Singaravelu
@@ -20,16 +20,17 @@ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: joblib
23
- Requires-Dist: jugaad-data
24
23
  Requires-Dist: matplotlib
24
+ Requires-Dist: nbformat>=5.10.4
25
25
  Requires-Dist: numpy>=2.0.2
26
26
  Requires-Dist: pandas>=2.2.2
27
+ Requires-Dist: plotly>=6.1.2
27
28
  Requires-Dist: pydantic>=2.8.2
28
29
  Requires-Dist: scipy>=1.13.1
29
30
  Requires-Dist: sqlalchemy>=2.0.38
30
31
  Requires-Dist: tabulate>=0.9.0
31
32
  Requires-Dist: urllib3==1.26.15
32
- Requires-Dist: yfinance>=0.2.43
33
+ Requires-Dist: yfinance==0.2.58
33
34
  Dynamic: author
34
35
  Dynamic: author-email
35
36
  Dynamic: classifier
@@ -57,13 +58,13 @@ pip install quantmod
57
58
 
58
59
  ## Modules
59
60
 
60
- * [markets](https://kannansingaravelu.com/docs/site/markets/)
61
- * [models](https://kannansingaravelu.com/docs/site/models/)
62
- * [risk](https://kannansingaravelu.com/docs/site/risk/)
63
- * [timeseries](https://kannansingaravelu.com/docs/site/timeseries/)
64
- * [indicators](https://kannansingaravelu.com/docs/site/indicators/)
65
- * [derivatives](https://kannansingaravelu.com/docs/site/derivatives/)
66
- * [datasets](https://kannansingaravelu.com/docs/site/datasets/)
61
+ * [markets](https://kannansingaravelu.com/quantmod/markets/)
62
+ * [models](https://kannansingaravelu.com/quantmod/models/)
63
+ * [risk](https://kannansingaravelu.com/quantmod/risk/)
64
+ * [timeseries](https://kannansingaravelu.com/quantmod/timeseries/)
65
+ * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
66
+ * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
67
+ * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
67
68
 
68
69
 
69
70
  ## Quickstart
@@ -99,7 +100,7 @@ Refer to the [examples](https://kannansingaravelu.com/) section for more details
99
100
 
100
101
 
101
102
  ## Changelog
102
- The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/docs/site/changelog/)
103
+ The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/quantmod/changelog/)
103
104
 
104
105
 
105
106
  ## Community
@@ -12,13 +12,13 @@ pip install quantmod
12
12
 
13
13
  ## Modules
14
14
 
15
- * [markets](https://kannansingaravelu.com/docs/site/markets/)
16
- * [models](https://kannansingaravelu.com/docs/site/models/)
17
- * [risk](https://kannansingaravelu.com/docs/site/risk/)
18
- * [timeseries](https://kannansingaravelu.com/docs/site/timeseries/)
19
- * [indicators](https://kannansingaravelu.com/docs/site/indicators/)
20
- * [derivatives](https://kannansingaravelu.com/docs/site/derivatives/)
21
- * [datasets](https://kannansingaravelu.com/docs/site/datasets/)
15
+ * [markets](https://kannansingaravelu.com/quantmod/markets/)
16
+ * [models](https://kannansingaravelu.com/quantmod/models/)
17
+ * [risk](https://kannansingaravelu.com/quantmod/risk/)
18
+ * [timeseries](https://kannansingaravelu.com/quantmod/timeseries/)
19
+ * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
20
+ * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
21
+ * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
22
22
 
23
23
 
24
24
  ## Quickstart
@@ -54,7 +54,7 @@ Refer to the [examples](https://kannansingaravelu.com/) section for more details
54
54
 
55
55
 
56
56
  ## Changelog
57
- The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/docs/site/changelog/)
57
+ The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/quantmod/changelog/)
58
58
 
59
59
 
60
60
  ## Community
@@ -28,6 +28,7 @@ from quantmod import derivatives, indicators, markets, models, timeseries, datas
28
28
 
29
29
  __all__ = [
30
30
  "hello",
31
+ "charts",
31
32
  "derivatives",
32
33
  "indicators",
33
34
  "markets",
@@ -16,6 +16,5 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- from .optionchain import OptionChain
20
-
21
- __all__ = ["OptionChain"]
19
+ # Import plotlytools to enable the monkey-patching of pandas objects
20
+ from . import plotting
@@ -0,0 +1,463 @@
1
+ """
2
+ quantmod.charts.plotting
3
+
4
+ Interactive Plotting Utilities for Quantmod
5
+
6
+ Usage Notes:
7
+ - In **Jupyter notebooks**, interactive plots display inline automatically when you call `df.iplot(...)`.
8
+ - In **Python scripts**, interactive plots open in your default web browser. For best results, set:
9
+ import plotly.io as pio
10
+ pio.renderers.default = "browser"
11
+ fig = df.iplot(...)
12
+ fig.show()
13
+ - In **headless environments** (e.g., servers), save plots to HTML files:
14
+ fig = df.iplot(...)
15
+ fig.write_html("plot.html")
16
+ print("Plot saved to plot.html. Open this file in your browser to view the plot.")
17
+
18
+ Available Chart Types:
19
+ - "line": Line chart
20
+ - "scatter": Scatter plot
21
+ - "ohlc": OHLC (Open-High-Low-Close) chart
22
+ - "candlestick": Candlestick chart
23
+ - "subplots": Multiple line charts as subplots
24
+ - "histogram": Histogram of daily returns
25
+ - "bar": Bar chart
26
+ - "heatmap": Heatmap
27
+ - "box": Box plot
28
+ - "pie": Pie chart
29
+ - "treemap": Treemap chart
30
+ - "overlay": Overlay multiple series with secondary y-axis
31
+ - "normalized": Normalized line chart (base=100)
32
+
33
+ """
34
+
35
+ import pandas as pd
36
+ import plotly.graph_objects as go
37
+ import plotly.express.colors as pc
38
+ from plotly.subplots import make_subplots
39
+ import plotly.io as pio
40
+ from .themes import THEMES, COLOR_SCALES, create_template
41
+
42
+ # Set global Plotly theme and dimensions
43
+ # pio.templates["pearl"] = create_pearl_template()
44
+ # pio.templates.default = "pearl"
45
+ # DEFAULT_THEME = "pearl"
46
+
47
+ # Register all available themes
48
+ for theme_name in THEMES.keys():
49
+ pio.templates[theme_name] = create_template(theme_name)
50
+
51
+ # Set default template
52
+ pio.templates.default = "pearl"
53
+
54
+ DEFAULT_THEME = "pearl"
55
+ DEFAULT_WIDTH = 1000
56
+ DEFAULT_HEIGHT = 600
57
+
58
+
59
+ # Main plot dispatcher
60
+ def _iplot(self, kind="line", **kwargs):
61
+ if kind == "normalized":
62
+ return _plot_normalized(self, **kwargs)
63
+ if kind == "overlay":
64
+ return _plot_overlay(self, **kwargs)
65
+ if isinstance(self, pd.Series):
66
+ df = self.to_frame()
67
+ else:
68
+ df = self.copy()
69
+
70
+ # Create a lowercase column mapping
71
+ colmap = {col.lower(): col for col in df.columns}
72
+
73
+ if kind == "line":
74
+ fig = _plot_line(df, **kwargs)
75
+ elif kind == "scatter":
76
+ fig = _plot_scatter(df, **kwargs)
77
+ elif kind == "ohlc":
78
+ fig = _plot_ohlc(df, colmap, **kwargs)
79
+ elif kind == "candlestick":
80
+ fig = _plot_candlestick(df, colmap, **kwargs)
81
+ elif kind == "subplots":
82
+ fig = _plot_subplots(df, **kwargs)
83
+ elif kind == "histogram":
84
+ fig = _plot_histogram(df, **kwargs)
85
+ elif kind == "bar":
86
+ fig = _plot_bar(df, **kwargs)
87
+ elif kind == "heatmap":
88
+ fig = _plot_heatmap(df, **kwargs)
89
+ elif kind == "box":
90
+ fig = _plot_box(df, **kwargs)
91
+ elif kind == "pie":
92
+ fig = _plot_pie(df, **kwargs)
93
+ elif kind == "treemap":
94
+ fig = _plot_treemap(df, **kwargs)
95
+ else:
96
+ raise ValueError(f"Plot type '{kind}' not supported.")
97
+
98
+ return fig
99
+
100
+
101
+ def _get_colors(theme_name):
102
+ """Get colors for the specified theme - FIXED"""
103
+ colorscale_name = THEMES.get(theme_name, {}).get("colorscale", "original")
104
+ colors = COLOR_SCALES.get(colorscale_name, COLOR_SCALES["original"])
105
+ return colors
106
+
107
+
108
+ def _plot_line(df, x=None, y=None, **kwargs):
109
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
110
+ colors = _get_colors(theme_name)
111
+
112
+ trace_kwargs = {}
113
+ if "color" in kwargs:
114
+ trace_kwargs["line"] = {"color": kwargs.pop("color")}
115
+ else:
116
+ # Use theme colors if no specific color provided
117
+ trace_kwargs["line"] = {"color": colors[0]}
118
+
119
+ for k in ["line", "name", "hoverinfo", "opacity"]:
120
+ if k in kwargs:
121
+ if k == "line" and "line" in trace_kwargs:
122
+ trace_kwargs["line"].update(kwargs.pop(k))
123
+ else:
124
+ trace_kwargs[k] = kwargs.pop(k)
125
+
126
+ x_data = df.index if x is None else df[x]
127
+ y_data = df[y] if isinstance(y, str) else df.iloc[:, 0]
128
+
129
+ fig = go.Figure(
130
+ go.Scatter(x=x_data, y=y_data, mode="lines", name="", **trace_kwargs)
131
+ )
132
+ _update_layout(fig, kwargs, default_title="Stock Prices", yaxis_title="")
133
+ return fig
134
+
135
+
136
+ def _plot_scatter(df, x=None, y=None, **kwargs):
137
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
138
+ colors = _get_colors(theme_name)
139
+
140
+ trace_kwargs = {}
141
+ if "color" in kwargs:
142
+ trace_kwargs["marker"] = {"color": kwargs.pop("color")}
143
+ else:
144
+ trace_kwargs["marker"] = {"color": colors[0]}
145
+
146
+ for k in ["marker", "line", "name", "hoverinfo", "opacity"]:
147
+ if k in kwargs:
148
+ if k == "marker" and "marker" in trace_kwargs:
149
+ trace_kwargs["marker"].update(kwargs.pop(k))
150
+ else:
151
+ trace_kwargs[k] = kwargs.pop(k)
152
+
153
+ x_data = df.index if x is None else df[x]
154
+ y_data = df[y] if isinstance(y, str) else df.iloc[:, 0]
155
+
156
+ fig = go.Figure(
157
+ go.Scatter(x=x_data, y=y_data, mode="markers", name="", **trace_kwargs)
158
+ )
159
+ _update_layout(fig, kwargs, default_title="Scatter Plot", yaxis_title="")
160
+ return fig
161
+
162
+
163
+ def _plot_ohlc(df, colmap, **kwargs):
164
+ # df = df[-30:] if len(df) > 30 else df
165
+ fig = go.Figure(
166
+ go.Ohlc(
167
+ x=df.index,
168
+ open=df[colmap.get("open")],
169
+ high=df[colmap.get("high")],
170
+ low=df[colmap.get("low")],
171
+ close=df[colmap.get("close")],
172
+ name="",
173
+ )
174
+ )
175
+ _update_layout(fig, kwargs, default_title="OHLC Chart", yaxis_title="")
176
+ return fig
177
+
178
+
179
+ def _plot_candlestick(df, colmap, **kwargs):
180
+ # df = df[-30:] if len(df) > 30 else df
181
+ fig = go.Figure(
182
+ go.Candlestick(
183
+ x=df.index,
184
+ open=df[colmap.get("open")],
185
+ high=df[colmap.get("high")],
186
+ low=df[colmap.get("low")],
187
+ close=df[colmap.get("close")],
188
+ name="",
189
+ )
190
+ )
191
+ _update_layout(fig, kwargs, default_title="Candlestick Chart", yaxis_title="")
192
+ return fig
193
+
194
+
195
+ def _plot_subplots(df, cols=None, **kwargs):
196
+ ncols = kwargs.pop("ncols", 2)
197
+ shared_yaxes = kwargs.pop("shared_yaxes", False)
198
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
199
+ colors = _get_colors(theme_name)
200
+
201
+ cols = df.columns if cols is None else cols
202
+ n = len(cols)
203
+ rows = (n - 1) // ncols + 1
204
+ fig = make_subplots(rows=rows, cols=ncols, shared_yaxes=shared_yaxes)
205
+
206
+ for i, col in enumerate(cols):
207
+ r, c = divmod(i, ncols)
208
+ fig.add_trace(
209
+ go.Scatter(
210
+ x=df.index,
211
+ y=df[col],
212
+ mode="lines",
213
+ name=col,
214
+ line=dict(color=colors[i % len(colors)]),
215
+ ),
216
+ row=r + 1,
217
+ col=c + 1,
218
+ )
219
+ _update_layout(fig, kwargs, default_title="Subplots", yaxis_title="")
220
+ return fig
221
+
222
+
223
+ def _plot_histogram(df, columns=None, **kwargs):
224
+ overlap = kwargs.pop("overlap", True)
225
+ ncols = kwargs.pop("ncols", 2)
226
+ shared_yaxes = kwargs.pop("shared_yaxes", False)
227
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
228
+ colors = _get_colors(theme_name)
229
+
230
+ columns = df.columns if columns is None else columns
231
+
232
+ if overlap:
233
+ fig = go.Figure()
234
+ for i, col in enumerate(columns):
235
+ fig.add_trace(
236
+ go.Histogram(
237
+ x=df[col] * 100,
238
+ nbinsx=kwargs.get("nbinsx", 50),
239
+ name=col,
240
+ opacity=0.75,
241
+ marker_color=colors[i % len(colors)],
242
+ )
243
+ )
244
+ else:
245
+ rows = (len(columns) - 1) // ncols + 1
246
+ fig = make_subplots(rows=rows, cols=ncols, shared_yaxes=shared_yaxes)
247
+ for i, col in enumerate(columns):
248
+ r, c = divmod(i, ncols)
249
+ fig.add_trace(
250
+ go.Histogram(
251
+ x=df[col] * 100,
252
+ nbinsx=kwargs.get("nbinsx", 50),
253
+ name=col,
254
+ opacity=0.75,
255
+ marker_color=colors[i % len(colors)],
256
+ ),
257
+ row=r + 1,
258
+ col=c + 1,
259
+ )
260
+
261
+ _update_layout(
262
+ fig,
263
+ kwargs,
264
+ default_title="Histogram of Daily Returns",
265
+ xaxis_title="",
266
+ yaxis_title="Frequency",
267
+ )
268
+ return fig
269
+
270
+
271
+ def _plot_bar(df, x=None, y=None, scale=False, **kwargs):
272
+ if scale:
273
+ df = df * 100
274
+
275
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
276
+ colors = kwargs.pop("colors", _get_colors(theme_name))
277
+ fig = go.Figure()
278
+
279
+ if df.shape[0] == 1: # single-row DataFrame
280
+ y_vals = df.iloc[0] if y is None else df[y]
281
+ x_vals = df.columns if x is None else x
282
+ for i, (xi, yi) in enumerate(zip(x_vals, y_vals)):
283
+ fig.add_trace(
284
+ go.Bar(
285
+ x=[xi], y=[yi], name=str(xi), marker_color=colors[i % len(colors)]
286
+ )
287
+ )
288
+ else: # multi-row DataFrame — use index as x, columns as series
289
+ columns_to_plot = (
290
+ df.columns if y is None else ([y] if isinstance(y, str) else y)
291
+ )
292
+ for i, col in enumerate(columns_to_plot):
293
+ fig.add_trace(
294
+ go.Bar(
295
+ x=df.index,
296
+ y=df[col],
297
+ name=col,
298
+ marker_color=colors[i % len(colors)],
299
+ )
300
+ )
301
+
302
+ _update_layout(fig, kwargs, default_title="Bar Chart", yaxis_title="")
303
+ return fig
304
+
305
+
306
+ def _plot_heatmap(df, matrix=False, **kwargs):
307
+ colorscale = kwargs.pop("colorscale", "Viridis")
308
+ if matrix:
309
+ cols = df.columns
310
+ n = len(cols)
311
+ rows = (n - 1) // 2 + 1
312
+ fig = make_subplots(rows=rows, cols=2)
313
+ for i, col in enumerate(cols):
314
+ r, c = divmod(i, 2)
315
+ fig.add_trace(
316
+ go.Heatmap(
317
+ z=df[[col]].values, x=[col], y=df.index, colorscale=colorscale
318
+ ),
319
+ row=r + 1,
320
+ col=c + 1,
321
+ )
322
+ else:
323
+ fig = go.Figure(
324
+ data=go.Heatmap(
325
+ z=df.values, x=df.columns, y=df.index, colorscale=colorscale
326
+ )
327
+ )
328
+ _update_layout(fig, kwargs, default_title="Heatmap", xaxis_title="", yaxis_title="")
329
+ return fig
330
+
331
+
332
+ def _plot_box(df, columns=None, **kwargs):
333
+ columns = df.columns if columns is None else columns
334
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
335
+ colors = _get_colors(theme_name)
336
+
337
+ fig = go.Figure()
338
+ for i, col in enumerate(columns):
339
+ fig.add_trace(go.Box(y=df[col], name=col, marker_color=colors[i % len(colors)]))
340
+ _update_layout(
341
+ fig, kwargs, default_title="Box Plot", xaxis_title="", yaxis_title=""
342
+ )
343
+ return fig
344
+
345
+
346
+ def _plot_pie(df, names=None, values=None, **kwargs):
347
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
348
+ colors = _get_colors(theme_name)
349
+
350
+ names_data = df.index if names is None else df[names]
351
+ values_data = df.iloc[:, 0] if values is None else df[values]
352
+
353
+ fig = go.Figure(
354
+ data=[
355
+ go.Pie(
356
+ labels=names_data,
357
+ values=values_data,
358
+ marker_colors=colors[: len(names_data)],
359
+ )
360
+ ]
361
+ )
362
+ _update_layout(fig, kwargs, default_title="Pie Chart")
363
+ return fig
364
+
365
+
366
+ def _plot_overlay(df, secondary_y=None, **kwargs):
367
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
368
+ colors = _get_colors(theme_name)
369
+
370
+ fig = make_subplots(specs=[[{"secondary_y": True}]])
371
+ cols = df.columns
372
+ for i, col in enumerate(cols):
373
+ fig.add_trace(
374
+ go.Scatter(
375
+ x=df.index,
376
+ y=df[col],
377
+ mode="lines",
378
+ name=col,
379
+ line=dict(color=colors[i % len(colors)]),
380
+ ),
381
+ secondary_y=(col == secondary_y),
382
+ )
383
+ _update_layout(fig, kwargs, default_title="Overlay Chart", yaxis_title="")
384
+ return fig
385
+
386
+
387
+ def _plot_normalized(df, **kwargs):
388
+ theme_name = kwargs.get("theme", DEFAULT_THEME)
389
+ colors = _get_colors(theme_name)
390
+
391
+ norm_df = (df / df.iloc[0]) * 100
392
+ fig = go.Figure()
393
+ for i, col in enumerate(norm_df.columns):
394
+ fig.add_trace(
395
+ go.Scatter(
396
+ x=norm_df.index,
397
+ y=norm_df[col],
398
+ mode="lines",
399
+ name=col,
400
+ line=dict(color=colors[i % len(colors)]),
401
+ )
402
+ )
403
+ _update_layout(
404
+ fig,
405
+ kwargs,
406
+ default_title="Normalized Prices",
407
+ yaxis_title="Normalized (Base = 100)",
408
+ )
409
+ return fig
410
+
411
+
412
+ def _plot_treemap(df, path=None, values=None, labels=None, parents=None, **kwargs):
413
+ if path is not None:
414
+ df = df.copy()
415
+ for p in path:
416
+ if p not in df.columns:
417
+ raise ValueError(f"Column '{p}' not in DataFrame")
418
+ labels = df[path[-1]]
419
+ parents = df[path[-2]] if len(path) > 1 else [""] * len(df)
420
+ values = df[values or df.columns[0]]
421
+ elif labels is not None and parents is not None and values is not None:
422
+ labels = df[labels] if isinstance(labels, str) else labels
423
+ parents = df[parents] if isinstance(parents, str) else parents
424
+ values = df[values] if isinstance(values, str) else values
425
+ else:
426
+ raise ValueError(
427
+ "Must provide either 'path' or all of 'labels', 'parents', and 'values'"
428
+ )
429
+
430
+ fig = go.Figure(
431
+ go.Treemap(
432
+ labels=labels,
433
+ parents=parents,
434
+ values=values,
435
+ hoverinfo="label+value+percent parent",
436
+ marker=dict(colors=_get_colors(DEFAULT_THEME)),
437
+ )
438
+ )
439
+
440
+ _update_layout(fig, kwargs, default_title="Treemap")
441
+ return fig
442
+
443
+
444
+ def _update_layout(fig, user_kwargs, default_title="", xaxis_title="", yaxis_title=""):
445
+ theme_name = user_kwargs.pop("theme", DEFAULT_THEME)
446
+ theme_layout = THEMES.get(theme_name, {}).get("layout", {})
447
+
448
+ layout_defaults = dict(
449
+ title=default_title,
450
+ xaxis_title=xaxis_title,
451
+ yaxis_title=yaxis_title,
452
+ width=user_kwargs.pop("width", DEFAULT_WIDTH),
453
+ height=user_kwargs.pop("height", DEFAULT_HEIGHT),
454
+ showlegend=user_kwargs.pop("showlegend", False),
455
+ )
456
+ layout_defaults.update(theme_layout)
457
+ layout_defaults.update(user_kwargs) # allow override
458
+ fig.update_layout(**layout_defaults)
459
+
460
+
461
+ # Attach iplot method to both DataFrame and Series
462
+ pd.DataFrame.iplot = _iplot
463
+ pd.Series.iplot = _iplot