quantmod 0.0.6__tar.gz → 0.0.8__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 (44) hide show
  1. {quantmod-0.0.6 → quantmod-0.0.8}/PKG-INFO +12 -6
  2. {quantmod-0.0.6 → quantmod-0.0.8}/README.md +8 -4
  3. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/__init__.py +1 -0
  4. {quantmod-0.0.6/quantmod/datasets/data → quantmod-0.0.8/quantmod/charts}/__init__.py +4 -1
  5. quantmod-0.0.8/quantmod/charts/plotting.py +463 -0
  6. quantmod-0.0.8/quantmod/charts/themes.py +142 -0
  7. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/datasets/dataloader.py +19 -12
  8. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/derivatives/nse.py +7 -1
  9. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/risk/varbacktest.py +2 -1
  10. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/timeseries/performance.py +15 -5
  11. quantmod-0.0.8/quantmod/version.py +1 -0
  12. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod.egg-info/PKG-INFO +12 -6
  13. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod.egg-info/SOURCES.txt +3 -1
  14. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod.egg-info/requires.txt +3 -1
  15. quantmod-0.0.6/quantmod/version.py +0 -1
  16. {quantmod-0.0.6 → quantmod-0.0.8}/LICENSE.txt +0 -0
  17. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/_version.py +0 -0
  18. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/datasets/__init__.py +0 -0
  19. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/datasets/data/nifty50.csv +0 -0
  20. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/datasets/data/spx.csv +0 -0
  21. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/derivatives/__init__.py +0 -0
  22. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/indicators/__init__.py +0 -0
  23. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/indicators/indicators.py +0 -0
  24. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/main.py +0 -0
  25. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/markets/__init__.py +0 -0
  26. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/markets/bb.py +0 -0
  27. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/markets/yahoo.py +0 -0
  28. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/models/__init__.py +0 -0
  29. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/models/binomial.py +0 -0
  30. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/models/blackscholes.py +0 -0
  31. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/models/montecarlo.py +0 -0
  32. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/models/optioninputs.py +0 -0
  33. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/risk/__init__.py +0 -0
  34. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/risk/var.py +0 -0
  35. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/risk/varinputs.py +0 -0
  36. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/timeseries/__init__.py +0 -0
  37. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/timeseries/timeseries.py +0 -0
  38. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod/utils.py +0 -0
  39. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod.egg-info/dependency_links.txt +0 -0
  40. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod.egg-info/entry_points.txt +0 -0
  41. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod.egg-info/not-zip-safe +0 -0
  42. {quantmod-0.0.6 → quantmod-0.0.8}/quantmod.egg-info/top_level.txt +0 -0
  43. {quantmod-0.0.6 → quantmod-0.0.8}/setup.cfg +0 -0
  44. {quantmod-0.0.6 → quantmod-0.0.8}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: quantmod
3
- Version: 0.0.6
3
+ Version: 0.0.8
4
4
  Summary: Quantmod Python Package
5
5
  Home-page: https://kannansingaravelu.com/
6
6
  Author: Kannan Singaravelu
@@ -21,14 +21,16 @@ Description-Content-Type: text/markdown
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: joblib
23
23
  Requires-Dist: matplotlib
24
+ Requires-Dist: nbformat>=5.10.4
24
25
  Requires-Dist: numpy>=2.0.2
25
26
  Requires-Dist: pandas>=2.2.2
27
+ Requires-Dist: plotly>=6.1.2
26
28
  Requires-Dist: pydantic>=2.8.2
27
29
  Requires-Dist: scipy>=1.13.1
28
30
  Requires-Dist: sqlalchemy>=2.0.38
29
31
  Requires-Dist: tabulate>=0.9.0
30
32
  Requires-Dist: urllib3==1.26.15
31
- Requires-Dist: yfinance>=0.2.43
33
+ Requires-Dist: yfinance==0.2.58
32
34
  Dynamic: author
33
35
  Dynamic: author-email
34
36
  Dynamic: classifier
@@ -43,7 +45,7 @@ Dynamic: requires-python
43
45
  Dynamic: summary
44
46
 
45
47
 
46
- Quantmod Python package is inspired by the quantmod package for R. This new tool is designed to assist quantitative traders and data analysts with the development, testing, and rapid prototyping of trading strategies. quantmod features a straightforward and intuitive interface aimed at simplifying workflows and boosting productivity.
48
+ The quantmod package is inspired by the popular R package of the same name but reimagined for the modern Python data stack. It’s designed to support data scientists, analysts, and AI researchers with tools for fast, flexible data exploration and visualization. Whether you're working with time series, building machine learning pipelines, or prototyping data-driven ideas, quantmod offers a clean, intuitive interface that helps you move quickly from data to insight.
47
49
 
48
50
 
49
51
  ## Installation
@@ -56,13 +58,14 @@ pip install quantmod
56
58
 
57
59
  ## Modules
58
60
 
61
+ * [charts](https://kannansingaravelu.com/quantmod/charts/)
62
+ * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
63
+ * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
64
+ * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
59
65
  * [markets](https://kannansingaravelu.com/quantmod/markets/)
60
66
  * [models](https://kannansingaravelu.com/quantmod/models/)
61
67
  * [risk](https://kannansingaravelu.com/quantmod/risk/)
62
68
  * [timeseries](https://kannansingaravelu.com/quantmod/timeseries/)
63
- * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
64
- * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
65
- * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
66
69
 
67
70
 
68
71
  ## Quickstart
@@ -71,6 +74,9 @@ pip install quantmod
71
74
  # Retrieves market data & ticker object
72
75
  from quantmod.markets import getData, getTicker
73
76
 
77
+ # Charting module
78
+ import quantmod.charts
79
+
74
80
  # Option price
75
81
  from quantmod.models import OptionInputs, BlackScholesOptionPricing, MonteCarloOptionPricing
76
82
 
@@ -1,5 +1,5 @@
1
1
 
2
- Quantmod Python package is inspired by the quantmod package for R. This new tool is designed to assist quantitative traders and data analysts with the development, testing, and rapid prototyping of trading strategies. quantmod features a straightforward and intuitive interface aimed at simplifying workflows and boosting productivity.
2
+ The quantmod package is inspired by the popular R package of the same name but reimagined for the modern Python data stack. It’s designed to support data scientists, analysts, and AI researchers with tools for fast, flexible data exploration and visualization. Whether you're working with time series, building machine learning pipelines, or prototyping data-driven ideas, quantmod offers a clean, intuitive interface that helps you move quickly from data to insight.
3
3
 
4
4
 
5
5
  ## Installation
@@ -12,13 +12,14 @@ pip install quantmod
12
12
 
13
13
  ## Modules
14
14
 
15
+ * [charts](https://kannansingaravelu.com/quantmod/charts/)
16
+ * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
17
+ * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
18
+ * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
15
19
  * [markets](https://kannansingaravelu.com/quantmod/markets/)
16
20
  * [models](https://kannansingaravelu.com/quantmod/models/)
17
21
  * [risk](https://kannansingaravelu.com/quantmod/risk/)
18
22
  * [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
23
 
23
24
 
24
25
  ## Quickstart
@@ -27,6 +28,9 @@ pip install quantmod
27
28
  # Retrieves market data & ticker object
28
29
  from quantmod.markets import getData, getTicker
29
30
 
31
+ # Charting module
32
+ import quantmod.charts
33
+
30
34
  # Option price
31
35
  from quantmod.models import OptionInputs, BlackScholesOptionPricing, MonteCarloOptionPricing
32
36
 
@@ -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",
@@ -14,4 +14,7 @@
14
14
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
- #
17
+ #
18
+
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
@@ -0,0 +1,142 @@
1
+ import plotly.graph_objects as go
2
+
3
+ THEMES = {
4
+ "pearl": {
5
+ "colorscale": "original",
6
+ "linewidth": 1.3,
7
+ "bargap": 0.01,
8
+ "layout": {
9
+ "legend": {"bgcolor": "#F5F6F9", "font": {"color": "#4D5663"}},
10
+ "paper_bgcolor": "#F5F6F9",
11
+ "plot_bgcolor": "#F5F6F9",
12
+ "title": {"font": {"color": "#4D5663"}, "x": 0.5},
13
+ "yaxis": {
14
+ "tickfont": {"color": "#4D5663"},
15
+ "gridcolor": "#E1E5ED",
16
+ "title": {"font": {"color": "#4D5663"}},
17
+ "zerolinecolor": "#E1E5ED",
18
+ "showgrid": True,
19
+ },
20
+ "xaxis": {
21
+ "tickfont": {"color": "#4D5663"},
22
+ "gridcolor": "#E1E5ED",
23
+ "title": {"font": {"color": "#4D5663"}},
24
+ "zerolinecolor": "#E1E5ED",
25
+ "showgrid": True,
26
+ },
27
+ },
28
+ "annotations": {"fontcolor": "#4D5663", "arrowcolor": "#9499A3"},
29
+ "3d": {
30
+ "scene": {
31
+ "yaxis": {
32
+ "gridcolor": "#9499A3",
33
+ "zerolinecolor": "#9499A3",
34
+ "title": {"font": {"color": "#4D5663"}},
35
+ },
36
+ "xaxis": {
37
+ "gridcolor": "#9499A3",
38
+ "zerolinecolor": "#9499A3",
39
+ "title": {"font": {"color": "#4D5663"}},
40
+ },
41
+ "zaxis": {
42
+ "gridcolor": "#9499A3",
43
+ "zerolinecolor": "#9499A3",
44
+ "title": {"font": {"color": "#4D5663"}},
45
+ },
46
+ }
47
+ },
48
+ },
49
+ "henanigans": {
50
+ "colorscale": "original",
51
+ "linewidth": 1.3,
52
+ "bargap": 0.01,
53
+ "layout": {
54
+ "legend": {"bgcolor": "#242424", "font": {"color": "#F4F4F4"}},
55
+ "paper_bgcolor": "#242424",
56
+ "plot_bgcolor": "#242424",
57
+ "title": {"font": {"color": "#F4F4F4"}, "x": 0.5},
58
+ "yaxis": {
59
+ "tickfont": {"color": "#A4A4A4"},
60
+ "gridcolor": "#343434",
61
+ "title": {"font": {"color": "#A4A4A4"}},
62
+ "zerolinecolor": "#444444",
63
+ "showgrid": True,
64
+ },
65
+ "xaxis": {
66
+ "tickfont": {"color": "#A4A4A4"},
67
+ "gridcolor": "#343434",
68
+ "title": {"font": {"color": "#A4A4A4"}},
69
+ "zerolinecolor": "#444444",
70
+ "showgrid": True,
71
+ },
72
+ },
73
+ "annotations": {"fontcolor": "#EBB483", "arrowcolor": "#EBB483"},
74
+ "3d": {
75
+ "scene": {
76
+ "yaxis": {
77
+ "gridcolor": "#343434",
78
+ "zerolinecolor": "#343434",
79
+ "title": {"font": {"color": "#A4A4A4"}},
80
+ },
81
+ "xaxis": {
82
+ "gridcolor": "#343434",
83
+ "zerolinecolor": "#343434",
84
+ "title": {"font": {"color": "#A4A4A4"}},
85
+ },
86
+ "zaxis": {
87
+ "gridcolor": "#343434",
88
+ "zerolinecolor": "#343434",
89
+ "title": {"font": {"color": "#A4A4A4"}},
90
+ },
91
+ }
92
+ },
93
+ },
94
+ }
95
+
96
+ # Custom color scales
97
+ COLOR_SCALES = {
98
+ "original": [
99
+ "#FF6B35",
100
+ "#004E89",
101
+ "#00A896",
102
+ "#7209B7",
103
+ "#E63946",
104
+ "#2A9D8F",
105
+ "#F4A261",
106
+ "#6A994E",
107
+ "#FA8072",
108
+ "#87CEEB",
109
+ ]
110
+ }
111
+
112
+
113
+ # Create pearl template
114
+ def create_pearl_template():
115
+ """Create a proper Plotly template from the theme configuration"""
116
+ theme_config = THEMES["pearl"]["layout"]
117
+
118
+ template = go.layout.Template(layout=go.Layout(**theme_config))
119
+
120
+ return template
121
+
122
+
123
+ # Create henanigans template
124
+ def create_henanigans_template():
125
+ """Create a proper Plotly template from the theme configuration"""
126
+ theme_config = THEMES["henanigans"]["layout"]
127
+
128
+ template = go.layout.Template(layout=go.Layout(**theme_config))
129
+
130
+ return template
131
+
132
+
133
+ # Function to create any template
134
+ def create_template(theme_name):
135
+ """Create a template for any theme"""
136
+ if theme_name not in THEMES:
137
+ raise ValueError(f"Theme '{theme_name}' not found in THEMES")
138
+
139
+ theme_config = THEMES[theme_name]["layout"]
140
+ template = go.layout.Template(layout=go.Layout(**theme_config))
141
+
142
+ return template
@@ -1,10 +1,14 @@
1
1
  import pandas as pd
2
2
  from quantmod.utils import convert_date_format
3
- import importlib.resources as pkg_resources
3
+
4
+ # import importlib.resources as pkg_resources
5
+ from importlib.resources import files
4
6
  from . import data
5
7
 
6
8
 
7
- def fetch_historical_data(symbol: str, start_date: str=None, end_date: str=None) -> pd.DataFrame:
9
+ def fetch_historical_data(
10
+ symbol: str, start_date: str = None, end_date: str = None
11
+ ) -> pd.DataFrame:
8
12
  """
9
13
  Load historical data for a given symbol
10
14
 
@@ -26,15 +30,19 @@ def fetch_historical_data(symbol: str, start_date: str=None, end_date: str=None)
26
30
  ------
27
31
  ValueError
28
32
  If the symbol is invalid or the start date is greater than the end date
29
- If the start date is less than the first date in the dataset or the end date is greater than the last date in the dataset
33
+ If the start date is less than the first date in the dataset or the end date is greater than the last date in the dataset
30
34
  If the start date is greater than the last date in the dataset or the end date is less than the first date in the dataset
31
35
  """
32
36
 
37
+ # def read_csv_file(filename):
38
+ # with pkg_resources.path(data, filename) as file_path:
39
+ # return pd.read_csv(file_path)
40
+
33
41
  def read_csv_file(filename):
34
- with pkg_resources.path(data, filename) as file_path:
35
- return pd.read_csv(file_path)
42
+ with files(data).joinpath(filename).open("r") as f:
43
+ return pd.read_csv(f)
36
44
 
37
- if symbol.lower() == "spx":
45
+ if symbol.lower() == "spx":
38
46
  df = read_csv_file("spx.csv")
39
47
  # df = pd.read_csv("./data/spx.csv")
40
48
  elif symbol.lower() == "nifty":
@@ -42,24 +50,23 @@ def fetch_historical_data(symbol: str, start_date: str=None, end_date: str=None)
42
50
  # df = pd.read_csv("./data/nifty50.csv")
43
51
  else:
44
52
  raise ValueError("Invalid symbol. Only SPX & NIFTY is supported")
45
-
53
+
46
54
  df = df.rename(columns=str.lower)
47
- df = convert_date_format(df, 'date')
55
+ df = convert_date_format(df, "date")
48
56
 
49
57
  if start_date is None or end_date is None:
50
58
  return df
51
59
 
52
60
  elif start_date > end_date:
53
61
  raise ValueError("Start date should be less than or equal to end date")
54
-
62
+
55
63
  elif start_date < df.date.iloc[0] or end_date < df.date.iloc[0]:
56
64
  raise ValueError(f"Date should be greater than or equal to {df.date.iloc[0]}")
57
-
65
+
58
66
  elif start_date > df.date.iloc[-1] or end_date > df.date.iloc[-1]:
59
67
  raise ValueError(f"Date should be less than or equal to {df.date.iloc[-1]}")
60
-
68
+
61
69
  else:
62
70
  # Use query method for filtering based on date range
63
71
  query_str = f"date >= '{start_date}' and date <= '{end_date}'"
64
72
  return df.query(query_str)
65
-
@@ -47,7 +47,13 @@ if mode == "local":
47
47
  # print(output)
48
48
  except ValueError:
49
49
  s = requests.Session()
50
- output = s.get("http://nseindia.com", headers=headers)
50
+ try:
51
+ output = s.get("http://nseindia.com/option-chain", headers=headers)
52
+ output = s.get(payload, headers=headers).json()
53
+ except ValueError:
54
+ output = s.get("https://www.nseindia.com", headers=headers)
55
+ output = output.json()
56
+ # output = s.get("https://www.nseindia.com/option-chain", headers=headers) # replaced http://nseindia.com with https://www.nseindia.com/option-chain
51
57
  output = s.get(payload, headers=headers).json()
52
58
  return output
53
59
 
@@ -73,7 +73,7 @@ class VaRAnalyzer:
73
73
  data["shifted_returns"].rolling(window=self.window_forward).sum()
74
74
  )
75
75
  data["shifted_forward_returns"] = data["forward_returns"].shift(
76
- -self.window_forward + 1
76
+ -self.window_forward + 2
77
77
  )
78
78
 
79
79
  # Identify breaches
@@ -121,4 +121,5 @@ class VaRAnalyzer:
121
121
  ["Conditional Probability of Breaches", conditional_probability * 100],
122
122
  ]
123
123
 
124
+ # return data
124
125
  return tabulate(table, headers=header, floatfmt=(".2f"))
@@ -18,7 +18,7 @@ def periodReturn(
18
18
 
19
19
  Returns
20
20
  -------
21
- pd.DataFrame | pd.Series
21
+ pd.Series
22
22
  resampled dataframe series of log returns
23
23
  """
24
24
 
@@ -27,7 +27,10 @@ def periodReturn(
27
27
  raise ValueError("Input must be a pandas DataFrame or Series")
28
28
 
29
29
  # Initialize variable to hold log returns
30
- temp = pd.Series(dtype=float)
30
+ if isinstance(data, pd.DataFrame):
31
+ temp = pd.DataFrame(dtype=float)
32
+ else:
33
+ temp = pd.Series(dtype=float)
31
34
 
32
35
  if period is None:
33
36
  temp = np.log(data).diff()
@@ -92,11 +95,18 @@ def annualReturn(data: pd.DataFrame | pd.Series) -> pd.DataFrame | pd.Series:
92
95
  return periodReturn(data, period="A")
93
96
 
94
97
 
95
- def allReturn(data: pd.DataFrame | pd.Series) -> pd.DataFrame | pd.Series:
98
+ def allReturn(data: pd.Series) -> pd.Series:
96
99
  """Calculates annual returns for the specified inputs."""
97
100
  return periodReturn(data, period="all")
98
101
 
99
102
 
103
+ def rollingReturn(
104
+ data: pd.DataFrame | pd.Series, window: int = 10
105
+ ) -> pd.DataFrame | pd.Series:
106
+ """Calculates rolling returns for the specified inputs."""
107
+ return dailyReturn(data).rolling(window).sum()
108
+
109
+
100
110
  def cagr(returns: pd.Series, intra_period: int = 1, is_log: bool = False) -> float:
101
111
  """
102
112
  Compounded Annual Growth Rate (CAGR) is the annual rate of return
@@ -127,12 +137,12 @@ def cagr(returns: pd.Series, intra_period: int = 1, is_log: bool = False) -> flo
127
137
  if is_log:
128
138
  cumulative_returns = np.exp(returns.sum()) # for log returns
129
139
  years = len(returns) / (252 * intra_period)
130
- return (cumulative_returns[-1]) ** (1 / years) - 1
140
+ return (cumulative_returns.iloc[-1]) ** (1 / years) - 1
131
141
  else:
132
142
  cumulative_returns = (1 + returns).cumprod() # for simple returns
133
143
 
134
144
  years = len(returns) / (252 * intra_period)
135
- return (cumulative_returns[-1]) ** (1 / years) - 1
145
+ return (cumulative_returns.iloc[-1]) ** (1 / years) - 1
136
146
 
137
147
 
138
148
  def volatility(returns: pd.Series, intra_period: int = 1) -> float:
@@ -0,0 +1 @@
1
+ version = "0.0.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: quantmod
3
- Version: 0.0.6
3
+ Version: 0.0.8
4
4
  Summary: Quantmod Python Package
5
5
  Home-page: https://kannansingaravelu.com/
6
6
  Author: Kannan Singaravelu
@@ -21,14 +21,16 @@ Description-Content-Type: text/markdown
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: joblib
23
23
  Requires-Dist: matplotlib
24
+ Requires-Dist: nbformat>=5.10.4
24
25
  Requires-Dist: numpy>=2.0.2
25
26
  Requires-Dist: pandas>=2.2.2
27
+ Requires-Dist: plotly>=6.1.2
26
28
  Requires-Dist: pydantic>=2.8.2
27
29
  Requires-Dist: scipy>=1.13.1
28
30
  Requires-Dist: sqlalchemy>=2.0.38
29
31
  Requires-Dist: tabulate>=0.9.0
30
32
  Requires-Dist: urllib3==1.26.15
31
- Requires-Dist: yfinance>=0.2.43
33
+ Requires-Dist: yfinance==0.2.58
32
34
  Dynamic: author
33
35
  Dynamic: author-email
34
36
  Dynamic: classifier
@@ -43,7 +45,7 @@ Dynamic: requires-python
43
45
  Dynamic: summary
44
46
 
45
47
 
46
- Quantmod Python package is inspired by the quantmod package for R. This new tool is designed to assist quantitative traders and data analysts with the development, testing, and rapid prototyping of trading strategies. quantmod features a straightforward and intuitive interface aimed at simplifying workflows and boosting productivity.
48
+ The quantmod package is inspired by the popular R package of the same name but reimagined for the modern Python data stack. It’s designed to support data scientists, analysts, and AI researchers with tools for fast, flexible data exploration and visualization. Whether you're working with time series, building machine learning pipelines, or prototyping data-driven ideas, quantmod offers a clean, intuitive interface that helps you move quickly from data to insight.
47
49
 
48
50
 
49
51
  ## Installation
@@ -56,13 +58,14 @@ pip install quantmod
56
58
 
57
59
  ## Modules
58
60
 
61
+ * [charts](https://kannansingaravelu.com/quantmod/charts/)
62
+ * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
63
+ * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
64
+ * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
59
65
  * [markets](https://kannansingaravelu.com/quantmod/markets/)
60
66
  * [models](https://kannansingaravelu.com/quantmod/models/)
61
67
  * [risk](https://kannansingaravelu.com/quantmod/risk/)
62
68
  * [timeseries](https://kannansingaravelu.com/quantmod/timeseries/)
63
- * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
64
- * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
65
- * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
66
69
 
67
70
 
68
71
  ## Quickstart
@@ -71,6 +74,9 @@ pip install quantmod
71
74
  # Retrieves market data & ticker object
72
75
  from quantmod.markets import getData, getTicker
73
76
 
77
+ # Charting module
78
+ import quantmod.charts
79
+
74
80
  # Option price
75
81
  from quantmod.models import OptionInputs, BlackScholesOptionPricing, MonteCarloOptionPricing
76
82
 
@@ -14,9 +14,11 @@ quantmod.egg-info/entry_points.txt
14
14
  quantmod.egg-info/not-zip-safe
15
15
  quantmod.egg-info/requires.txt
16
16
  quantmod.egg-info/top_level.txt
17
+ quantmod/charts/__init__.py
18
+ quantmod/charts/plotting.py
19
+ quantmod/charts/themes.py
17
20
  quantmod/datasets/__init__.py
18
21
  quantmod/datasets/dataloader.py
19
- quantmod/datasets/data/__init__.py
20
22
  quantmod/datasets/data/nifty50.csv
21
23
  quantmod/datasets/data/spx.csv
22
24
  quantmod/derivatives/__init__.py
@@ -1,10 +1,12 @@
1
1
  joblib
2
2
  matplotlib
3
+ nbformat>=5.10.4
3
4
  numpy>=2.0.2
4
5
  pandas>=2.2.2
6
+ plotly>=6.1.2
5
7
  pydantic>=2.8.2
6
8
  scipy>=1.13.1
7
9
  sqlalchemy>=2.0.38
8
10
  tabulate>=0.9.0
9
11
  urllib3==1.26.15
10
- yfinance>=0.2.43
12
+ yfinance==0.2.58
@@ -1 +0,0 @@
1
- version = "0.0.6"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes