maidr 1.1.0__tar.gz → 1.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {maidr-1.1.0 → maidr-1.2.1}/PKG-INFO +2 -1
  2. {maidr-1.1.0 → maidr-1.2.1}/maidr/__init__.py +2 -1
  3. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/enum/plot_type.py +1 -0
  4. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/figure_manager.py +9 -3
  5. maidr-1.2.1/maidr/core/plot/candlestick.py +239 -0
  6. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/maidr_plot_factory.py +26 -10
  7. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/boxplot.py +1 -2
  8. maidr-1.2.1/maidr/patch/candlestick.py +57 -0
  9. {maidr-1.1.0 → maidr-1.2.1}/pyproject.toml +3 -2
  10. {maidr-1.1.0 → maidr-1.2.1}/LICENSE +0 -0
  11. {maidr-1.1.0 → maidr-1.2.1}/README.md +0 -0
  12. {maidr-1.1.0 → maidr-1.2.1}/maidr/api.py +0 -0
  13. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/__init__.py +0 -0
  14. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/context_manager.py +0 -0
  15. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/enum/__init__.py +0 -0
  16. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/enum/library.py +0 -0
  17. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/enum/maidr_key.py +0 -0
  18. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/enum/smooth_keywords.py +0 -0
  19. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/maidr.py +0 -0
  20. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/__init__.py +0 -0
  21. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/barplot.py +0 -0
  22. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/boxplot.py +0 -0
  23. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/grouped_barplot.py +0 -0
  24. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/heatmap.py +0 -0
  25. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/histogram.py +0 -0
  26. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/lineplot.py +0 -0
  27. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/maidr_plot.py +0 -0
  28. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/regplot.py +0 -0
  29. {maidr-1.1.0 → maidr-1.2.1}/maidr/core/plot/scatterplot.py +0 -0
  30. {maidr-1.1.0 → maidr-1.2.1}/maidr/exception/__init__.py +0 -0
  31. {maidr-1.1.0 → maidr-1.2.1}/maidr/exception/extraction_error.py +0 -0
  32. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/__init__.py +0 -0
  33. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/barplot.py +0 -0
  34. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/clear.py +0 -0
  35. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/common.py +0 -0
  36. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/heatmap.py +0 -0
  37. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/highlight.py +0 -0
  38. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/histogram.py +0 -0
  39. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/kdeplot.py +0 -0
  40. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/lineplot.py +0 -0
  41. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/regplot.py +0 -0
  42. {maidr-1.1.0 → maidr-1.2.1}/maidr/patch/scatterplot.py +0 -0
  43. {maidr-1.1.0 → maidr-1.2.1}/maidr/util/__init__.py +0 -0
  44. {maidr-1.1.0 → maidr-1.2.1}/maidr/util/dedup_utils.py +0 -0
  45. {maidr-1.1.0 → maidr-1.2.1}/maidr/util/environment.py +0 -0
  46. {maidr-1.1.0 → maidr-1.2.1}/maidr/util/mixin/__init__.py +0 -0
  47. {maidr-1.1.0 → maidr-1.2.1}/maidr/util/mixin/extractor_mixin.py +0 -0
  48. {maidr-1.1.0 → maidr-1.2.1}/maidr/util/mixin/merger_mixin.py +0 -0
  49. {maidr-1.1.0 → maidr-1.2.1}/maidr/util/regression_line_utils.py +0 -0
  50. {maidr-1.1.0 → maidr-1.2.1}/maidr/util/svg_utils.py +0 -0
  51. {maidr-1.1.0 → maidr-1.2.1}/maidr/widget/__init__.py +0 -0
  52. {maidr-1.1.0 → maidr-1.2.1}/maidr/widget/shiny.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: maidr
3
- Version: 1.1.0
3
+ Version: 1.2.1
4
4
  Summary: Multimodal Access and Interactive Data Representations
5
5
  License: GPL-3.0-or-later
6
6
  Keywords: accessibility,visualization,sonification,braille,tactile,multimodal,data representation,blind,low vision,visual impairments
@@ -29,6 +29,7 @@ Requires-Dist: htmltools (>=0.5)
29
29
  Requires-Dist: jupyter (>=1.0.0,<2.0.0)
30
30
  Requires-Dist: lxml (>=5.1.0)
31
31
  Requires-Dist: matplotlib (>=3.8)
32
+ Requires-Dist: mplfinance (>=0.12.10b0,<0.13.0)
32
33
  Requires-Dist: numpy (>=1.26)
33
34
  Requires-Dist: seaborn (>=0.12)
34
35
  Requires-Dist: statsmodels (>=0.14.4,<0.15.0)
@@ -1,4 +1,4 @@
1
- __version__ = "1.1.0"
1
+ __version__ = "1.2.1"
2
2
 
3
3
  from .api import close, render, save_html, show, stacked
4
4
  from .core import Maidr
@@ -6,6 +6,7 @@ from .core.enum import PlotType
6
6
  from .patch import (
7
7
  barplot,
8
8
  boxplot,
9
+ candlestick,
9
10
  clear,
10
11
  heatmap,
11
12
  highlight,
@@ -14,3 +14,4 @@ class PlotType(str, Enum):
14
14
  SCATTER = "point"
15
15
  STACKED = "stacked_bar"
16
16
  SMOOTH = "smooth"
17
+ CANDLESTICK = "candlestick"
@@ -48,18 +48,24 @@ class FigureManager:
48
48
  return cls._instance
49
49
 
50
50
  @classmethod
51
- def create_maidr(cls, ax: Axes, plot_type: PlotType, **kwargs) -> Maidr:
51
+ def create_maidr(
52
+ cls, axes: Axes | list[Axes], plot_type: PlotType, **kwargs
53
+ ) -> Maidr:
52
54
  """Create a Maidr instance for the given Axes and plot type, and adds a plot to it."""
53
- if ax is None:
55
+ if axes is None:
54
56
  raise ValueError("No plot found.")
55
57
  if plot_type is None:
56
58
  raise ValueError("No plot type found.")
59
+ if isinstance(axes, list):
60
+ ax = axes[0]
61
+ else:
62
+ ax = axes
57
63
  if ax.get_figure() is None:
58
64
  raise ValueError(f"No figure found for axis: {ax}.")
59
65
 
60
66
  # Add plot to the Maidr object associated with the plot's figure.
61
67
  maidr = cls._get_maidr(ax.get_figure(), plot_type)
62
- plot = MaidrPlotFactory.create(ax, plot_type, **kwargs)
68
+ plot = MaidrPlotFactory.create(axes, plot_type, **kwargs)
63
69
  maidr.plots.append(plot)
64
70
  maidr.selector_ids.append(Maidr._unique_id())
65
71
  return maidr
@@ -0,0 +1,239 @@
1
+ import matplotlib.dates as mdates
2
+ import numpy as np
3
+ from matplotlib.axes import Axes
4
+ from matplotlib.collections import LineCollection, PatchCollection
5
+ from matplotlib.patches import Rectangle
6
+
7
+ from maidr.core.enum.plot_type import PlotType
8
+ from maidr.core.plot.maidr_plot import MaidrPlot
9
+
10
+
11
+ class CandlestickPlot(MaidrPlot):
12
+ def __init__(self, axes: list[Axes], **kwargs) -> None:
13
+ """
14
+ Initialize the CandlestickPlot.
15
+
16
+ Parameters
17
+ ----------
18
+ axes : list[Axes]
19
+ A list of Matplotlib Axes objects. Expected to contain at least
20
+ one Axes for OHLC data, and optionally a second for volume data.
21
+ **kwargs : dict
22
+ Additional keyword arguments.
23
+ """
24
+ self.axes = axes
25
+ # Ensure there's at least one axis for the superclass init
26
+ if not axes:
27
+ raise ValueError("Axes list cannot be empty.")
28
+ super().__init__(axes[0], PlotType.CANDLESTICK)
29
+
30
+ def _extract_plot_data(self) -> list[dict]:
31
+ """
32
+ Extracts candlestick (OHLC) and volume data from the plot axes.
33
+
34
+ This method assumes that the candlestick chart is structured with
35
+ LineCollection for wicks and PatchCollection of Rectangles for bodies
36
+ on the first axis (self.axes[0]). Volume data is expected as a
37
+ PatchCollection of Rectangles on the second axis (self.axes[1]), if present.
38
+ Open and close prices are inferred from the body rectangle's color.
39
+
40
+ Returns
41
+ -------
42
+ list[dict]
43
+ A list of dictionaries, where each dictionary represents a data point
44
+ with 'value' (date string YYYY-MM-DD), 'open', 'high', 'low',
45
+ 'close', and 'volume'. Fields that cannot be extracted will be None.
46
+
47
+ Examples
48
+ --------
49
+ Assuming a plot has been generated and `plot_instance.axes` is populated:
50
+ >>> data = plot_instance._extract_plot_data()
51
+ >>> print(data[0])
52
+ {
53
+ 'value': '2021-01-01',
54
+ 'open': 100.0,
55
+ 'high': 100.9,
56
+ 'low': 99.27,
57
+ 'close': 100.75,
58
+ 'volume': 171914,
59
+ }
60
+ """
61
+ if not self.axes:
62
+ return []
63
+
64
+ plot_data: list[dict] = []
65
+ ax_ohlc: Axes = self.axes[0]
66
+
67
+ body_rectangles: list[Rectangle] = []
68
+ wick_collection: LineCollection | None = None
69
+
70
+ # Find candlestick body Rectangles from the OHLC axis
71
+ # Prefer PatchCollection containing Rectangles, fallback to individual Rectangles in ax.patches
72
+ for collection in ax_ohlc.collections:
73
+ if isinstance(collection, PatchCollection):
74
+ # Check if the collection's patches are Rectangles
75
+ try:
76
+ # Iterating a PatchCollection yields its constituent Patch objects
77
+ patches_are_rects = all(
78
+ isinstance(p, Rectangle) for p in collection
79
+ )
80
+ if (
81
+ patches_are_rects and len(collection.get_paths()) > 0
82
+ ): # Ensure it has paths and they are Rectangles
83
+ for (
84
+ patch
85
+ ) in collection: # Iterate to get actual Rectangle objects
86
+ if isinstance(patch, Rectangle):
87
+ body_rectangles.append(patch)
88
+ if (
89
+ body_rectangles
90
+ ): # If we found rectangles this way, assume this is the primary body collection
91
+ break
92
+ except Exception:
93
+ # Could fail if collection is not iterable in the expected way or patches are not Rectangles
94
+ pass
95
+
96
+ if not body_rectangles:
97
+ for patch in ax_ohlc.patches:
98
+ if isinstance(patch, Rectangle):
99
+ body_rectangles.append(patch)
100
+
101
+ if not body_rectangles:
102
+ pass
103
+
104
+ ax_for_wicks: Axes | None = None
105
+ if len(self.axes) > 1:
106
+ ax_for_wicks = self.axes[1]
107
+
108
+ if ax_for_wicks:
109
+ # Attempt 1: Find wicks in ax_for_wicks.collections (as a LineCollection)
110
+ for collection in ax_for_wicks.collections:
111
+ if isinstance(collection, LineCollection):
112
+ segments = collection.get_segments()
113
+ # Check if the collection contains segments and the first segment looks like a vertical line
114
+ if segments is not None and len(segments) > 0:
115
+ first_segment = segments[0]
116
+ if (
117
+ len(first_segment) == 2 # Segment consists of two points
118
+ and len(first_segment[0]) == 2 # First point has (x, y)
119
+ and len(first_segment[1]) == 2 # Second point has (x, y)
120
+ and np.isclose(
121
+ first_segment[0][0], first_segment[1][0]
122
+ ) # X-coordinates are close (vertical)
123
+ ):
124
+ wick_collection = collection
125
+ break # Found a suitable LineCollection
126
+
127
+ # Attempt 2: If no LineCollection found, try to find wicks from individual Line2D objects in ax_for_wicks.get_lines()
128
+ if not wick_collection and hasattr(ax_for_wicks, "get_lines"):
129
+ potential_wick_segments = []
130
+ for line in ax_for_wicks.get_lines(): # Iterate over Line2D objects
131
+ x_data, y_data = line.get_data()
132
+ # A wick is typically a vertical line defined by two points.
133
+ if len(x_data) == 2 and len(y_data) == 2:
134
+ if np.isclose(x_data[0], x_data[1]): # Check for verticality
135
+ # Create a segment in the format [[x1, y1], [x2, y2]]
136
+ segment = [
137
+ [x_data[0], y_data[0]],
138
+ [x_data[1], y_data[1]],
139
+ ]
140
+ potential_wick_segments.append(segment)
141
+
142
+ if potential_wick_segments:
143
+ # If wick segments were found from individual lines,
144
+ # create a new LineCollection to hold them.
145
+ # This allows the downstream processing logic
146
+ # for wicks to remain consistent.
147
+ # Basic properties are set; color/linestyle
148
+ # are defaults and may not match
149
+ # the original plot's styling if that
150
+ # were relevant for segment extraction.
151
+ wick_collection = LineCollection(
152
+ potential_wick_segments,
153
+ colors="k", # Default color for the temporary collection
154
+ linestyles="solid", # Default linestyle
155
+ )
156
+
157
+ # Process wicks into a map: x_coordinate -> (low_price, high_price)
158
+ wick_segments_map: dict[float, tuple[float, float]] = {}
159
+ if wick_collection:
160
+ for seg in wick_collection.get_segments():
161
+ if len(seg) == 2 and len(seg[0]) == 2 and len(seg[1]) == 2:
162
+ # Ensure x-coordinates are (nearly) identical for a vertical wick line
163
+ if np.isclose(seg[0][0], seg[1][0]):
164
+ x_coord = seg[0][0] # Matplotlib date number
165
+ low_price = min(seg[0][1], seg[1][1])
166
+ high_price = max(seg[0][1], seg[1][1])
167
+ wick_segments_map[x_coord] = (low_price, high_price)
168
+
169
+ body_rectangles.sort(key=lambda r: r.get_x())
170
+
171
+ for rect in body_rectangles:
172
+ x_left = rect.get_x()
173
+ width = rect.get_width()
174
+ x_center_num = x_left + width / 2.0
175
+
176
+ try:
177
+ date_dt = mdates.num2date(x_center_num)
178
+ date_str = date_dt.strftime("%Y-%m-%d")
179
+ except ValueError:
180
+ date_str = f"raw_date_{x_center_num:.2f}"
181
+
182
+ y_bottom = rect.get_y()
183
+ height = rect.get_height()
184
+ face_color = rect.get_facecolor() # RGBA tuple
185
+
186
+ # Infer open and close prices
187
+ # Heuristic: Green component > Red component for an "up" candle (close > open)
188
+ # This assumes standard green for up, red for down.
189
+ # A more robust method would involve knowing the exact up/down colors used.
190
+ is_up_candle = (
191
+ face_color[1] > face_color[0]
192
+ ) # Compare Green and Red components
193
+
194
+ if is_up_candle: # Typically green: price went up
195
+ open_price = y_bottom
196
+ close_price = y_bottom + height
197
+ else: # Typically red: price went down (or other color)
198
+ close_price = y_bottom
199
+ open_price = y_bottom + height
200
+
201
+ matched_wick_data = None
202
+ closest_wick_x = None
203
+ min_diff = float("inf")
204
+
205
+ for wick_x, prices in wick_segments_map.items():
206
+ diff = abs(wick_x - x_center_num)
207
+ if diff < min_diff:
208
+ min_diff = diff
209
+ closest_wick_x = wick_x
210
+
211
+ # Tolerance for matching wick x-coordinate (e.g., 10% of candle width)
212
+ if closest_wick_x is not None and min_diff < (width * 0.1):
213
+ matched_wick_data = wick_segments_map[closest_wick_x]
214
+
215
+ if matched_wick_data:
216
+ low_price, high_price = matched_wick_data
217
+ # Ensure high >= max(open,close) and low <= min(open,close)
218
+ high_price = max(high_price, open_price, close_price)
219
+ low_price = min(low_price, open_price, close_price)
220
+ else:
221
+ # Fallback if no wick found: high is max(open,close), low is min(open,close)
222
+ high_price = max(open_price, close_price)
223
+ low_price = min(open_price, close_price)
224
+
225
+ plot_data.append(
226
+ {
227
+ "value": date_str,
228
+ "open": open_price,
229
+ "high": high_price,
230
+ "low": low_price,
231
+ "close": close_price,
232
+ "volume": 0,
233
+ }
234
+ )
235
+ self._elements.extend(body_rectangles)
236
+ return plot_data
237
+
238
+ def _extract_axes_data(self) -> dict:
239
+ return {}
@@ -5,6 +5,7 @@ from matplotlib.axes import Axes
5
5
  from maidr.core.enum import PlotType
6
6
  from maidr.core.plot.barplot import BarPlot
7
7
  from maidr.core.plot.boxplot import BoxPlot
8
+ from maidr.core.plot.candlestick import CandlestickPlot
8
9
  from maidr.core.plot.grouped_barplot import GroupedBarPlot
9
10
  from maidr.core.plot.heatmap import HeatPlot
10
11
  from maidr.core.plot.histogram import HistPlot
@@ -30,22 +31,37 @@ class MaidrPlotFactory:
30
31
  """
31
32
 
32
33
  @staticmethod
33
- def create(ax: Axes, plot_type: PlotType, **kwargs) -> MaidrPlot:
34
- if PlotType.BAR == plot_type or PlotType.COUNT == plot_type:
35
- return BarPlot(ax)
34
+ def create(ax: Axes | list[Axes], plot_type: PlotType, **kwargs) -> MaidrPlot:
35
+ if isinstance(ax, list):
36
+ single_ax = ax[0]
37
+ else:
38
+ single_ax = ax
39
+
40
+ if plot_type == PlotType.CANDLESTICK:
41
+ if isinstance(ax, list):
42
+ # If ax is a list of lists, flatten it
43
+ if ax and isinstance(ax[0], list):
44
+ axes = ax[0] # Take the first inner list
45
+ else:
46
+ axes = ax # Use the list as-is
47
+ else:
48
+ axes = [ax] # Wrap single axes in list
49
+ return CandlestickPlot(axes, **kwargs)
50
+ elif PlotType.BAR == plot_type or PlotType.COUNT == plot_type:
51
+ return BarPlot(single_ax)
36
52
  elif PlotType.BOX == plot_type:
37
- return BoxPlot(ax, **kwargs)
53
+ return BoxPlot(single_ax, **kwargs)
38
54
  elif PlotType.HEAT == plot_type:
39
- return HeatPlot(ax, **kwargs)
55
+ return HeatPlot(single_ax, **kwargs)
40
56
  elif PlotType.HIST == plot_type:
41
- return HistPlot(ax)
57
+ return HistPlot(single_ax)
42
58
  elif PlotType.LINE == plot_type:
43
- return MultiLinePlot(ax)
59
+ return MultiLinePlot(single_ax)
44
60
  elif PlotType.SCATTER == plot_type:
45
- return ScatterPlot(ax)
61
+ return ScatterPlot(single_ax)
46
62
  elif PlotType.DODGED == plot_type or PlotType.STACKED == plot_type:
47
- return GroupedBarPlot(ax, plot_type, **kwargs)
63
+ return GroupedBarPlot(single_ax, plot_type, **kwargs)
48
64
  elif PlotType.SMOOTH == plot_type:
49
- return SmoothPlot(ax, **kwargs)
65
+ return SmoothPlot(single_ax, **kwargs)
50
66
  else:
51
67
  raise TypeError(f"Unsupported plot type: {plot_type}.")
@@ -1,10 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import wrapt
4
-
5
4
  from matplotlib.axes import Axes
6
5
 
7
- from maidr.core.context_manager import ContextManager, BoxplotContextManager
6
+ from maidr.core.context_manager import BoxplotContextManager, ContextManager
8
7
  from maidr.core.enum import PlotType
9
8
  from maidr.core.figure_manager import FigureManager
10
9
 
@@ -0,0 +1,57 @@
1
+ import wrapt
2
+ from typing import Any, Callable, Dict, Tuple
3
+ from matplotlib.patches import Rectangle
4
+ from mplfinance import original_flavor
5
+
6
+ from maidr.core.context_manager import ContextManager
7
+ from maidr.core.enum.plot_type import PlotType
8
+ from maidr.core.figure_manager import FigureManager
9
+
10
+
11
+ def candlestick(
12
+ wrapped: Callable[..., list[Rectangle]],
13
+ instance: Any,
14
+ args: Tuple[Any, ...],
15
+ kwargs: Dict[str, Any],
16
+ ) -> list[Rectangle]:
17
+ """
18
+ Patch function for candlestick plots.
19
+
20
+ This function patches the candlestick plotting function to extract
21
+ candlestick data and create MAIDR plot instances for visualization.
22
+
23
+ Parameters
24
+ ----------
25
+ wrapped : Callable[..., list[Rectangle]]
26
+ The original candlestick function to be wrapped.
27
+ instance : Any
28
+ The instance of the class where the function is being patched.
29
+ args : Tuple[Any, ...]
30
+ Positional arguments passed to the original function.
31
+ kwargs : Dict[str, Any]
32
+ Keyword arguments passed to the original function.
33
+
34
+ Returns
35
+ -------
36
+ list[Rectangle]
37
+ The list of Rectangle objects returned by the original candlestick function.
38
+
39
+ Notes
40
+ -----
41
+ This wrapper function is used by the wrapt library to patch the mplfinance
42
+ candlestick function. It creates MAIDR plot instances for candlestick charts
43
+ while preserving the original functionality.
44
+ """
45
+ with ContextManager.set_internal_context():
46
+ # Patch the plotting function.
47
+ plot = wrapped(*args, **kwargs)
48
+
49
+ axes = []
50
+ for ax in plot:
51
+ axes.append(FigureManager.get_axes(ax))
52
+ FigureManager.create_maidr(axes, PlotType.CANDLESTICK)
53
+
54
+ return plot
55
+
56
+
57
+ wrapt.wrap_function_wrapper(original_flavor, "_candlestick", candlestick)
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "maidr"
7
- version = "1.1.0"
7
+ version = "1.2.1"
8
8
  description = "Multimodal Access and Interactive Data Representations"
9
9
  authors = [
10
10
  "JooYoung Seo <jseo1005@illinois.edu>",
@@ -43,6 +43,7 @@ htmltools = ">=0.5"
43
43
  jupyter = "^1.0.0"
44
44
  wrapt = "^1.16.0"
45
45
  virtualenv = "^20.26.6"
46
+ mplfinance = "^0.12.10b0"
46
47
  statsmodels = "^0.14.4"
47
48
 
48
49
  [tool.poetry.group.dev.dependencies]
@@ -72,7 +73,7 @@ match = "(main|master)"
72
73
  prerelease_token = "rc"
73
74
  prerelease = false
74
75
 
75
- [tool.semantic_release.changelog]
76
+ [tool.semantic_release.changelog.default_templates]
76
77
  template_dir = "templates"
77
78
  changelog_file = "CHANGELOG.md"
78
79
  exclude_commit_patterns = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes