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.
- {maidr-1.9.0 → maidr-1.10.0}/CHANGELOG.md +9 -0
- {maidr-1.9.0 → maidr-1.10.0}/PKG-INFO +1 -1
- {maidr-1.9.0 → maidr-1.10.0}/docs/examples.qmd +1 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/__init__.py +1 -1
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/candlestick.py +59 -90
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/datetime_conversion.py +14 -27
- maidr-1.10.0/maidr/util/mplfinance_utils.py +117 -0
- {maidr-1.9.0 → maidr-1.10.0}/pyproject.toml +1 -1
- {maidr-1.9.0 → maidr-1.10.0}/uv.lock +7 -1
- maidr-1.9.0/maidr/util/mplfinance_utils.py +0 -415
- {maidr-1.9.0 → maidr-1.10.0}/.commitlintrc.cjs +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.editorconfig +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.github/copilot-instructions.md +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.github/workflows/ci.yml +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.github/workflows/docs.yml +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.github/workflows/release.yml +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.gitignore +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.pre-commit-config.yaml +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.vscode/extensions.json +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/.vscode/settings.json +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/CONDUCT.md +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/CONTRIBUTING.md +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/LICENSE +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/README.md +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/.gitignore +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/CNAME +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_environment +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/machow/interlinks/.gitignore +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/machow/interlinks/_extension.yml +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/machow/interlinks/interlinks.lua +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/_extension.yml +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/line-highlight.lua +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/resources/css/line-highlight.css +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/resources/js/line-highlight.js +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/_quarto.yml +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/index.qmd +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/docs/styles.css +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/bar/example_bar_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/bar/matplotlib/example_mpl_bar_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/bar/seaborn/example_sns_bar_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/box/example_box_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/box/matplotlib/example_mpl_box.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/box/seaborn/example_sns_box.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/legacy_candlestick_example.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/mplfinance_candlestick_example.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_daily_current_year.csv +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_daily_mixed_years.csv +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_hourly.csv +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_minute.csv +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/test_data_weekly.csv +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/candle_stick/volcandat.csv +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/count/example_count_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/count/seaborn/example_sns_count_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/dodged/matplotlib/example_mpl_dodged.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/dodged/seaborn/example_sns_dodged.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/facet-subplots/matplotlib/example_mpl_facet_bar_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/facet-subplots/matplotlib/example_mpl_facet_combined_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/facet-subplots/seaborn/example_sns_facet_bar_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/facet-subplots/seaborn/example_sns_facet_combined_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/flask/test_flask_app.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/heatmap/example_heatmap_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/heatmap/matplotlib/example_mpl_heatmap.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/heatmap/seaborn/example_sns_heatmap.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/histogram/example_histogram_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/histogram/matplotlib/example_mpl_hist.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/histogram/matplotlib/histogram_with_kde_matplotlib.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/histogram/seaborn/example_sns_hist.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/histogram/seaborn/histogram_with_kde_seaborn.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/kde/example_kde_plots.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/kde/matplotlib/multiple_kde_matplotlib.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/kde/matplotlib/single_kde_matplotlib.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/kde/seaborn/multiple_kde_seaborn.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/kde/seaborn/single_kde_seaborn.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/line/example_line_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/line/matplotlib/example_mpl_line.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/line/seaborn/example_sns_line.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/multilayer/example_mpl_multilayer.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/multilayer/example_multilayer_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/multiline/example_multiline_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/multiline/matplotlib/example_mpl_multiline.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/multiline/seaborn/example_sns_multiline.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/multipanel/example_multipanel_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/multipanel/matplotlib/example_mpl_multipanel.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/multipanel/seaborn/example_sns_multipanel.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/quarto/demo.qmd +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/reg/example_reg_plots.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/reg/matplotlib/example_matplotlib_smooth_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/reg/seaborn/example_sns_reg.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/scatter/example_scatter_plot.ipynb +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/scatter/matplotlib/example_mpl_scatter.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/scatter/seaborn/example_sns_scatter.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/shiny/example_shiny_scatter.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/stacked/matplotlib/example_mpl_stacked.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/stacked/seaborn/example_sns_stacked.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/example/streamlit/example_streamlit_app.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/api.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/context_manager.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/library.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/maidr_key.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/plot_type.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/enum/smooth_keywords.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/figure_manager.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/maidr.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/barplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/boxplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/grouped_barplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/heatmap.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/histogram.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/lineplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/maidr_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/maidr_plot_factory.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/mplfinance_barplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/mplfinance_lineplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/regplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/core/plot/scatterplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/exception/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/exception/extraction_error.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/barplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/boxplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/candlestick.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/clear.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/common.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/heatmap.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/highlight.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/histogram.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/kdeplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/lineplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/mplfinance.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/regplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/patch/scatterplot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/dedup_utils.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/environment.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/mixin/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/mixin/extractor_mixin.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/mixin/merger_mixin.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/plot_detection.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/regression_line_utils.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/util/svg_utils.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/widget/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/maidr/widget/shiny.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/conftest.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/core/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/core/enum/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/core/plot/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/core/test_figure_manager.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/core/test_maidr_plot.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/core/test_maidr_plot_factory.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/fixture/__init__.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/fixture/library_factory.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/fixture/matplotlib_factory.py +0 -0
- {maidr-1.9.0 → maidr-1.10.0}/tests/fixture/seaborn_factory.py +0 -0
- {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
|
|
@@ -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,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
|
-
|
|
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
|
|
21
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
#
|
|
98
|
-
if self.
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
10
|
-
and provides intelligent date/time formatting for mplfinance plots.
|
|
9
|
+
Datetime converter for mplfinance plots.
|
|
11
10
|
|
|
12
|
-
This utility
|
|
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
|
|
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: "
|
|
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: "
|
|
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
|
|
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
|
-
|
|
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) # "
|
|
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
|
-
|
|
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
|
|
201
|
+
Formatted datetime string in ISO format.
|
|
206
202
|
|
|
207
203
|
Notes
|
|
208
204
|
-----
|
|
209
|
-
|
|
210
|
-
-
|
|
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
|
-
|
|
215
|
-
|
|
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}"
|
|
@@ -1876,7 +1876,7 @@ wheels = [
|
|
|
1876
1876
|
|
|
1877
1877
|
[[package]]
|
|
1878
1878
|
name = "maidr"
|
|
1879
|
-
version = "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" },
|