maidr 1.3.0__py3-none-any.whl → 1.4.1__py3-none-any.whl
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.
- maidr/__init__.py +2 -1
- maidr/core/maidr.py +74 -7
- maidr/core/plot/candlestick.py +112 -204
- maidr/core/plot/maidr_plot.py +7 -2
- maidr/core/plot/maidr_plot_factory.py +12 -11
- maidr/core/plot/mplfinance_barplot.py +139 -0
- maidr/core/plot/mplfinance_lineplot.py +273 -0
- maidr/patch/__init__.py +15 -0
- maidr/patch/mplfinance.py +215 -0
- maidr/util/__init__.py +3 -0
- maidr/util/mplfinance_utils.py +409 -0
- maidr/util/plot_detection.py +136 -0
- {maidr-1.3.0.dist-info → maidr-1.4.1.dist-info}/METADATA +1 -1
- {maidr-1.3.0.dist-info → maidr-1.4.1.dist-info}/RECORD +16 -11
- {maidr-1.3.0.dist-info → maidr-1.4.1.dist-info}/LICENSE +0 -0
- {maidr-1.3.0.dist-info → maidr-1.4.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for handling mplfinance-specific data extraction and processing.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import matplotlib.dates as mdates
|
|
6
|
+
import numpy as np
|
|
7
|
+
from matplotlib.patches import Rectangle
|
|
8
|
+
from typing import List, Optional, Tuple, Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MplfinanceDataExtractor:
|
|
12
|
+
"""
|
|
13
|
+
Utility class for extracting and processing mplfinance-specific data.
|
|
14
|
+
|
|
15
|
+
This class handles the conversion of mplfinance plot elements (patches, collections)
|
|
16
|
+
into standardized data formats that can be used by the core plot classes.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def extract_volume_data(
|
|
21
|
+
volume_patches: List[Rectangle], date_nums: Optional[List[float]] = None
|
|
22
|
+
) -> List[dict]:
|
|
23
|
+
"""
|
|
24
|
+
Extract volume data from mplfinance Rectangle patches.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
volume_patches : List[Rectangle]
|
|
29
|
+
List of Rectangle patches representing volume bars
|
|
30
|
+
date_nums : Optional[List[float]], default=None
|
|
31
|
+
List of matplotlib date numbers corresponding to the patches
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
List[dict]
|
|
36
|
+
List of dictionaries with 'x' and 'y' keys for volume data
|
|
37
|
+
"""
|
|
38
|
+
if not volume_patches:
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
formatted_data = []
|
|
42
|
+
|
|
43
|
+
# Sort patches by x-coordinate to maintain order
|
|
44
|
+
sorted_patches = sorted(volume_patches, key=lambda p: p.get_x())
|
|
45
|
+
|
|
46
|
+
for i, patch in enumerate(sorted_patches):
|
|
47
|
+
height = patch.get_height()
|
|
48
|
+
|
|
49
|
+
# Use date mapping if available, otherwise use index
|
|
50
|
+
if date_nums is not None and i < len(date_nums):
|
|
51
|
+
date_num = date_nums[i]
|
|
52
|
+
x_label = MplfinanceDataExtractor._convert_date_num_to_string(date_num)
|
|
53
|
+
else:
|
|
54
|
+
x_label = f"date_{i:03d}"
|
|
55
|
+
|
|
56
|
+
formatted_data.append({"x": str(x_label), "y": float(height)})
|
|
57
|
+
|
|
58
|
+
return formatted_data
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def extract_candlestick_data(
|
|
62
|
+
body_collection: Any,
|
|
63
|
+
wick_collection: Any,
|
|
64
|
+
date_nums: Optional[List[float]] = None,
|
|
65
|
+
) -> List[dict]:
|
|
66
|
+
"""
|
|
67
|
+
Extract candlestick data from mplfinance collections.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
body_collection : Any
|
|
72
|
+
PolyCollection containing candlestick bodies
|
|
73
|
+
wick_collection : Any
|
|
74
|
+
LineCollection containing candlestick wicks
|
|
75
|
+
date_nums : Optional[List[float]], default=None
|
|
76
|
+
List of matplotlib date numbers corresponding to the candles
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
List[dict]
|
|
81
|
+
List of dictionaries with OHLC data
|
|
82
|
+
"""
|
|
83
|
+
if not body_collection or not hasattr(body_collection, "get_paths"):
|
|
84
|
+
return []
|
|
85
|
+
|
|
86
|
+
candles = []
|
|
87
|
+
paths = body_collection.get_paths()
|
|
88
|
+
face_colors = body_collection.get_facecolor()
|
|
89
|
+
|
|
90
|
+
for i, path in enumerate(paths):
|
|
91
|
+
if len(path.vertices) >= 4:
|
|
92
|
+
# Extract rectangle coordinates from the path
|
|
93
|
+
vertices = path.vertices
|
|
94
|
+
x_coords = vertices[:, 0]
|
|
95
|
+
y_coords = vertices[:, 1]
|
|
96
|
+
|
|
97
|
+
x_min, x_max = x_coords.min(), x_coords.max()
|
|
98
|
+
y_min, y_max = y_coords.min(), y_coords.max()
|
|
99
|
+
|
|
100
|
+
# Use date mapping if available
|
|
101
|
+
if date_nums is not None and i < len(date_nums):
|
|
102
|
+
date_num = date_nums[i]
|
|
103
|
+
date_str = MplfinanceDataExtractor._convert_date_num_to_string(
|
|
104
|
+
date_num
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
x_center = (x_min + x_max) / 2
|
|
108
|
+
date_str = MplfinanceDataExtractor._convert_date_num_to_string(
|
|
109
|
+
x_center
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Determine if this is an up or down candle based on color
|
|
113
|
+
is_up = MplfinanceDataExtractor._is_up_candle(face_colors, i)
|
|
114
|
+
|
|
115
|
+
# Extract OHLC values
|
|
116
|
+
(
|
|
117
|
+
open_val,
|
|
118
|
+
close_val,
|
|
119
|
+
) = MplfinanceDataExtractor._extract_ohlc_from_rectangle(
|
|
120
|
+
y_min, y_max, is_up
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Estimate high and low (these would normally come from wick data)
|
|
124
|
+
high_val = y_max + (y_max - y_min) * 0.1
|
|
125
|
+
low_val = y_min - (y_max - y_min) * 0.1
|
|
126
|
+
|
|
127
|
+
candle_data = {
|
|
128
|
+
"value": date_str,
|
|
129
|
+
"open": round(open_val, 2),
|
|
130
|
+
"high": round(high_val, 2),
|
|
131
|
+
"low": round(low_val, 2),
|
|
132
|
+
"close": round(close_val, 2),
|
|
133
|
+
"volume": 0, # Volume is handled separately
|
|
134
|
+
}
|
|
135
|
+
candles.append(candle_data)
|
|
136
|
+
|
|
137
|
+
return candles
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def extract_rectangle_candlestick_data(
|
|
141
|
+
body_rectangles: List[Rectangle], date_nums: Optional[List[float]] = None
|
|
142
|
+
) -> List[dict]:
|
|
143
|
+
"""
|
|
144
|
+
Extract candlestick data from Rectangle patches (original_flavor).
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
body_rectangles : List[Rectangle]
|
|
149
|
+
List of Rectangle patches representing candlestick bodies
|
|
150
|
+
date_nums : Optional[List[float]], default=None
|
|
151
|
+
List of matplotlib date numbers corresponding to the candles
|
|
152
|
+
|
|
153
|
+
Returns
|
|
154
|
+
-------
|
|
155
|
+
List[dict]
|
|
156
|
+
List of dictionaries with OHLC data
|
|
157
|
+
"""
|
|
158
|
+
if not body_rectangles:
|
|
159
|
+
return []
|
|
160
|
+
|
|
161
|
+
candles = []
|
|
162
|
+
|
|
163
|
+
# Sort rectangles by x-coordinate
|
|
164
|
+
body_rectangles.sort(key=lambda r: r.get_x())
|
|
165
|
+
|
|
166
|
+
for i, rect in enumerate(body_rectangles):
|
|
167
|
+
x_left = rect.get_x()
|
|
168
|
+
width = rect.get_width()
|
|
169
|
+
x_center_num = x_left + width / 2.0
|
|
170
|
+
|
|
171
|
+
# Convert x coordinate to date
|
|
172
|
+
if date_nums is not None and i < len(date_nums):
|
|
173
|
+
date_str = MplfinanceDataExtractor._convert_date_num_to_string(
|
|
174
|
+
date_nums[i]
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
date_str = MplfinanceDataExtractor._convert_date_num_to_string(
|
|
178
|
+
x_center_num
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
y_bottom = rect.get_y()
|
|
182
|
+
height = rect.get_height()
|
|
183
|
+
face_color = rect.get_facecolor()
|
|
184
|
+
|
|
185
|
+
# Determine if this is an up or down candle based on color
|
|
186
|
+
is_up_candle = MplfinanceDataExtractor._is_up_candle_from_color(face_color)
|
|
187
|
+
|
|
188
|
+
# Extract OHLC values from rectangle
|
|
189
|
+
(
|
|
190
|
+
open_price,
|
|
191
|
+
close_price,
|
|
192
|
+
) = MplfinanceDataExtractor._extract_ohlc_from_rectangle(
|
|
193
|
+
y_bottom, y_bottom + height, is_up_candle
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Estimate high and low
|
|
197
|
+
high_price = max(open_price, close_price) + height * 0.1
|
|
198
|
+
low_price = min(open_price, close_price) - height * 0.1
|
|
199
|
+
|
|
200
|
+
# Ensure all values are valid numbers
|
|
201
|
+
open_price = float(open_price) if not np.isnan(open_price) else 0.0
|
|
202
|
+
high_price = float(high_price) if not np.isnan(high_price) else 0.0
|
|
203
|
+
low_price = float(low_price) if not np.isnan(low_price) else 0.0
|
|
204
|
+
close_price = float(close_price) if not np.isnan(close_price) else 0.0
|
|
205
|
+
|
|
206
|
+
candle_data = {
|
|
207
|
+
"value": date_str,
|
|
208
|
+
"open": round(open_price, 2),
|
|
209
|
+
"high": round(high_price, 2),
|
|
210
|
+
"low": round(low_price, 2),
|
|
211
|
+
"close": round(close_price, 2),
|
|
212
|
+
"volume": 0,
|
|
213
|
+
}
|
|
214
|
+
candles.append(candle_data)
|
|
215
|
+
|
|
216
|
+
return candles
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def clean_axis_label(label: str) -> str:
|
|
220
|
+
"""
|
|
221
|
+
Clean up axis labels by removing LaTeX formatting.
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
label : str
|
|
226
|
+
The original axis label
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
str
|
|
231
|
+
Cleaned axis label
|
|
232
|
+
"""
|
|
233
|
+
if not label or not isinstance(label, str):
|
|
234
|
+
return label
|
|
235
|
+
|
|
236
|
+
import re
|
|
237
|
+
|
|
238
|
+
# Removes LaTeX-like scientific notation, e.g., "$10^{6}$"
|
|
239
|
+
cleaned_label = re.sub(r"\s*\$.*?\$", "", label).strip()
|
|
240
|
+
return cleaned_label if cleaned_label else label
|
|
241
|
+
|
|
242
|
+
@staticmethod
|
|
243
|
+
def _convert_date_num_to_string(date_num: float) -> str:
|
|
244
|
+
"""
|
|
245
|
+
Convert matplotlib date number to date string.
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
date_num : float
|
|
250
|
+
Matplotlib date number
|
|
251
|
+
|
|
252
|
+
Returns
|
|
253
|
+
-------
|
|
254
|
+
str
|
|
255
|
+
Date string in YYYY-MM-DD format or fallback index
|
|
256
|
+
"""
|
|
257
|
+
try:
|
|
258
|
+
# Check if this looks like a matplotlib date number (typically > 700000)
|
|
259
|
+
if date_num > 700000:
|
|
260
|
+
date_dt = mdates.num2date(date_num)
|
|
261
|
+
if hasattr(date_dt, "replace"):
|
|
262
|
+
date_dt = date_dt.replace(tzinfo=None)
|
|
263
|
+
return date_dt.strftime("%Y-%m-%d")
|
|
264
|
+
elif date_num > 1000:
|
|
265
|
+
# Try converting as if it's a pandas timestamp
|
|
266
|
+
try:
|
|
267
|
+
import pandas as pd
|
|
268
|
+
|
|
269
|
+
date_dt = pd.to_datetime(date_num, unit="D")
|
|
270
|
+
return date_dt.strftime("%Y-%m-%d")
|
|
271
|
+
except:
|
|
272
|
+
pass
|
|
273
|
+
except (ValueError, TypeError, OverflowError):
|
|
274
|
+
pass
|
|
275
|
+
|
|
276
|
+
# Fallback to index-based date string
|
|
277
|
+
return f"date_{int(date_num):03d}"
|
|
278
|
+
|
|
279
|
+
@staticmethod
|
|
280
|
+
def convert_x_to_date(x_center_num: float, axes: Optional[List] = None) -> str:
|
|
281
|
+
"""
|
|
282
|
+
Convert matplotlib x-coordinate to date string.
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
x_center_num : float
|
|
287
|
+
X-coordinate value to convert
|
|
288
|
+
axes : Optional[List], optional
|
|
289
|
+
List of matplotlib axes to help with date conversion
|
|
290
|
+
|
|
291
|
+
Returns
|
|
292
|
+
-------
|
|
293
|
+
str
|
|
294
|
+
Date string in YYYY-MM-DD format or fallback
|
|
295
|
+
"""
|
|
296
|
+
# First, try to get the actual dates from the axes x-axis data
|
|
297
|
+
if axes and len(axes) > 0:
|
|
298
|
+
ax = axes[0]
|
|
299
|
+
try:
|
|
300
|
+
# Get x-axis ticks which might contain the actual dates
|
|
301
|
+
x_ticks = ax.get_xticks()
|
|
302
|
+
|
|
303
|
+
# If we have x-axis ticks and they look like dates (large numbers), use them
|
|
304
|
+
if len(x_ticks) > 0 and x_ticks[0] > 1000:
|
|
305
|
+
# Find the closest tick to our x_center_num
|
|
306
|
+
tick_index = int(round(x_center_num))
|
|
307
|
+
if 0 <= tick_index < len(x_ticks):
|
|
308
|
+
actual_date_num = x_ticks[tick_index]
|
|
309
|
+
|
|
310
|
+
# Convert the actual date number
|
|
311
|
+
if actual_date_num > 700000:
|
|
312
|
+
date_dt = mdates.num2date(actual_date_num)
|
|
313
|
+
if hasattr(date_dt, "replace"):
|
|
314
|
+
date_dt = date_dt.replace(tzinfo=None)
|
|
315
|
+
date_str = date_dt.strftime("%Y-%m-%d")
|
|
316
|
+
return date_str
|
|
317
|
+
except Exception:
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
# Use the utility class for date conversion
|
|
321
|
+
return MplfinanceDataExtractor._convert_date_num_to_string(x_center_num)
|
|
322
|
+
|
|
323
|
+
@staticmethod
|
|
324
|
+
def _is_up_candle(face_colors: Any, index: int) -> bool:
|
|
325
|
+
"""
|
|
326
|
+
Determine if a candle is up based on face color.
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
face_colors : Any
|
|
331
|
+
Face colors from the collection
|
|
332
|
+
index : int
|
|
333
|
+
Index of the candle
|
|
334
|
+
|
|
335
|
+
Returns
|
|
336
|
+
-------
|
|
337
|
+
bool
|
|
338
|
+
True if up candle, False if down candle
|
|
339
|
+
"""
|
|
340
|
+
is_up = True # Default to up candle
|
|
341
|
+
if hasattr(face_colors, "__len__") and len(face_colors) > index:
|
|
342
|
+
color = (
|
|
343
|
+
face_colors[index]
|
|
344
|
+
if hasattr(face_colors[index], "__len__")
|
|
345
|
+
else face_colors
|
|
346
|
+
)
|
|
347
|
+
if isinstance(color, (list, tuple, np.ndarray)):
|
|
348
|
+
if len(color) >= 3:
|
|
349
|
+
# Dark colors typically indicate down candles
|
|
350
|
+
if color[0] < 0.5 and color[1] < 0.5 and color[2] < 0.5:
|
|
351
|
+
is_up = False
|
|
352
|
+
return is_up
|
|
353
|
+
|
|
354
|
+
@staticmethod
|
|
355
|
+
def _is_up_candle_from_color(face_color: Any) -> bool:
|
|
356
|
+
"""
|
|
357
|
+
Determine if a candle is up based on face color (for Rectangle patches).
|
|
358
|
+
|
|
359
|
+
Parameters
|
|
360
|
+
----------
|
|
361
|
+
face_color : Any
|
|
362
|
+
Face color of the rectangle
|
|
363
|
+
|
|
364
|
+
Returns
|
|
365
|
+
-------
|
|
366
|
+
bool
|
|
367
|
+
True if up candle, False if down candle
|
|
368
|
+
"""
|
|
369
|
+
try:
|
|
370
|
+
if (
|
|
371
|
+
isinstance(face_color, (list, tuple, np.ndarray))
|
|
372
|
+
and len(face_color) >= 3
|
|
373
|
+
):
|
|
374
|
+
# Green colors typically indicate up candles
|
|
375
|
+
if face_color[1] > face_color[0]:
|
|
376
|
+
return True
|
|
377
|
+
else:
|
|
378
|
+
return False
|
|
379
|
+
except (TypeError, IndexError):
|
|
380
|
+
pass
|
|
381
|
+
return True # Default to up candle
|
|
382
|
+
|
|
383
|
+
@staticmethod
|
|
384
|
+
def _extract_ohlc_from_rectangle(
|
|
385
|
+
y_min: float, y_max: float, is_up: bool
|
|
386
|
+
) -> Tuple[float, float]:
|
|
387
|
+
"""
|
|
388
|
+
Extract open and close values from rectangle bounds.
|
|
389
|
+
|
|
390
|
+
Parameters
|
|
391
|
+
----------
|
|
392
|
+
y_min : float
|
|
393
|
+
Minimum y value of rectangle
|
|
394
|
+
y_max : float
|
|
395
|
+
Maximum y value of rectangle
|
|
396
|
+
is_up : bool
|
|
397
|
+
Whether this is an up candle
|
|
398
|
+
|
|
399
|
+
Returns
|
|
400
|
+
-------
|
|
401
|
+
Tuple[float, float]
|
|
402
|
+
(open_price, close_price)
|
|
403
|
+
"""
|
|
404
|
+
if is_up:
|
|
405
|
+
# Up candle: open at bottom, close at top
|
|
406
|
+
return y_min, y_max
|
|
407
|
+
else:
|
|
408
|
+
# Down candle: open at top, close at bottom
|
|
409
|
+
return y_max, y_min
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for detecting plot types and determining appropriate plot classes.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from matplotlib.axes import Axes
|
|
6
|
+
from matplotlib.lines import Line2D
|
|
7
|
+
from typing import Dict, Any, List, Union
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PlotDetectionUtils:
|
|
11
|
+
"""
|
|
12
|
+
Utility class for detecting plot characteristics and determining appropriate plot classes.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def is_mplfinance_bar_plot(**kwargs) -> bool:
|
|
17
|
+
"""
|
|
18
|
+
Detect if this is an mplfinance bar plot based on kwargs.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
**kwargs : dict
|
|
23
|
+
Keyword arguments passed to the plot factory
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
bool
|
|
28
|
+
True if this is an mplfinance bar plot
|
|
29
|
+
"""
|
|
30
|
+
return "_maidr_patches" in kwargs or "_maidr_date_nums" in kwargs
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def is_mplfinance_line_plot(ax: Axes, **kwargs) -> bool:
|
|
34
|
+
"""
|
|
35
|
+
Detect if this is an mplfinance line plot based on kwargs or Line2D attributes.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
ax : Axes
|
|
40
|
+
The matplotlib axes object
|
|
41
|
+
**kwargs : dict
|
|
42
|
+
Keyword arguments passed to the plot factory
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
bool
|
|
47
|
+
True if this is an mplfinance line plot
|
|
48
|
+
"""
|
|
49
|
+
# Check kwargs first (more efficient)
|
|
50
|
+
if "_maidr_date_nums" in kwargs:
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
# Check Line2D objects for mplfinance attributes
|
|
54
|
+
try:
|
|
55
|
+
for line in ax.get_lines():
|
|
56
|
+
if isinstance(line, Line2D) and hasattr(line, "_maidr_date_nums"):
|
|
57
|
+
return True
|
|
58
|
+
except (AttributeError, TypeError):
|
|
59
|
+
# Handle cases where ax doesn't have get_lines() method (e.g., Mock objects)
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def is_mplfinance_candlestick_plot(**kwargs) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Detect if this is an mplfinance candlestick plot based on kwargs.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
**kwargs : dict
|
|
72
|
+
Keyword arguments passed to the plot factory
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
bool
|
|
77
|
+
True if this is an mplfinance candlestick plot
|
|
78
|
+
"""
|
|
79
|
+
return any(key.startswith("_maidr_") for key in kwargs.keys())
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def should_use_candlestick_plot(
|
|
83
|
+
ax: Union[Axes, List[Axes], List[List[Axes]]], **kwargs
|
|
84
|
+
) -> bool:
|
|
85
|
+
"""
|
|
86
|
+
Determine if a candlestick plot should be used based on axes structure.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
ax : Union[Axes, List[Axes], List[List[Axes]]]
|
|
91
|
+
The matplotlib axes object(s)
|
|
92
|
+
**kwargs : dict
|
|
93
|
+
Keyword arguments passed to the plot factory
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
bool
|
|
98
|
+
True if candlestick plot should be used
|
|
99
|
+
"""
|
|
100
|
+
# Check if we have mplfinance-specific kwargs
|
|
101
|
+
if PlotDetectionUtils.is_mplfinance_candlestick_plot(**kwargs):
|
|
102
|
+
return True
|
|
103
|
+
|
|
104
|
+
# Check if we have a list of axes (typical for candlestick plots)
|
|
105
|
+
if isinstance(ax, list):
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def get_candlestick_axes(
|
|
112
|
+
ax: Union[Axes, List[Axes], List[List[Axes]]]
|
|
113
|
+
) -> List[Axes]:
|
|
114
|
+
"""
|
|
115
|
+
Extract and normalize axes for candlestick plots.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
ax : Union[Axes, List[Axes], List[List[Axes]]]
|
|
120
|
+
The matplotlib axes object(s)
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
List[Axes]
|
|
125
|
+
Normalized list of axes for candlestick plotting
|
|
126
|
+
"""
|
|
127
|
+
if isinstance(ax, list):
|
|
128
|
+
if ax and isinstance(ax[0], list):
|
|
129
|
+
# Handle nested list case: [[ax1, ax2]]
|
|
130
|
+
return ax[0]
|
|
131
|
+
else:
|
|
132
|
+
# Handle simple list case: [ax1, ax2]
|
|
133
|
+
return ax
|
|
134
|
+
else:
|
|
135
|
+
# Handle single axis case
|
|
136
|
+
return [ax]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: maidr
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
maidr/__init__.py,sha256=
|
|
1
|
+
maidr/__init__.py,sha256=rjIWNGJRb5ksabBvZv4Ia2vq3YIhfCUr2sytbDXnV4U,415
|
|
2
2
|
maidr/api.py,sha256=F43mXWsxc7tHdlZqbRlEWkc-RjJVo_zgxCn3NiLBY58,1764
|
|
3
3
|
maidr/core/__init__.py,sha256=WgxLpSEYMc4k3OyEOf1shOxfEq0ASzppEIZYmE91ThQ,25
|
|
4
4
|
maidr/core/context_manager.py,sha256=6cT7ZGOApSpC-SLD2XZWWU_H08i-nfv-JUlzXOtvWYw,3374
|
|
@@ -8,22 +8,24 @@ maidr/core/enum/maidr_key.py,sha256=ljG0omqzd8K8Yk213N7i7PXGvG-IOlnE5v7o6RoGJzc,
|
|
|
8
8
|
maidr/core/enum/plot_type.py,sha256=7Orx3b_0NdpI_PtVJfLyJPh4qBqYMTsYBBr8VwOtiAM,347
|
|
9
9
|
maidr/core/enum/smooth_keywords.py,sha256=VlpIX1BaoX8efwIrT72GIptxguTpiPtJvvJUPMoaFSQ,194
|
|
10
10
|
maidr/core/figure_manager.py,sha256=jXs-Prkeru1Pahj21hjh8BAwXM9ZFUZ3GFfKUfIRX_M,4117
|
|
11
|
-
maidr/core/maidr.py,sha256=
|
|
11
|
+
maidr/core/maidr.py,sha256=dxoJsvFyqhyv7PFHsYZrGAVnNmyjW0v0eng45cxhrf4,16325
|
|
12
12
|
maidr/core/plot/__init__.py,sha256=xDIpRGM-4DfaSSL3nKcXrjdMecCHJ6en4K4nA_fPefQ,83
|
|
13
13
|
maidr/core/plot/barplot.py,sha256=1HfoqyDGKIXkYQnCHN83Ye_faKpNQ3R4wjlbjD5jUyk,2092
|
|
14
14
|
maidr/core/plot/boxplot.py,sha256=i11GdNuz_c-hilmhydu3ah-bzyVdFoBkNvRi5lpMrrY,9946
|
|
15
|
-
maidr/core/plot/candlestick.py,sha256=
|
|
15
|
+
maidr/core/plot/candlestick.py,sha256=2eLdvZJSIgv9fW918uTn7ROsEGui7H3YaVGGFtqkBIw,5269
|
|
16
16
|
maidr/core/plot/grouped_barplot.py,sha256=bRcQcvwkF3Q3aZ3PlhbZ6bHI_AfcqdKUMVvlLL94wXM,2078
|
|
17
17
|
maidr/core/plot/heatmap.py,sha256=yMS-31tS2GW4peds9LtZesMxmmTV_YfqYO5M_t5KasQ,2521
|
|
18
18
|
maidr/core/plot/histogram.py,sha256=QV5W-6ZJQQcZsrM91JJBX-ONktJzH7yg_et5_bBPfQQ,1525
|
|
19
19
|
maidr/core/plot/lineplot.py,sha256=C3xz6uWXYM_mbTq_geb5bP0JdvhcQf6cpfTs78Y6fCM,3852
|
|
20
|
-
maidr/core/plot/maidr_plot.py,sha256=
|
|
21
|
-
maidr/core/plot/maidr_plot_factory.py,sha256=
|
|
20
|
+
maidr/core/plot/maidr_plot.py,sha256=DN4TTRNt_SCqGa_mbkHrnpCk-eUQm71HoFRqs3bB6xk,3868
|
|
21
|
+
maidr/core/plot/maidr_plot_factory.py,sha256=NW2iFScswgXbAC9rAOo4iMkAFsjY43DAvFioGr0yzx4,2732
|
|
22
|
+
maidr/core/plot/mplfinance_barplot.py,sha256=feQlVMGA987EvgFii3V5vH74xGtBMBZhNlulU97vQL8,5184
|
|
23
|
+
maidr/core/plot/mplfinance_lineplot.py,sha256=SVN81tau_IAsj17NZT-UtUEJ8NKBy-WxD1PznZELKHc,9338
|
|
22
24
|
maidr/core/plot/regplot.py,sha256=b7u6bGTz1IxKahplNUrfwIr_OGSwMJ2BuLgFAVjL0s0,2744
|
|
23
25
|
maidr/core/plot/scatterplot.py,sha256=o0i0uS-wXK9ZrENxneoHbh3-u-2goRONp19Yu9QLsaY,1257
|
|
24
26
|
maidr/exception/__init__.py,sha256=PzaXoYBhyZxMDcJkuxJugDx7jZeseI0El6LpxIwXyG4,46
|
|
25
27
|
maidr/exception/extraction_error.py,sha256=rd37Oxa9gn2OWFWt9AOH5fv0hNd3sAWGvpDMFBuJY2I,607
|
|
26
|
-
maidr/patch/__init__.py,sha256=
|
|
28
|
+
maidr/patch/__init__.py,sha256=FnkoUQJC2ODhLO37GwgRVSitBCRax42Ti0e4NIAgdO0,236
|
|
27
29
|
maidr/patch/barplot.py,sha256=nxgBWBMUyf3eAW56CN5EKYYy3vsTgEPRlcvNYS3-WiU,2479
|
|
28
30
|
maidr/patch/boxplot.py,sha256=l7wDD4pDi4ZbsL5EX5XDhPRxgtSIFSrFguMOZ7IC2eg,2845
|
|
29
31
|
maidr/patch/candlestick.py,sha256=NFkzwpxmLBpWmb5s05pjk6obNMQee-xIEZTqGkbhhqM,1776
|
|
@@ -34,19 +36,22 @@ maidr/patch/highlight.py,sha256=I1dGFHJAnVd0AHVnMJzk_TE8BC8Uv-I6fTzSrJLU5QM,1155
|
|
|
34
36
|
maidr/patch/histogram.py,sha256=k3N0RUf1SQ2402pwbaY5QyS98KnLWvr9glCHQw9NTko,2378
|
|
35
37
|
maidr/patch/kdeplot.py,sha256=qv-OKzuop2aTrkZgUe2OnLxvV-KMyeXt1Td0_uZeHzE,2338
|
|
36
38
|
maidr/patch/lineplot.py,sha256=og42V0tWBKCnf6idT3pLsIj3QBvKjg8aUN-k1udPRVw,1901
|
|
39
|
+
maidr/patch/mplfinance.py,sha256=oxRhtKWnC-Fr9EBNS1RF-nKJKCKOPbtg4mseLsP1kMI,7967
|
|
37
40
|
maidr/patch/regplot.py,sha256=Ciz43C5XZfWK6wtVWrlV0WNz4R__rcgdqVE9OCaXXRk,3236
|
|
38
41
|
maidr/patch/scatterplot.py,sha256=kln6zZwjVsdQzICalo-RnBOJrx1BnIB2xYUwItHvSNY,525
|
|
39
|
-
maidr/util/__init__.py,sha256=
|
|
42
|
+
maidr/util/__init__.py,sha256=eRJZfRpDX-n7UoV3JXw_9Lbfu_qNl_D0W1UTvLL-Iv4,81
|
|
40
43
|
maidr/util/dedup_utils.py,sha256=RpgPL5p-3oULUHaTCZJaQKhPHfyPkvBLHMt8lAGpJ5A,438
|
|
41
44
|
maidr/util/environment.py,sha256=-2LyZUpHojBCMEbkr_xkcC-_IDqtGDALB8683v7kTdI,5253
|
|
42
45
|
maidr/util/mixin/__init__.py,sha256=aGJZNhtWh77yIVPc7ipIZm1OajigjMtCWYKPuDWTC-c,217
|
|
43
46
|
maidr/util/mixin/extractor_mixin.py,sha256=oHtwpmS5kARvaLrSO3DKTPQxyFUw9nOcKN7rzTj1q4g,5192
|
|
44
47
|
maidr/util/mixin/merger_mixin.py,sha256=V0qLw_6DUB7X6CQ3BCMpsCQX_ZuwAhoSTm_E4xAJFKM,712
|
|
48
|
+
maidr/util/mplfinance_utils.py,sha256=AvDzvAAQbMsPZxcxWr3DwctZMp4xW5CkQEBezWTk1mY,13704
|
|
49
|
+
maidr/util/plot_detection.py,sha256=bVSocZMGCLP6CduRSpOmwIzLFWEBnNdYN9T1ULfVPMw,3893
|
|
45
50
|
maidr/util/regression_line_utils.py,sha256=P8RQLixTby2JLz73XZgNiu96C2Ct3pNe4ENRWOjgT8M,509
|
|
46
51
|
maidr/util/svg_utils.py,sha256=2gyzBtNKFHs0utrw1iOlxTmznzivOWQMV2aW8zu2c8E,1442
|
|
47
52
|
maidr/widget/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
53
|
maidr/widget/shiny.py,sha256=wrrw2KAIpE_A6CNQGBtNHauR1DjenA_n47qlFXX9_rk,745
|
|
49
|
-
maidr-1.
|
|
50
|
-
maidr-1.
|
|
51
|
-
maidr-1.
|
|
52
|
-
maidr-1.
|
|
54
|
+
maidr-1.4.1.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
55
|
+
maidr-1.4.1.dist-info/METADATA,sha256=rGSOkUIcGPVk2z2SgpfMy1EP_B7m0HgpStq3h1SKdK4,2664
|
|
56
|
+
maidr-1.4.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
57
|
+
maidr-1.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|