maidr 1.9.0__tar.gz → 1.10.0__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 (161) hide show
  1. {maidr-1.9.0 → maidr-1.10.0}/CHANGELOG.md +9 -0
  2. {maidr-1.9.0 → maidr-1.10.0}/PKG-INFO +1 -1
  3. {maidr-1.9.0 → maidr-1.10.0}/docs/examples.qmd +1 -0
  4. {maidr-1.9.0 → maidr-1.10.0}/maidr/__init__.py +1 -1
  5. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/candlestick.py +59 -90
  6. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/datetime_conversion.py +14 -27
  7. maidr-1.10.0/maidr/util/mplfinance_utils.py +117 -0
  8. {maidr-1.9.0 → maidr-1.10.0}/pyproject.toml +1 -1
  9. {maidr-1.9.0 → maidr-1.10.0}/uv.lock +7 -1
  10. maidr-1.9.0/maidr/util/mplfinance_utils.py +0 -415
  11. {maidr-1.9.0 → maidr-1.10.0}/.commitlintrc.cjs +0 -0
  12. {maidr-1.9.0 → maidr-1.10.0}/.editorconfig +0 -0
  13. {maidr-1.9.0 → maidr-1.10.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  14. {maidr-1.9.0 → maidr-1.10.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  15. {maidr-1.9.0 → maidr-1.10.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  16. {maidr-1.9.0 → maidr-1.10.0}/.github/copilot-instructions.md +0 -0
  17. {maidr-1.9.0 → maidr-1.10.0}/.github/workflows/ci.yml +0 -0
  18. {maidr-1.9.0 → maidr-1.10.0}/.github/workflows/docs.yml +0 -0
  19. {maidr-1.9.0 → maidr-1.10.0}/.github/workflows/release.yml +0 -0
  20. {maidr-1.9.0 → maidr-1.10.0}/.gitignore +0 -0
  21. {maidr-1.9.0 → maidr-1.10.0}/.pre-commit-config.yaml +0 -0
  22. {maidr-1.9.0 → maidr-1.10.0}/.vscode/extensions.json +0 -0
  23. {maidr-1.9.0 → maidr-1.10.0}/.vscode/settings.json +0 -0
  24. {maidr-1.9.0 → maidr-1.10.0}/CONDUCT.md +0 -0
  25. {maidr-1.9.0 → maidr-1.10.0}/CONTRIBUTING.md +0 -0
  26. {maidr-1.9.0 → maidr-1.10.0}/LICENSE +0 -0
  27. {maidr-1.9.0 → maidr-1.10.0}/README.md +0 -0
  28. {maidr-1.9.0 → maidr-1.10.0}/docs/.gitignore +0 -0
  29. {maidr-1.9.0 → maidr-1.10.0}/docs/CNAME +0 -0
  30. {maidr-1.9.0 → maidr-1.10.0}/docs/_environment +0 -0
  31. {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/machow/interlinks/.gitignore +0 -0
  32. {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/machow/interlinks/_extension.yml +0 -0
  33. {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/machow/interlinks/interlinks.lua +0 -0
  34. {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/_extension.yml +0 -0
  35. {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/line-highlight.lua +0 -0
  36. {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/resources/css/line-highlight.css +0 -0
  37. {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/resources/js/line-highlight.js +0 -0
  38. {maidr-1.9.0 → maidr-1.10.0}/docs/_quarto.yml +0 -0
  39. {maidr-1.9.0 → maidr-1.10.0}/docs/index.qmd +0 -0
  40. {maidr-1.9.0 → maidr-1.10.0}/docs/styles.css +0 -0
  41. {maidr-1.9.0 → maidr-1.10.0}/example/bar/example_bar_plot.ipynb +0 -0
  42. {maidr-1.9.0 → maidr-1.10.0}/example/bar/matplotlib/example_mpl_bar_plot.py +0 -0
  43. {maidr-1.9.0 → maidr-1.10.0}/example/bar/seaborn/example_sns_bar_plot.py +0 -0
  44. {maidr-1.9.0 → maidr-1.10.0}/example/box/example_box_plot.ipynb +0 -0
  45. {maidr-1.9.0 → maidr-1.10.0}/example/box/matplotlib/example_mpl_box.py +0 -0
  46. {maidr-1.9.0 → maidr-1.10.0}/example/box/seaborn/example_sns_box.py +0 -0
  47. {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/legacy_candlestick_example.py +0 -0
  48. {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/mplfinance_candlestick_example.py +0 -0
  49. {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_daily_current_year.csv +0 -0
  50. {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_daily_mixed_years.csv +0 -0
  51. {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_hourly.csv +0 -0
  52. {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_minute.csv +0 -0
  53. {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_weekly.csv +0 -0
  54. {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/volcandat.csv +0 -0
  55. {maidr-1.9.0 → maidr-1.10.0}/example/count/example_count_plot.ipynb +0 -0
  56. {maidr-1.9.0 → maidr-1.10.0}/example/count/seaborn/example_sns_count_plot.py +0 -0
  57. {maidr-1.9.0 → maidr-1.10.0}/example/dodged/matplotlib/example_mpl_dodged.py +0 -0
  58. {maidr-1.9.0 → maidr-1.10.0}/example/dodged/seaborn/example_sns_dodged.py +0 -0
  59. {maidr-1.9.0 → maidr-1.10.0}/example/facet-subplots/matplotlib/example_mpl_facet_bar_plot.py +0 -0
  60. {maidr-1.9.0 → maidr-1.10.0}/example/facet-subplots/matplotlib/example_mpl_facet_combined_plot.py +0 -0
  61. {maidr-1.9.0 → maidr-1.10.0}/example/facet-subplots/seaborn/example_sns_facet_bar_plot.py +0 -0
  62. {maidr-1.9.0 → maidr-1.10.0}/example/facet-subplots/seaborn/example_sns_facet_combined_plot.py +0 -0
  63. {maidr-1.9.0 → maidr-1.10.0}/example/flask/test_flask_app.py +0 -0
  64. {maidr-1.9.0 → maidr-1.10.0}/example/heatmap/example_heatmap_plot.ipynb +0 -0
  65. {maidr-1.9.0 → maidr-1.10.0}/example/heatmap/matplotlib/example_mpl_heatmap.py +0 -0
  66. {maidr-1.9.0 → maidr-1.10.0}/example/heatmap/seaborn/example_sns_heatmap.py +0 -0
  67. {maidr-1.9.0 → maidr-1.10.0}/example/histogram/example_histogram_plot.ipynb +0 -0
  68. {maidr-1.9.0 → maidr-1.10.0}/example/histogram/matplotlib/example_mpl_hist.py +0 -0
  69. {maidr-1.9.0 → maidr-1.10.0}/example/histogram/matplotlib/histogram_with_kde_matplotlib.py +0 -0
  70. {maidr-1.9.0 → maidr-1.10.0}/example/histogram/seaborn/example_sns_hist.py +0 -0
  71. {maidr-1.9.0 → maidr-1.10.0}/example/histogram/seaborn/histogram_with_kde_seaborn.py +0 -0
  72. {maidr-1.9.0 → maidr-1.10.0}/example/kde/example_kde_plots.ipynb +0 -0
  73. {maidr-1.9.0 → maidr-1.10.0}/example/kde/matplotlib/multiple_kde_matplotlib.py +0 -0
  74. {maidr-1.9.0 → maidr-1.10.0}/example/kde/matplotlib/single_kde_matplotlib.py +0 -0
  75. {maidr-1.9.0 → maidr-1.10.0}/example/kde/seaborn/multiple_kde_seaborn.py +0 -0
  76. {maidr-1.9.0 → maidr-1.10.0}/example/kde/seaborn/single_kde_seaborn.py +0 -0
  77. {maidr-1.9.0 → maidr-1.10.0}/example/line/example_line_plot.ipynb +0 -0
  78. {maidr-1.9.0 → maidr-1.10.0}/example/line/matplotlib/example_mpl_line.py +0 -0
  79. {maidr-1.9.0 → maidr-1.10.0}/example/line/seaborn/example_sns_line.py +0 -0
  80. {maidr-1.9.0 → maidr-1.10.0}/example/multilayer/example_mpl_multilayer.py +0 -0
  81. {maidr-1.9.0 → maidr-1.10.0}/example/multilayer/example_multilayer_plot.ipynb +0 -0
  82. {maidr-1.9.0 → maidr-1.10.0}/example/multiline/example_multiline_plot.ipynb +0 -0
  83. {maidr-1.9.0 → maidr-1.10.0}/example/multiline/matplotlib/example_mpl_multiline.py +0 -0
  84. {maidr-1.9.0 → maidr-1.10.0}/example/multiline/seaborn/example_sns_multiline.py +0 -0
  85. {maidr-1.9.0 → maidr-1.10.0}/example/multipanel/example_multipanel_plot.ipynb +0 -0
  86. {maidr-1.9.0 → maidr-1.10.0}/example/multipanel/matplotlib/example_mpl_multipanel.py +0 -0
  87. {maidr-1.9.0 → maidr-1.10.0}/example/multipanel/seaborn/example_sns_multipanel.py +0 -0
  88. {maidr-1.9.0 → maidr-1.10.0}/example/quarto/demo.qmd +0 -0
  89. {maidr-1.9.0 → maidr-1.10.0}/example/reg/example_reg_plots.ipynb +0 -0
  90. {maidr-1.9.0 → maidr-1.10.0}/example/reg/matplotlib/example_matplotlib_smooth_plot.py +0 -0
  91. {maidr-1.9.0 → maidr-1.10.0}/example/reg/seaborn/example_sns_reg.py +0 -0
  92. {maidr-1.9.0 → maidr-1.10.0}/example/scatter/example_scatter_plot.ipynb +0 -0
  93. {maidr-1.9.0 → maidr-1.10.0}/example/scatter/matplotlib/example_mpl_scatter.py +0 -0
  94. {maidr-1.9.0 → maidr-1.10.0}/example/scatter/seaborn/example_sns_scatter.py +0 -0
  95. {maidr-1.9.0 → maidr-1.10.0}/example/shiny/example_shiny_scatter.py +0 -0
  96. {maidr-1.9.0 → maidr-1.10.0}/example/stacked/matplotlib/example_mpl_stacked.py +0 -0
  97. {maidr-1.9.0 → maidr-1.10.0}/example/stacked/seaborn/example_sns_stacked.py +0 -0
  98. {maidr-1.9.0 → maidr-1.10.0}/example/streamlit/example_streamlit_app.py +0 -0
  99. {maidr-1.9.0 → maidr-1.10.0}/maidr/api.py +0 -0
  100. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/__init__.py +0 -0
  101. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/context_manager.py +0 -0
  102. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/__init__.py +0 -0
  103. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/library.py +0 -0
  104. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/maidr_key.py +0 -0
  105. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/plot_type.py +0 -0
  106. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/smooth_keywords.py +0 -0
  107. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/figure_manager.py +0 -0
  108. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/maidr.py +0 -0
  109. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/__init__.py +0 -0
  110. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/barplot.py +0 -0
  111. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/boxplot.py +0 -0
  112. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/grouped_barplot.py +0 -0
  113. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/heatmap.py +0 -0
  114. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/histogram.py +0 -0
  115. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/lineplot.py +0 -0
  116. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/maidr_plot.py +0 -0
  117. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/maidr_plot_factory.py +0 -0
  118. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/mplfinance_barplot.py +0 -0
  119. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/mplfinance_lineplot.py +0 -0
  120. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/regplot.py +0 -0
  121. {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/scatterplot.py +0 -0
  122. {maidr-1.9.0 → maidr-1.10.0}/maidr/exception/__init__.py +0 -0
  123. {maidr-1.9.0 → maidr-1.10.0}/maidr/exception/extraction_error.py +0 -0
  124. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/__init__.py +0 -0
  125. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/barplot.py +0 -0
  126. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/boxplot.py +0 -0
  127. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/candlestick.py +0 -0
  128. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/clear.py +0 -0
  129. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/common.py +0 -0
  130. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/heatmap.py +0 -0
  131. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/highlight.py +0 -0
  132. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/histogram.py +0 -0
  133. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/kdeplot.py +0 -0
  134. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/lineplot.py +0 -0
  135. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/mplfinance.py +0 -0
  136. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/regplot.py +0 -0
  137. {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/scatterplot.py +0 -0
  138. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/__init__.py +0 -0
  139. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/dedup_utils.py +0 -0
  140. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/environment.py +0 -0
  141. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/mixin/__init__.py +0 -0
  142. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/mixin/extractor_mixin.py +0 -0
  143. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/mixin/merger_mixin.py +0 -0
  144. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/plot_detection.py +0 -0
  145. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/regression_line_utils.py +0 -0
  146. {maidr-1.9.0 → maidr-1.10.0}/maidr/util/svg_utils.py +0 -0
  147. {maidr-1.9.0 → maidr-1.10.0}/maidr/widget/__init__.py +0 -0
  148. {maidr-1.9.0 → maidr-1.10.0}/maidr/widget/shiny.py +0 -0
  149. {maidr-1.9.0 → maidr-1.10.0}/tests/__init__.py +0 -0
  150. {maidr-1.9.0 → maidr-1.10.0}/tests/conftest.py +0 -0
  151. {maidr-1.9.0 → maidr-1.10.0}/tests/core/__init__.py +0 -0
  152. {maidr-1.9.0 → maidr-1.10.0}/tests/core/enum/__init__.py +0 -0
  153. {maidr-1.9.0 → maidr-1.10.0}/tests/core/plot/__init__.py +0 -0
  154. {maidr-1.9.0 → maidr-1.10.0}/tests/core/test_figure_manager.py +0 -0
  155. {maidr-1.9.0 → maidr-1.10.0}/tests/core/test_maidr_plot.py +0 -0
  156. {maidr-1.9.0 → maidr-1.10.0}/tests/core/test_maidr_plot_factory.py +0 -0
  157. {maidr-1.9.0 → maidr-1.10.0}/tests/fixture/__init__.py +0 -0
  158. {maidr-1.9.0 → maidr-1.10.0}/tests/fixture/library_factory.py +0 -0
  159. {maidr-1.9.0 → maidr-1.10.0}/tests/fixture/matplotlib_factory.py +0 -0
  160. {maidr-1.9.0 → maidr-1.10.0}/tests/fixture/seaborn_factory.py +0 -0
  161. {maidr-1.9.0 → maidr-1.10.0}/tox.ini +0 -0
@@ -1,6 +1,15 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v1.10.0 (2026-01-31)
5
+
6
+ ### Features
7
+
8
+ - Remove candlestick formatting and add ylabel to dodged plots
9
+ ([#260](https://github.com/xability/py-maidr/pull/260),
10
+ [`70b2626`](https://github.com/xability/py-maidr/commit/70b2626bb31e109e8cdb29051f809fe03bfb6275))
11
+
12
+
4
13
  ## v1.9.0 (2025-10-31)
5
14
 
6
15
  ### Features
@@ -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
@@ -152,6 +152,7 @@ for offset, (category, counts) in zip(offsets, weight_counts.items()):
152
152
  ax.set_xticks(x)
153
153
  ax.set_xticklabels(species)
154
154
  ax.set_xlabel("Species")
155
+ ax.set_ylabel("Weight")
155
156
  ax.set_title("Dodged Bar Plot: Penguin Weight Counts")
156
157
  ax.legend(loc="upper right")
157
158
 
@@ -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]:
@@ -0,0 +1,117 @@
1
+ """
2
+ Utility functions for handling mplfinance-specific data extraction and processing.
3
+ """
4
+
5
+ import re
6
+ import matplotlib.dates as mdates
7
+ from matplotlib.patches import Rectangle
8
+ from typing import List, Optional
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 clean_axis_label(label: str) -> str:
62
+ """
63
+ Clean up axis labels by removing LaTeX formatting.
64
+
65
+ Parameters
66
+ ----------
67
+ label : str
68
+ The original axis label
69
+
70
+ Returns
71
+ -------
72
+ str
73
+ Cleaned axis label
74
+ """
75
+ if not label or not isinstance(label, str):
76
+ return label
77
+
78
+ # Removes LaTeX-like scientific notation, e.g., "$10^{6}$"
79
+ cleaned_label = re.sub(r"\s*\$.*?\$", "", label).strip()
80
+ return cleaned_label if cleaned_label else label
81
+
82
+ @staticmethod
83
+ def _convert_date_num_to_string(date_num: float) -> str:
84
+ """
85
+ Convert matplotlib date number to date string.
86
+
87
+ Parameters
88
+ ----------
89
+ date_num : float
90
+ Matplotlib date number
91
+
92
+ Returns
93
+ -------
94
+ str
95
+ Date string representation (e.g., "2024-01-15 00:00:00") or fallback index
96
+ """
97
+ try:
98
+ # Check if this looks like a matplotlib date number (typically > 700000)
99
+ if date_num > 700000:
100
+ date_dt = mdates.num2date(date_num)
101
+ if hasattr(date_dt, "replace"):
102
+ date_dt = date_dt.replace(tzinfo=None)
103
+ return str(date_dt)
104
+ elif date_num > 1000:
105
+ # Try converting as if it's a pandas timestamp
106
+ try:
107
+ import pandas as pd
108
+
109
+ date_dt = pd.to_datetime(date_num, unit="D")
110
+ return str(date_dt)
111
+ except (ValueError, TypeError):
112
+ pass
113
+ except (ValueError, TypeError, OverflowError):
114
+ pass
115
+
116
+ # Fallback to index-based date string
117
+ return f"date_{int(date_num):03d}"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "maidr"
7
- version = "1.9.0"
7
+ version = "1.10.0"
8
8
  description = "Multimodal Access and Interactive Data Representations"
9
9
  authors = [
10
10
  { name = "JooYoung Seo", email = "jseo1005@illinois.edu" },
@@ -1876,7 +1876,7 @@ wheels = [
1876
1876
 
1877
1877
  [[package]]
1878
1878
  name = "maidr"
1879
- version = "1.8.1"
1879
+ version = "1.9.0"
1880
1880
  source = { editable = "." }
1881
1881
  dependencies = [
1882
1882
  { name = "htmltools" },
@@ -3993,6 +3993,12 @@ wheels = [
3993
3993
  { url = "https://files.pythonhosted.org/packages/1e/48/973da1ee8bc0743519759e74c3615b39acdc3faf00e0a0710f8c856d8c9d/statsmodels-0.14.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a085d47c8ef5387279a991633883d0e700de2b0acc812d7032d165888627bef", size = 10453538, upload-time = "2025-07-07T14:24:06.959Z" },
3994
3994
  { url = "https://files.pythonhosted.org/packages/c7/d6/18903fb707afd31cf1edaec5201964dbdacb2bfae9a22558274647a7c88f/statsmodels-0.14.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f866b2ebb2904b47c342d00def83c526ef2eb1df6a9a3c94ba5fe63d0005aec", size = 10681584, upload-time = "2025-07-07T14:24:21.038Z" },
3995
3995
  { url = "https://files.pythonhosted.org/packages/44/d6/80df1bbbfcdc50bff4152f43274420fa9856d56e234d160d6206eb1f5827/statsmodels-0.14.5-cp313-cp313-win_amd64.whl", hash = "sha256:2a06bca03b7a492f88c8106103ab75f1a5ced25de90103a89f3a287518017939", size = 9604641, upload-time = "2025-07-07T12:08:36.23Z" },
3996
+ { url = "https://files.pythonhosted.org/packages/fd/6c/0fb40a89d715412160097c6f3387049ed88c9bd866c8838a8852c705ae2f/statsmodels-0.14.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:07c4dad25bbb15864a31b4917a820f6d104bdc24e5ddadcda59027390c3bed9e", size = 10211256, upload-time = "2025-10-30T13:46:58.591Z" },
3997
+ { url = "https://files.pythonhosted.org/packages/88/4a/e36fe8b19270ab3e80df357da924c6c029cab0fb9a0fbd28aaf49341707d/statsmodels-0.14.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:babb067c852e966c2c933b79dbb5d0240919d861941a2ef6c0e13321c255528d", size = 10110933, upload-time = "2025-10-30T13:47:11.774Z" },
3998
+ { url = "https://files.pythonhosted.org/packages/8a/bf/1b7e7b1a6c09a88a9c5c9e60622c050dfd08af11c2e6d4a42dbc71b32ee1/statsmodels-0.14.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:110194b137286173cc676d7bad0119a197778de6478fc6cbdc3b33571165ac1e", size = 10253981, upload-time = "2025-10-30T16:32:22.399Z" },
3999
+ { url = "https://files.pythonhosted.org/packages/b8/d0/f95da95524bdd99613923ca61a3036d1308cee1290e5e8acb89f51736a8c/statsmodels-0.14.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c8a9c384a60c80731b278e7fd18764364c8817f4995b13a175d636f967823d1", size = 10460450, upload-time = "2025-10-30T16:32:44.985Z" },
4000
+ { url = "https://files.pythonhosted.org/packages/28/bb/59e7be0271be264b7b541baf3973f97747740950bfd5115de731f63da8ab/statsmodels-0.14.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:557df3a870a57248df744fdfcc444ecbc5bdbf1c042b8a8b5d8e3e797830dc2a", size = 10694060, upload-time = "2025-10-30T16:33:07.656Z" },
4001
+ { url = "https://files.pythonhosted.org/packages/8b/c0/b28d0fd0347ea38d3610052f479e4b922eb33bb8790817f93cd89e6e08ba/statsmodels-0.14.5-cp314-cp314-win_amd64.whl", hash = "sha256:95af7a9c4689d514f4341478b891f867766f3da297f514b8c4adf08f4fa61d03", size = 9648961, upload-time = "2025-10-30T13:47:24.303Z" },
3996
4002
  { url = "https://files.pythonhosted.org/packages/39/2d/3ab5a8e736b94a91434a70dcbdc4363775711ef17c733e6bde5f24cb2f62/statsmodels-0.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b23b8f646dd78ef5e8d775d879208f8dc0a73418b41c16acac37361ff9ab7738", size = 10077385, upload-time = "2025-07-07T12:13:55.07Z" },
3997
4003
  { url = "https://files.pythonhosted.org/packages/44/ec/091dc1e69bbc84139e3409e45ac26e285ef41eb67116d13e094cdde7804d/statsmodels-0.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e5e26b21d2920905764fb0860957d08b5ba2fae4466ef41b1f7c53ecf9fc7fa", size = 9752723, upload-time = "2025-07-07T12:08:52.238Z" },
3998
4004
  { url = "https://files.pythonhosted.org/packages/72/0a/0ab3a900fc3245ebdaaca59018567b1e23bcab13c9eea2d7b3d8ffcbb82e/statsmodels-0.14.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a060c7e0841c549c8ce2825fd6687e6757e305d9c11c9a73f6c5a0ce849bb69", size = 10470566, upload-time = "2025-07-07T14:33:03.356Z" },