maidr 1.9.0__py3-none-any.whl → 1.10.0__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 CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.9.0"
1
+ __version__ = "1.10.0"
2
2
 
3
3
  from .api import close, render, save_html, show, stacked
4
4
  from .core import Maidr
@@ -1,24 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
- import uuid
4
3
  from typing import Union, Dict
5
4
  from matplotlib.axes import Axes
6
- from matplotlib.patches import Rectangle
7
- import numpy as np
5
+ import pandas as pd
8
6
 
9
7
  from maidr.core.enum import PlotType
10
8
  from maidr.core.plot import MaidrPlot
11
9
  from maidr.core.enum.maidr_key import MaidrKey
12
10
  from maidr.exception import ExtractionError
13
- from maidr.util.mplfinance_utils import MplfinanceDataExtractor
14
11
 
15
12
 
16
13
  class CandlestickPlot(MaidrPlot):
17
14
  """
18
15
  Specialized candlestick plot class for mplfinance OHLC data.
19
16
 
20
- This class handles the extraction and processing of candlestick data from mplfinance
21
- plots, including proper date conversion and data validation.
17
+ This class extracts candlestick data directly from the original DataFrame
18
+ without any formatting or transformation.
22
19
  """
23
20
 
24
21
  def __init__(self, axes: list[Axes], **kwargs) -> None:
@@ -34,23 +31,17 @@ class CandlestickPlot(MaidrPlot):
34
31
  Additional keyword arguments.
35
32
  """
36
33
  self.axes = axes
37
- # Ensure there's at least one axis for the superclass init
38
34
  if not axes:
39
35
  raise ValueError("Axes list cannot be empty.")
40
36
  super().__init__(axes[0], PlotType.CANDLESTICK)
41
37
 
42
- # Store custom collections passed from mplfinance patch
38
+ # Store collections passed from mplfinance patch
43
39
  self._maidr_wick_collection = kwargs.get("_maidr_wick_collection", None)
44
40
  self._maidr_body_collection = kwargs.get("_maidr_body_collection", None)
45
- self._maidr_date_nums = kwargs.get("_maidr_date_nums", None)
46
- self._maidr_original_data = kwargs.get(
47
- "_maidr_original_data", None
48
- ) # Store original data
49
- self._maidr_datetime_converter = kwargs.get("_maidr_datetime_converter", None)
41
+ self._maidr_original_data = kwargs.get("_maidr_original_data", None)
50
42
 
51
- # Store the GID for proper selector generation (legacy/shared)
43
+ # Store the GID for selector generation
52
44
  self._maidr_gid = None
53
- # Modern-path separate gids for body and wick
54
45
  self._maidr_body_gid = None
55
46
  self._maidr_wick_gid = None
56
47
  if self._maidr_body_collection:
@@ -62,105 +53,83 @@ class CandlestickPlot(MaidrPlot):
62
53
 
63
54
  def _extract_plot_data(self) -> list[dict]:
64
55
  """
65
- Extract candlestick data from the plot.
66
-
67
- This method processes candlestick plots from both modern (mplfinance.plot) and
68
- legacy (original_flavor) pipelines, extracting OHLC data and setting up
69
- highlighting elements and GIDs.
56
+ Extract candlestick data directly from the original DataFrame.
70
57
 
71
58
  Returns
72
59
  -------
73
60
  list[dict]
74
61
  List of dictionaries containing candlestick data with keys:
75
- - 'value': Date string
62
+ - 'value': Date string (raw from DataFrame index)
76
63
  - 'open': Opening price (float)
77
64
  - 'high': High price (float)
78
65
  - 'low': Low price (float)
79
66
  - 'close': Closing price (float)
80
- - 'volume': Volume (float, typically 0 for candlestick-only plots)
67
+ - 'volume': Volume (float)
81
68
  """
82
-
83
- # Get the custom collections from kwargs
84
69
  body_collection = self._maidr_body_collection
85
70
  wick_collection = self._maidr_wick_collection
86
71
 
87
72
  if body_collection and wick_collection:
88
- # Store the GIDs from the collections (modern path)
73
+ # Store the GIDs from the collections
89
74
  self._maidr_body_gid = body_collection.get_gid()
90
75
  self._maidr_wick_gid = wick_collection.get_gid()
91
- # Keep legacy gid filled for backward compatibility
92
76
  self._maidr_gid = self._maidr_body_gid or self._maidr_wick_gid
93
77
 
94
78
  # Use the original collections for highlighting
95
79
  self._elements = [body_collection, wick_collection]
96
80
 
97
- # Use datetime converter for enhanced data extraction
98
- if self._maidr_datetime_converter is not None:
99
- data = self._maidr_datetime_converter.extract_candlestick_data(
100
- self.axes[0], wick_collection, body_collection
101
- )
102
- return data
103
-
104
- # Fallback to original detection method
105
- if not self.axes:
106
- return []
107
-
108
- ax_ohlc = self.axes[0]
109
-
110
- # Look for Rectangle patches (original_flavor candlestick)
111
- body_rectangles = []
112
- for patch in ax_ohlc.patches:
113
- if isinstance(patch, Rectangle):
114
- body_rectangles.append(patch)
115
-
116
- if body_rectangles:
117
- # Set elements for highlighting
118
- self._elements = body_rectangles
119
-
120
- # Generate a GID for highlighting if none exists
121
- if not self._maidr_gid:
122
- self._maidr_gid = f"maidr-{uuid.uuid4()}"
123
- # Set GID on all rectangles
124
- for rect in body_rectangles:
125
- rect.set_gid(self._maidr_gid)
126
- # Keep a dedicated body gid for legacy dict selectors
127
- self._maidr_body_gid = (
128
- getattr(self, "_maidr_body_gid", None) or self._maidr_gid
129
- )
130
-
131
- # Assign a shared gid to wick Line2D (vertical 2-point lines) on the same axis
132
- wick_lines = []
133
- for line in ax_ohlc.get_lines():
134
- try:
135
- xydata = line.get_xydata()
136
- if xydata is None:
137
- continue
138
- xy_arr = np.asarray(xydata)
139
- if (
140
- xy_arr.ndim == 2
141
- and xy_arr.shape[0] == 2
142
- and xy_arr.shape[1] >= 2
143
- ):
144
- x0 = float(xy_arr[0, 0])
145
- x1 = float(xy_arr[1, 0])
146
- if abs(x0 - x1) < 1e-10:
147
- wick_lines.append(line)
148
- except Exception:
149
- continue
150
- if wick_lines:
151
- if not getattr(self, "_maidr_wick_gid", None):
152
- self._maidr_wick_gid = f"maidr-{uuid.uuid4()}"
153
- for line in wick_lines:
154
- line.set_gid(self._maidr_wick_gid)
155
-
156
- # Use the utility class to extract data
157
- data = MplfinanceDataExtractor.extract_rectangle_candlestick_data(
158
- body_rectangles, self._maidr_date_nums, self._maidr_original_data
159
- )
160
- return data
81
+ # Extract data directly from DataFrame
82
+ if self._maidr_original_data is not None and isinstance(
83
+ self._maidr_original_data, pd.DataFrame
84
+ ):
85
+ return self._extract_from_dataframe(self._maidr_original_data)
161
86
 
162
87
  return []
163
88
 
89
+ def _extract_from_dataframe(self, df: pd.DataFrame) -> list[dict]:
90
+ """
91
+ Extract candlestick data directly from DataFrame without any formatting.
92
+
93
+ Parameters
94
+ ----------
95
+ df : pd.DataFrame
96
+ DataFrame with OHLC data and DatetimeIndex.
97
+
98
+ Returns
99
+ -------
100
+ list[dict]
101
+ List of candlestick data dictionaries with raw values.
102
+ """
103
+ candles = []
104
+
105
+ for i in range(len(df)):
106
+ try:
107
+ # Get date directly from index - raw representation
108
+ date_value = str(df.index[i])
109
+
110
+ # Get OHLC values directly from DataFrame columns
111
+ open_price = float(df.iloc[i]["Open"])
112
+ high_price = float(df.iloc[i]["High"])
113
+ low_price = float(df.iloc[i]["Low"])
114
+ close_price = float(df.iloc[i]["Close"])
115
+
116
+ # Get volume if available, otherwise 0
117
+ volume = float(df.iloc[i].get("Volume", 0.0))
118
+
119
+ candle_data = {
120
+ "value": date_value,
121
+ "open": open_price,
122
+ "high": high_price,
123
+ "low": low_price,
124
+ "close": close_price,
125
+ "volume": volume,
126
+ }
127
+ candles.append(candle_data)
128
+ except (KeyError, IndexError, ValueError, TypeError):
129
+ continue
130
+
131
+ return candles
132
+
164
133
  def _extract_axes_data(self) -> dict:
165
134
  """
166
135
  Extract the plot's axes data including labels.
@@ -6,18 +6,16 @@ from datetime import datetime
6
6
 
7
7
  class DatetimeConverter:
8
8
  """
9
- Enhanced datetime converter that automatically detects time periods
10
- and provides intelligent date/time formatting for mplfinance plots.
9
+ Datetime converter for mplfinance plots.
11
10
 
12
- This utility automatically detects the time period of financial data and formats
13
- datetime values consistently for screen reader accessibility and visual clarity.
11
+ This utility provides datetime value conversion for financial data visualization.
14
12
 
15
13
  Parameters
16
14
  ----------
17
15
  data : pd.DataFrame
18
16
  DataFrame with DatetimeIndex containing financial data.
19
17
  datetime_format : str, optional
20
- Custom datetime format string. If None, automatic format detection is used.
18
+ Custom datetime format string (currently unused, kept for compatibility).
21
19
 
22
20
  Attributes
23
21
  ----------
@@ -49,14 +47,14 @@ class DatetimeConverter:
49
47
  >>>
50
48
  >>> # Get formatted datetime
51
49
  >>> formatted = converter.get_formatted_datetime(0)
52
- >>> print(formatted) # Output: "Jan 15 2024"
50
+ >>> print(formatted) # Output: "2024-01-15 00:00:00"
53
51
  >>>
54
52
  >>> # For time-based data
55
53
  >>> hourly_dates = pd.date_range('2024-01-15 09:00:00', periods=3, freq='H')
56
54
  >>> df_hourly = pd.DataFrame({'Open': [3050, 3078, 3080]}, index=hourly_dates)
57
55
  >>> converter_hourly = create_datetime_converter(df_hourly)
58
56
  >>> formatted_hourly = converter_hourly.get_formatted_datetime(0)
59
- >>> print(formatted_hourly) # Output: "Jan 15 2024 09:00"
57
+ >>> print(formatted_hourly) # Output: "2024-01-15 09:00:00"
60
58
  """
61
59
 
62
60
  def __init__(
@@ -164,9 +162,7 @@ class DatetimeConverter:
164
162
 
165
163
  def get_formatted_datetime(self, index: int) -> Optional[str]:
166
164
  """
167
- Get formatted datetime string for given index using consistent formatting.
168
-
169
- Always includes year for screen reader accessibility.
165
+ Get datetime string for given index.
170
166
 
171
167
  Parameters
172
168
  ----------
@@ -176,13 +172,13 @@ class DatetimeConverter:
176
172
  Returns
177
173
  -------
178
174
  str or None
179
- Formatted datetime string or None if index is invalid.
175
+ Datetime string or None if index is invalid.
180
176
 
181
177
  Examples
182
178
  --------
183
179
  >>> converter = create_datetime_converter(df)
184
180
  >>> formatted = converter.get_formatted_datetime(0)
185
- >>> print(formatted) # "Jan 15 2024" for daily data
181
+ >>> print(formatted) # "2024-01-15 00:00:00"
186
182
  """
187
183
  if index not in self.date_mapping:
188
184
  return None
@@ -192,7 +188,7 @@ class DatetimeConverter:
192
188
 
193
189
  def _format_datetime_custom(self, dt: datetime) -> str:
194
190
  """
195
- Consistent datetime formatting with year always included.
191
+ Format datetime as-is using ISO format.
196
192
 
197
193
  Parameters
198
194
  ----------
@@ -202,24 +198,15 @@ class DatetimeConverter:
202
198
  Returns
203
199
  -------
204
200
  str
205
- Formatted datetime string with consistent pattern.
201
+ Formatted datetime string in ISO format.
206
202
 
207
203
  Notes
208
204
  -----
209
- Formatting rules:
210
- - Daily data: "Jan 15 2024"
211
- - Time-based data: "Jan 15 2024 09:00" or "Jan 15 2024 09:00:30"
212
- - Seconds are only shown when they are non-zero for cleaner display.
205
+ Returns the datetime as a string without smart formatting.
206
+ Output format is "YYYY-MM-DD HH:MM:SS" (e.g., "2024-01-15 00:00:00").
213
207
  """
214
- if self.time_period in ["minute", "intraday", "hour"]:
215
- # Time-based data: include time with optional seconds
216
- if dt.second == 0:
217
- return dt.strftime("%b %d %Y %H:%M")
218
- else:
219
- return dt.strftime("%b %d %Y %H:%M:%S")
220
- else:
221
- # Daily/weekly/monthly data: just date
222
- return dt.strftime("%b %d %Y")
208
+ # Return string representation of datetime
209
+ return str(dt)
223
210
 
224
211
  @property
225
212
  def date_nums(self) -> List[float]:
@@ -2,11 +2,10 @@
2
2
  Utility functions for handling mplfinance-specific data extraction and processing.
3
3
  """
4
4
 
5
+ import re
5
6
  import matplotlib.dates as mdates
6
- import numpy as np
7
7
  from matplotlib.patches import Rectangle
8
- from typing import List, Optional, Tuple, Any, Union
9
- import pandas as pd
8
+ from typing import List, Optional
10
9
 
11
10
 
12
11
  class MplfinanceDataExtractor:
@@ -58,229 +57,6 @@ class MplfinanceDataExtractor:
58
57
 
59
58
  return formatted_data
60
59
 
61
- @staticmethod
62
- def extract_candlestick_data(
63
- body_collection: Any,
64
- wick_collection: Any,
65
- date_nums: Optional[List[float]] = None,
66
- original_data: Optional[Union[pd.DataFrame, pd.Series, dict]] = None,
67
- ) -> List[dict]:
68
- """
69
- Extract candlestick data from mplfinance collections.
70
-
71
- Parameters
72
- ----------
73
- body_collection : Any
74
- PolyCollection containing candlestick bodies
75
- wick_collection : Any
76
- LineCollection containing candlestick wicks
77
- date_nums : Optional[List[float]], default=None
78
- List of matplotlib date numbers corresponding to the candles
79
- original_data : Optional[Union[pd.DataFrame, pd.Series, dict]], default=None
80
- Original DataFrame/Series/dict with OHLC data for accurate bull/bear classification
81
-
82
- Returns
83
- -------
84
- List[dict]
85
- List of dictionaries with OHLC data
86
- """
87
- if not body_collection or not hasattr(body_collection, "get_paths"):
88
- return []
89
-
90
- candles = []
91
- paths = body_collection.get_paths()
92
-
93
- for i, path in enumerate(paths):
94
- if len(path.vertices) >= 4:
95
- # Extract rectangle coordinates from the path
96
- vertices = path.vertices
97
- x_coords = vertices[:, 0]
98
- y_coords = vertices[:, 1]
99
-
100
- x_min, x_max = x_coords.min(), x_coords.max()
101
- y_min, y_max = y_coords.min(), y_coords.max()
102
-
103
- # Use date mapping if available
104
- if date_nums is not None and i < len(date_nums):
105
- date_num = date_nums[i]
106
- date_str = MplfinanceDataExtractor._convert_date_num_to_string(
107
- date_num
108
- )
109
- else:
110
- x_center = (x_min + x_max) / 2
111
- date_str = MplfinanceDataExtractor._convert_date_num_to_string(
112
- x_center
113
- )
114
-
115
- # Determine if this is an up or down candle using original data
116
- is_up = MplfinanceDataExtractor._determine_bull_bear_from_data(
117
- original_data, i, date_str
118
- )
119
-
120
- # Extract OHLC values
121
- (
122
- open_val,
123
- close_val,
124
- ) = MplfinanceDataExtractor._extract_ohlc_from_rectangle(
125
- y_min, y_max, is_up
126
- )
127
-
128
- # Estimate high and low (these would normally come from wick data)
129
- high_val = y_max + (y_max - y_min) * 0.1
130
- low_val = y_min - (y_max - y_min) * 0.1
131
-
132
- candle_data = {
133
- "value": date_str,
134
- "open": round(open_val, 2),
135
- "high": round(high_val, 2),
136
- "low": round(low_val, 2),
137
- "close": round(close_val, 2),
138
- "volume": 0, # Volume is handled separately
139
- }
140
-
141
- candles.append(candle_data)
142
-
143
- return candles
144
-
145
- @staticmethod
146
- def extract_rectangle_candlestick_data(
147
- body_rectangles: List[Rectangle],
148
- date_nums: Optional[List[float]] = None,
149
- original_data: Optional[Union[pd.DataFrame, pd.Series, dict]] = None,
150
- ) -> List[dict]:
151
- """
152
- Extract candlestick data from Rectangle patches (original_flavor).
153
-
154
- Parameters
155
- ----------
156
- body_rectangles : List[Rectangle]
157
- List of Rectangle patches representing candlestick bodies
158
- date_nums : Optional[List[float]], default=None
159
- List of matplotlib date numbers corresponding to the candles
160
- original_data : Optional[Union[pd.DataFrame, pd.Series, dict]], default=None
161
- Original DataFrame/Series/dict with OHLC data for accurate bull/bear classification
162
-
163
- Returns
164
- -------
165
- List[dict]
166
- List of dictionaries with OHLC data
167
- """
168
- if not body_rectangles:
169
- return []
170
-
171
- candles = []
172
-
173
- # Sort rectangles by x-coordinate
174
- body_rectangles.sort(key=lambda r: r.get_x())
175
-
176
- for i, rect in enumerate(body_rectangles):
177
- x_left = rect.get_x()
178
- width = rect.get_width()
179
- x_center_num = x_left + width / 2.0
180
-
181
- # Convert x coordinate to date
182
- if date_nums is not None and i < len(date_nums):
183
- date_str = MplfinanceDataExtractor._convert_date_num_to_string(
184
- date_nums[i]
185
- )
186
- else:
187
- date_str = MplfinanceDataExtractor._convert_date_num_to_string(
188
- x_center_num
189
- )
190
-
191
- y_bottom = rect.get_y()
192
- height = rect.get_height()
193
-
194
- # Determine if this is an up or down candle using original data
195
- is_up_candle = MplfinanceDataExtractor._determine_bull_bear_from_data(
196
- original_data, i, date_str
197
- )
198
-
199
- # Extract OHLC values from rectangle
200
- (
201
- open_price,
202
- close_price,
203
- ) = MplfinanceDataExtractor._extract_ohlc_from_rectangle(
204
- y_bottom, y_bottom + height, is_up_candle
205
- )
206
-
207
- # Estimate high and low
208
- high_price = max(open_price, close_price) + height * 0.1
209
- low_price = min(open_price, close_price) - height * 0.1
210
-
211
- # Ensure all values are valid numbers
212
- open_price = float(open_price) if not np.isnan(open_price) else 0.0
213
- high_price = float(high_price) if not np.isnan(high_price) else 0.0
214
- low_price = float(low_price) if not np.isnan(low_price) else 0.0
215
- close_price = float(close_price) if not np.isnan(close_price) else 0.0
216
-
217
- candle_data = {
218
- "value": date_str,
219
- "open": round(open_price, 2),
220
- "high": round(high_price, 2),
221
- "low": round(low_price, 2),
222
- "close": round(close_price, 2),
223
- "volume": 0,
224
- }
225
-
226
- candles.append(candle_data)
227
-
228
- return candles
229
-
230
- @staticmethod
231
- def _determine_bull_bear_from_data(
232
- original_data: Optional[Union[pd.DataFrame, pd.Series, dict]],
233
- index: int,
234
- date_str: str,
235
- ) -> bool:
236
- """
237
- Determine if a candle is bullish (up) or bearish (down) using original OHLC data.
238
-
239
- This is the most robust method as it uses the actual data rather than colors.
240
-
241
- Parameters
242
- ----------
243
- original_data : Optional[Union[pd.DataFrame, pd.Series, dict]]
244
- Original DataFrame/Series/dict with OHLC data
245
- index : int
246
- Index of the candle
247
- date_str : str
248
- Date string for the candle
249
-
250
- Returns
251
- -------
252
- bool
253
- True if bullish (close > open), False if bearish (close < open)
254
- """
255
- # Default to bullish if no data available
256
- if original_data is None:
257
- return True
258
-
259
- try:
260
- # Try to access the original data
261
- if hasattr(original_data, "iloc"):
262
- # It's a pandas DataFrame/Series
263
- if index < len(original_data):
264
- row = original_data.iloc[index]
265
- if "Close" in row and "Open" in row:
266
- is_bullish = row["Close"] > row["Open"]
267
- return is_bullish
268
-
269
- elif hasattr(original_data, "__getitem__"):
270
- # It's a dictionary or similar
271
- if "Close" in original_data and "Open" in original_data:
272
- closes = original_data["Close"]
273
- opens = original_data["Open"]
274
- if index < len(closes) and index < len(opens):
275
- is_bullish = closes[index] > opens[index]
276
- return is_bullish
277
-
278
- except (KeyError, IndexError, AttributeError):
279
- pass
280
-
281
- # Fallback to bullish if data access fails
282
- return True # Default to bullish
283
-
284
60
  @staticmethod
285
61
  def clean_axis_label(label: str) -> str:
286
62
  """
@@ -299,8 +75,6 @@ class MplfinanceDataExtractor:
299
75
  if not label or not isinstance(label, str):
300
76
  return label
301
77
 
302
- import re
303
-
304
78
  # Removes LaTeX-like scientific notation, e.g., "$10^{6}$"
305
79
  cleaned_label = re.sub(r"\s*\$.*?\$", "", label).strip()
306
80
  return cleaned_label if cleaned_label else label
@@ -318,7 +92,7 @@ class MplfinanceDataExtractor:
318
92
  Returns
319
93
  -------
320
94
  str
321
- Date string in YYYY-MM-DD format or fallback index
95
+ Date string representation (e.g., "2024-01-15 00:00:00") or fallback index
322
96
  """
323
97
  try:
324
98
  # Check if this looks like a matplotlib date number (typically > 700000)
@@ -326,14 +100,14 @@ class MplfinanceDataExtractor:
326
100
  date_dt = mdates.num2date(date_num)
327
101
  if hasattr(date_dt, "replace"):
328
102
  date_dt = date_dt.replace(tzinfo=None)
329
- return date_dt.strftime("%Y-%m-%d")
103
+ return str(date_dt)
330
104
  elif date_num > 1000:
331
105
  # Try converting as if it's a pandas timestamp
332
106
  try:
333
107
  import pandas as pd
334
108
 
335
109
  date_dt = pd.to_datetime(date_num, unit="D")
336
- return date_dt.strftime("%Y-%m-%d")
110
+ return str(date_dt)
337
111
  except (ValueError, TypeError):
338
112
  pass
339
113
  except (ValueError, TypeError, OverflowError):
@@ -341,75 +115,3 @@ class MplfinanceDataExtractor:
341
115
 
342
116
  # Fallback to index-based date string
343
117
  return f"date_{int(date_num):03d}"
344
-
345
- @staticmethod
346
- def convert_x_to_date(x_center_num: float, axes: Optional[List] = None) -> str:
347
- """
348
- Convert matplotlib x-coordinate to date string.
349
-
350
- Parameters
351
- ----------
352
- x_center_num : float
353
- X-coordinate value to convert
354
- axes : Optional[List], optional
355
- List of matplotlib axes to help with date conversion
356
-
357
- Returns
358
- -------
359
- str
360
- Date string in YYYY-MM-DD format or fallback
361
- """
362
- # First, try to get the actual dates from the axes x-axis data
363
- if axes and len(axes) > 0:
364
- ax = axes[0]
365
- try:
366
- # Get x-axis ticks which might contain the actual dates
367
- x_ticks = ax.get_xticks()
368
-
369
- # If we have x-axis ticks and they look like dates (large numbers), use them
370
- if len(x_ticks) > 0 and x_ticks[0] > 1000:
371
- # Find the closest tick to our x_center_num
372
- tick_index = int(round(x_center_num))
373
- if 0 <= tick_index < len(x_ticks):
374
- actual_date_num = x_ticks[tick_index]
375
-
376
- # Convert the actual date number
377
- if actual_date_num > 700000:
378
- date_dt = mdates.num2date(actual_date_num)
379
- if hasattr(date_dt, "replace"):
380
- date_dt = date_dt.replace(tzinfo=None)
381
- date_str = date_dt.strftime("%Y-%m-%d")
382
- return date_str
383
- except Exception:
384
- pass
385
-
386
- # Use the utility class for date conversion
387
- return MplfinanceDataExtractor._convert_date_num_to_string(x_center_num)
388
-
389
- @staticmethod
390
- def _extract_ohlc_from_rectangle(
391
- y_min: float, y_max: float, is_up: bool
392
- ) -> Tuple[float, float]:
393
- """
394
- Extract open and close values from rectangle bounds.
395
-
396
- Parameters
397
- ----------
398
- y_min : float
399
- Minimum y value of rectangle
400
- y_max : float
401
- Maximum y value of rectangle
402
- is_up : bool
403
- Whether this is an up candle
404
-
405
- Returns
406
- -------
407
- Tuple[float, float]
408
- (open_price, close_price)
409
- """
410
- if is_up:
411
- # Up candle: open at bottom, close at top
412
- return y_min, y_max
413
- else:
414
- # Down candle: open at top, close at bottom
415
- return y_max, y_min
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maidr
3
- Version: 1.9.0
3
+ Version: 1.10.0
4
4
  Summary: Multimodal Access and Interactive Data Representations
5
5
  Project-URL: Homepage, https://xability.github.io/py-maidr
6
6
  Project-URL: Repository, https://github.com/xability/py-maidr
@@ -1,4 +1,4 @@
1
- maidr/__init__.py,sha256=szUjuM4cnGyzipfrIxpyZV841yhHjMmIcP7yXHq7kq8,415
1
+ maidr/__init__.py,sha256=BXkAG-4A4pF_IjbBj6cOmPTmQYICXnfPniPJ-4OHnr0,416
2
2
  maidr/api.py,sha256=WK7jfQttuPeF1q45RuF_wTciYyiFY6SyjfZVSOVkiUs,4316
3
3
  maidr/core/__init__.py,sha256=WgxLpSEYMc4k3OyEOf1shOxfEq0ASzppEIZYmE91ThQ,25
4
4
  maidr/core/context_manager.py,sha256=6cT7ZGOApSpC-SLD2XZWWU_H08i-nfv-JUlzXOtvWYw,3374
@@ -12,7 +12,7 @@ maidr/core/enum/smooth_keywords.py,sha256=z2kVZZ-mETWWh5reWu_hj9WkJD6WFj7_2-6s1e
12
12
  maidr/core/plot/__init__.py,sha256=xDIpRGM-4DfaSSL3nKcXrjdMecCHJ6en4K4nA_fPefQ,83
13
13
  maidr/core/plot/barplot.py,sha256=0hBgp__putezvxXc9G3qmaktmAzze3cN8pQMD9iqktE,2116
14
14
  maidr/core/plot/boxplot.py,sha256=i11GdNuz_c-hilmhydu3ah-bzyVdFoBkNvRi5lpMrrY,9946
15
- maidr/core/plot/candlestick.py,sha256=ofvlUwtzaaopvv6VjNDf1IZODbu1UkMHsi1zdvcG-Yo,10120
15
+ maidr/core/plot/candlestick.py,sha256=qOsYIbn2sRdGE2ES-YJ-Rwhu4NsScHs52mtKe6Bl7_A,8236
16
16
  maidr/core/plot/grouped_barplot.py,sha256=odZ52Pl22nb9cWKD3NGsZsyFDxXdBDAEcbOj66HKp9I,4063
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
@@ -40,10 +40,10 @@ maidr/patch/mplfinance.py,sha256=ySD32onanoMgdQkV6XlSAbVd_BQuLWuEQtpkYSEDSzA,949
40
40
  maidr/patch/regplot.py,sha256=k86ekd0E4XJ_L1u85zObuDnxuXlM83z7tKtyXRTj2rI,3240
41
41
  maidr/patch/scatterplot.py,sha256=kln6zZwjVsdQzICalo-RnBOJrx1BnIB2xYUwItHvSNY,525
42
42
  maidr/util/__init__.py,sha256=eRJZfRpDX-n7UoV3JXw_9Lbfu_qNl_D0W1UTvLL-Iv4,81
43
- maidr/util/datetime_conversion.py,sha256=AQ8qShbEkLVo13TUkOOmtOLnOvaI05Vh5oWhgchvXSA,14478
43
+ maidr/util/datetime_conversion.py,sha256=BF115xweGcrKyDnnjYPeScc0WgeNpCylV0Z-mYKaP4w,13769
44
44
  maidr/util/dedup_utils.py,sha256=RpgPL5p-3oULUHaTCZJaQKhPHfyPkvBLHMt8lAGpJ5A,438
45
45
  maidr/util/environment.py,sha256=C4VMyB16mqzrFxpJdxFdm40M0IZojxh60UX80680jgo,9403
46
- maidr/util/mplfinance_utils.py,sha256=OZe5Y7gzjdjve9DViioQvGZYTdZvz8obvN3oHElQFZw,14418
46
+ maidr/util/mplfinance_utils.py,sha256=00YEjrCUbigZZL1j9jzOTamNnwfy5ZZmXJj65AhgNbw,3662
47
47
  maidr/util/plot_detection.py,sha256=bgLHoDcHSRwOiyKzUK3EqGwdAIhF44ocHW5ox6xYGZw,3883
48
48
  maidr/util/regression_line_utils.py,sha256=yFKr-H0whT_su2YVZwNksBLp5EC5s77sr6HUFgNcsyY,2329
49
49
  maidr/util/svg_utils.py,sha256=2gyzBtNKFHs0utrw1iOlxTmznzivOWQMV2aW8zu2c8E,1442
@@ -52,7 +52,7 @@ maidr/util/mixin/extractor_mixin.py,sha256=j2Rv2vh_gqqcxLV1ka3xsPaPAfWsX94CtKIW2
52
52
  maidr/util/mixin/merger_mixin.py,sha256=V0qLw_6DUB7X6CQ3BCMpsCQX_ZuwAhoSTm_E4xAJFKM,712
53
53
  maidr/widget/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  maidr/widget/shiny.py,sha256=wrrw2KAIpE_A6CNQGBtNHauR1DjenA_n47qlFXX9_rk,745
55
- maidr-1.9.0.dist-info/METADATA,sha256=EK0mJx_hRNjiVvAnGcph7sBceEbiQrgvioGFPcw8-EY,3154
56
- maidr-1.9.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
57
- maidr-1.9.0.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
58
- maidr-1.9.0.dist-info/RECORD,,
55
+ maidr-1.10.0.dist-info/METADATA,sha256=6llsnqQI7-gfp62yZJ1ucSXxf9xvrUVNtcItkoxnwnM,3155
56
+ maidr-1.10.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
57
+ maidr-1.10.0.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
58
+ maidr-1.10.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any