PySide6Plot 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,213 @@
1
+ from qfluentwidgets import FluentIcon, Action, RoundMenu, MessageBox
2
+ import pyqtgraph as pg
3
+ from ..widgets.q_plot_widget import QPlotWidget
4
+ from ..widgets.value_select_box import select_str, select_limited_str
5
+ from ..widgets.removable_table import RemovableTable
6
+
7
+
8
+ class SavedFrameTable(RemovableTable):
9
+ """
10
+ Table widget for displaying saved frames.
11
+ """
12
+
13
+ def __init__(self, parent=None):
14
+ super().__init__(
15
+ parent=parent,
16
+ show_row_index=False,
17
+ resize_columns_to_contents=False,
18
+ show_move_up_down_buttons=False,
19
+ show_delete_button=True,
20
+ show_rename_button=True,
21
+ accept_name="Jump to",
22
+ accept_icon=FluentIcon.CHEVRON_RIGHT,
23
+ link_default_slot=True,
24
+ )
25
+ self.set_header_labels(["Name", "Location", "Range"])
26
+ self.setColumnWidth(0, 100)
27
+ self.setColumnWidth(1, 150)
28
+ self.setColumnWidth(2, 150)
29
+
30
+
31
+ class FrameRecorderComponent:
32
+ """
33
+ A Component for recording and managing frames in a plot widget.
34
+ """
35
+
36
+ def __init__(self, plot_widget: QPlotWidget, parent=None) -> None:
37
+ """
38
+ Initialize the FrameRecorderComponent.
39
+
40
+ Args:
41
+ plot_widget (QPlotWidget): The plot widget to record frames from.
42
+ parent (QWidget, optional): The parent widget. Defaults to None.
43
+ """
44
+ self.parent = parent
45
+ self.plot_widget = plot_widget
46
+ view_rect = self.plot_widget.viewRect()
47
+ self.current_x_loc = view_rect.left()
48
+ self.current_x_range = view_rect.width()
49
+ self.record_current_frame_change = True
50
+ self.previous_frame = []
51
+ self.saved_frame = []
52
+ self.num_previous_frame = 30
53
+ self.full_range_action = Action(FluentIcon.FULL_SCREEN, "Full range")
54
+ self.latest_frame_action = Action(FluentIcon.RIGHT_ARROW, "Latest")
55
+ self.previous_frame_action = Action(FluentIcon.CANCEL, "Previous frame")
56
+ self.given_frame_action = Action(FluentIcon.LABEL, "Input coordinate")
57
+ self.record_current_frame_action = Action(FluentIcon.ADD_TO, "Record current frame")
58
+ self.jump_to_menu = RoundMenu("Jump to", self.parent)
59
+ self.jump_to_menu.setIcon(FluentIcon.CHEVRON_RIGHT)
60
+ self.jump_to_menu.addAction(self.previous_frame_action)
61
+ self.jump_to_menu.addAction(self.full_range_action)
62
+ self.jump_to_menu.addAction(self.latest_frame_action)
63
+ self.jump_to_menu.addAction(self.given_frame_action)
64
+ self.saved_frame_table = SavedFrameTable(self.parent)
65
+ self.saved_frame_table.sigAcceptClicked.connect(self.__on_table_jump_to_clicked)
66
+ self.saved_frame_table.sigRowClicked.connect(self.__on_table_jump_to_clicked)
67
+ self.saved_frame_table.sigNameEdited.connect(self.__on_table_name_changed)
68
+ self.saved_frame_table.sigDeleteClicked.connect(self.__on_table_row_deleted)
69
+ self.saved_frame_table.hide()
70
+
71
+ self.__init_connections()
72
+
73
+ def __init_connections(self):
74
+ """
75
+ Initialize the signal connections.
76
+ """
77
+ self.full_range_action.triggered.connect(
78
+ lambda: self.plot_widget.update_plot(x_loc=self.plot_widget.x_start, x_range=self.plot_widget.x_range_max)
79
+ )
80
+ self.latest_frame_action.triggered.connect(
81
+ lambda: self.plot_widget.update_plot(
82
+ x_loc=self.plot_widget.x_end - self.plot_widget.viewRect().width(),
83
+ x_range=self.plot_widget.viewRect().width(),
84
+ )
85
+ )
86
+ self.previous_frame_action.triggered.connect(self.__on_previous_frame_clicked)
87
+ self.given_frame_action.triggered.connect(self.__on_given_frame_clicked)
88
+ self.record_current_frame_action.triggered.connect(self.__on_record_current_frame_clicked)
89
+ self.pg_plotter_view_changed_slot = pg.SignalProxy(
90
+ self.plot_widget.sigRangeChanged, rateLimit=100, slot=self.__on_pg_plotter_view_changed
91
+ )
92
+
93
+ def __on_pg_plotter_view_changed(self, event):
94
+ """
95
+ Handle the event when the plot view changes.
96
+
97
+ Args:
98
+ event: The event object.
99
+
100
+ Returns:
101
+ None
102
+ """
103
+ if self.record_current_frame_change: # move plot will also trigger this event
104
+ view_rect = self.plot_widget.viewRect()
105
+ if view_rect.left() != self.current_x_loc or view_rect.width() != self.current_x_range:
106
+ self.current_x_loc = view_rect.left()
107
+ self.current_x_range = view_rect.width()
108
+ if len(self.previous_frame) == self.num_previous_frame:
109
+ self.previous_frame.pop(0)
110
+ self.previous_frame.append((self.current_x_loc, self.current_x_range))
111
+ else:
112
+ self.previous_frame.append((self.current_x_loc, self.current_x_range))
113
+ if len(self.previous_frame) > 1:
114
+ self.previous_frame_action.setVisible(True)
115
+ self.record_current_frame_change = True
116
+
117
+ def __on_previous_frame_clicked(self):
118
+ """
119
+ Move the plot to the previous frame and update the previous_frame list.
120
+ If there is only one frame left in the previous_frame list, hide the 'previous_frame' option in the context menu.
121
+ """
122
+ if len(self.previous_frame) > 1:
123
+ self.record_current_frame_change = False
124
+ self.plot_widget.update_plot(x_loc=self.previous_frame[-2][0], x_range=self.previous_frame[-2][1])
125
+ self.previous_frame.pop(-1)
126
+ if len(self.previous_frame) == 1:
127
+ self.previous_frame_action.setVisible(False)
128
+
129
+ def __on_record_current_frame_clicked(self):
130
+ """
131
+ Records the current frame by prompting the user to input a frame name and saves it to the `saved_frame` list.
132
+ Updates the saved frame menu afterwards.
133
+ """
134
+ frame_name = select_str(self.parent.window(), "Input frame name", f"saved_frame_{len(self.saved_frame) + 1}")
135
+ if frame_name is not None:
136
+ view_rect = self.plot_widget.viewRect()
137
+ self.saved_frame.append([frame_name, view_rect.left(), view_rect.width()])
138
+ self.saved_frame_table.add_row_data(
139
+ [frame_name, self.plot_widget.getAxis("bottom").tick_str(view_rect.left()), str(view_rect.width())]
140
+ )
141
+
142
+ def __on_given_frame_clicked(self):
143
+ """
144
+ Handle the event when a frame is clicked.
145
+
146
+ This method prompts the user to input a coordinate and moves the plot accordingly.
147
+
148
+ Returns:
149
+ None
150
+ """
151
+ if self.plot_widget.getAxis("bottom").plot_strs is None:
152
+ MessageBox(
153
+ "Error", content="There is no support coordinate system in the plot!", parent=self.parent.window()
154
+ ).exec()
155
+ else:
156
+ items = list(self.plot_widget.getAxis("bottom").plot_strs.values())
157
+ select_x = select_limited_str(self.parent.window(), "Input coordinate", items)
158
+ if select_x is not None:
159
+ self.plot_widget.update_plot(
160
+ x_loc=items.index(select_x) - self.plot_widget.x_range_min / 2, x_range=self.plot_widget.x_range_min
161
+ )
162
+
163
+ def __on_table_jump_to_clicked(self, row: int):
164
+ """
165
+ Handle the event when a frame is clicked.
166
+
167
+ This method moves the plot to the selected frame.
168
+
169
+ Args:
170
+ row (int): The row index of the selected frame.
171
+
172
+ Returns:
173
+ None
174
+ """
175
+ self.plot_widget.update_plot(x_loc=self.saved_frame[row][1], x_range=self.saved_frame[row][2])
176
+
177
+ def __on_table_name_changed(self, row: int, new_name: str):
178
+ """
179
+ Handle the event when the name of a frame is changed.
180
+
181
+ Args:
182
+ row (int): The row index of the frame.
183
+ new_name (str): The new name for the frame.
184
+
185
+ Returns:
186
+ None
187
+ """
188
+ if new_name != "":
189
+ self.saved_frame[row][0] = new_name
190
+ self.saved_frame_table.set_row_data_item(row, 0, new_name)
191
+ else:
192
+ self.saved_frame_table.set_row_data_item(row, 0, self.saved_frame[row][0])
193
+
194
+ def __on_table_row_deleted(self, row: int):
195
+ """
196
+ Handle the event when a frame is deleted.
197
+
198
+ Args:
199
+ row (int): The row index of the frame to be deleted.
200
+
201
+ Returns:
202
+ None
203
+ """
204
+ self.saved_frame.pop(row)
205
+
206
+ def get_widget(self):
207
+ """
208
+ Get the widget associated with the FrameRecorderComponent.
209
+
210
+ Returns:
211
+ QWidget: The widget associated with the FrameRecorderComponent.
212
+ """
213
+ return self.saved_frame_table
@@ -0,0 +1,160 @@
1
+ from pyqtgraph import SignalProxy
2
+ from ..widgets.q_plot_widget import QPlotWidget
3
+ from ..widgets.zoom_bar import ZoomBar
4
+ from ..widgets.fluent_scroller import HorizontalFluentScroller, VerticalFluentScroller
5
+ from ..libs.constant import ZOOM_MODEL, YLOC_MODEL
6
+
7
+
8
+ class StockWidgetZoomBar(ZoomBar):
9
+ """
10
+ A custom zoom bar widget for the stock plot widget.
11
+
12
+ Args:
13
+ plot_widget (QPlotWidget): The parent plot widget.
14
+ parent (QWidget): The parent widget.
15
+
16
+ Attributes:
17
+ plot_widget (QPlotWidget): The parent plot widget.
18
+ current_value (float): The current value of the plot widget's view rectangle width.
19
+ """
20
+
21
+ def __init__(self, plot_widget: QPlotWidget, parent=None) -> None:
22
+ super().__init__(parent=parent, use_opacity_effect=True)
23
+ self.plot_widget = plot_widget
24
+ self.current_value = plot_widget.viewRect().width()
25
+
26
+ def update_position():
27
+ self.setGeometry(
28
+ self.plot_widget.geometry().width() - 300 - 20, self.plot_widget.geometry().y() + 10, 300, 40
29
+ )
30
+
31
+ update_position()
32
+ self.plot_widget.sigSizeChanged.connect(update_position)
33
+
34
+ def update_boundings():
35
+ self.current_value = self.plot_widget.viewRect().width()
36
+ self.update_min_max_value(min_v=self.plot_widget.x_range_min, max_v=self.plot_widget.x_range_max)
37
+
38
+ update_boundings()
39
+ self.plot_widget.sigBoundingUpdated.connect(update_boundings)
40
+
41
+ def update_widget():
42
+ self.update_widget(value=self.plot_widget.viewRect().width())
43
+
44
+ update_widget()
45
+ self.plotter_view_changed_slot = SignalProxy(self.plot_widget.sigRangeChanged, rateLimit=50, slot=update_widget)
46
+
47
+ def apply_value_func(self, value):
48
+ """
49
+ Apply the given value to the plot widget's x range.
50
+
51
+ Args:
52
+ value (float): The value to be applied.
53
+
54
+ Returns:
55
+ None
56
+
57
+ """
58
+ # x_loc = (self.plot_widget.viewRect().right()+self.plot_widget.viewRect().left())/2-value/2
59
+ self.plot_widget.update_plot(x_range=value)
60
+
61
+
62
+ class StockWidgetHorizontalScroller(HorizontalFluentScroller):
63
+ """
64
+ A custom horizontal scroller widget for controlling the movement of a plot widget.
65
+
66
+ Args:
67
+ plot_widget (QPlotWidget): The plot widget to be controlled.
68
+ parent: The parent widget.
69
+ """
70
+
71
+ def __init__(self, plot_widget: QPlotWidget, parent: None) -> None:
72
+ super().__init__(parent)
73
+ self.plot_widget = plot_widget
74
+ self.move_unit_ratio = 0.02
75
+ self.move_unit_min = 1
76
+ self.move_from_update = False
77
+ self.update_location()
78
+ self.plot_widget.sigBoundingUpdated.connect(self.update_location)
79
+ self.plotter_view_changed_slot = SignalProxy(
80
+ self.plot_widget.sigRangeChanged, rateLimit=50, slot=self.update_location
81
+ )
82
+ self.valueChanged.connect(self.on_value_changed)
83
+
84
+ def update_location(self):
85
+ """
86
+ Update the location and range of the scroller based on the plot widget's view.
87
+ """
88
+ self.move_unit = max(self.plot_widget.viewRect().width() * self.move_unit_ratio, self.move_unit_min)
89
+ self.setRange(
90
+ 0,
91
+ int(
92
+ (self.plot_widget.x_end - self.plot_widget.x_start - self.plot_widget.viewRect().width())
93
+ / self.move_unit
94
+ ),
95
+ )
96
+ self.move_from_update = True
97
+ self.setValue(int((self.plot_widget.viewRect().left() - self.plot_widget.x_start) / self.move_unit))
98
+ self.move_from_update = False
99
+
100
+ def on_value_changed(self):
101
+ """
102
+ Handle the value changed signal of the scroller and update the plot widget's plot accordingly.
103
+ """
104
+ if not self.move_from_update:
105
+ self.plot_widget.update_plot(x_loc=self.value() * self.move_unit + self.plot_widget.x_start)
106
+
107
+
108
+ class StockWidgetVerticalScroller(VerticalFluentScroller):
109
+ """
110
+ A custom vertical scroller widget for controlling the y-axis movement of a plot widget.
111
+
112
+ Args:
113
+ plot_widget (QPlotWidget): The plot widget to control.
114
+ parent (None): The parent widget.
115
+ """
116
+
117
+ def __init__(self, plot_widget: QPlotWidget, parent: None) -> None:
118
+ super().__init__(parent)
119
+ self.plot_widget = plot_widget
120
+ self.move_unit_ratio = 0.02
121
+ self.move_unit_min = 1
122
+ self.move_from_update = False
123
+ self.update_location()
124
+ self.on_zoom_loc_model_changed()
125
+ self.plot_widget.sigZoomModelChanged.connect(self.on_zoom_loc_model_changed)
126
+ self.plot_widget.sigYLocModelChanged.connect(self.on_zoom_loc_model_changed)
127
+ self.plot_widget.sigBoundingUpdated.connect(self.update_location)
128
+ self.plotter_view_changed_slot = SignalProxy(
129
+ self.plot_widget.sigRangeChanged, rateLimit=50, slot=self.update_location
130
+ )
131
+ self.valueChanged.connect(self.on_value_changed)
132
+
133
+ def update_location(self):
134
+ """
135
+ Update the location and range of the scroller based on the plot widget's current state.
136
+ """
137
+ self.move_unit = max(self.plot_widget.viewRect().height() * self.move_unit_ratio, self.move_unit_min)
138
+ maximum_y = int(
139
+ (self.plot_widget.y_end - self.plot_widget.y_start - self.plot_widget.viewRect().height()) / self.move_unit
140
+ )
141
+ self.setRange(0, maximum_y)
142
+ self.move_from_update = True
143
+ self.setValue(maximum_y - int((self.plot_widget.viewRect().top() - self.plot_widget.y_start) / self.move_unit))
144
+ self.move_from_update = False
145
+
146
+ def on_value_changed(self):
147
+ """
148
+ Handle the value changed signal of the scroller and update the plot widget's y-axis location accordingly.
149
+ """
150
+ if not self.move_from_update:
151
+ self.plot_widget.move_y_loc((self.maximum() - self.value()) * self.move_unit + self.plot_widget.y_start)
152
+
153
+ def on_zoom_loc_model_changed(self):
154
+ """
155
+ Handle the zoom and y-axis location model changed signals of the plot widget and enable/disable the scroller accordingly.
156
+ """
157
+ if self.plot_widget.zoom_model != ZOOM_MODEL.AUTO_RANGE and self.plot_widget.y_loc_model == YLOC_MODEL.FREE:
158
+ self.setEnabled(True)
159
+ else:
160
+ self.setEnabled(False)
@@ -0,0 +1,3 @@
1
+ """
2
+ Utils and helper functions for qstock_plotter
3
+ """
@@ -0,0 +1,19 @@
1
+ from .helpers import GeneralDataClass
2
+
3
+ ZOOM_MODEL = GeneralDataClass(
4
+ AUTO_RANGE=0,
5
+ FIXED_RATIO=1,
6
+ FIXED_YRANGE=2,
7
+ )
8
+
9
+ YLOC_MODEL = GeneralDataClass(
10
+ FREE=0, # allow free move, y_loc is changed as while y_center is not changed
11
+ DATA_CENTERED=1, # doesn't allow free move, y_loc is always the center of the data
12
+ FIXED=2, # dosen't allow free move, y_loc is not changed
13
+ )
14
+
15
+ SCALE_LOC_MODEL = GeneralDataClass(
16
+ CENTRAL=0,
17
+ LEFT=1,
18
+ RIGHT=2,
19
+ )
@@ -0,0 +1,230 @@
1
+ import pandas as pd
2
+ from dataclasses import dataclass
3
+ from copy import deepcopy
4
+
5
+
6
+ class ChildDataFrame:
7
+ """
8
+ A class representing a child DataFrame.
9
+
10
+ Attributes:
11
+ data_frame (pd.DataFrame): The parent DataFrame.
12
+ data_keys (list): The key(s) of the data column(s) in the parent DataFrame.
13
+ max_y_key (str): The key of the maximum y-value column.
14
+ min_y_key (str): The key of the minimum y-value column.
15
+ x_ticks (dict): A dictionary mapping index values to x-labels.
16
+ __index_start (int): The starting index of the child DataFrame.
17
+
18
+ Methods:
19
+ get_min_x(): Returns the minimum x-value in the parent DataFrame.
20
+ get_max_x(): Returns the maximum x-value in the parent DataFrame.
21
+ get_local_range(x_start, x_end): Returns the local range of y-values between x_start and x_end.
22
+ get_x_ticks(): Returns the x-ticks dictionary.
23
+ __len__(): Returns the length of the child DataFrame.
24
+ __getitem__(idx): Returns a tuple of data values at the given index.
25
+
26
+ """
27
+
28
+ def __init__(
29
+ self, data_frame: pd.DataFrame, data_keys: str | list, max_y_key=None, min_y_key=None, x_label_key=None
30
+ ) -> None:
31
+ """
32
+ Initialize the DataHandler object.
33
+
34
+ Args:
35
+ data_frame (pd.DataFrame): The parent DataFrame containing the data.
36
+ data_keys (Union[str, list]): The key(s) to access the data in the parent DataFrame.
37
+ max_y_key (str, optional): The key to access the maximum y-value data. Defaults to None.
38
+ min_y_key (str, optional): The key to access the minimum y-value data. Defaults to None.
39
+ x_label_key (str, optional): The key to access the x-label data. Defaults to None.
40
+ """
41
+ data_keys = data_keys if isinstance(data_keys, list) else [data_keys]
42
+ self.data_keys = deepcopy(data_keys)
43
+ if max_y_key is not None:
44
+ if max_y_key not in data_keys:
45
+ data_keys.append(max_y_key)
46
+ else:
47
+ max_y_key = data_keys[0]
48
+ self.max_y_key = max_y_key
49
+ if min_y_key is not None:
50
+ if min_y_key not in data_keys:
51
+ data_keys.append(min_y_key)
52
+ else:
53
+ min_y_key = data_keys[0]
54
+ self.min_y_key = min_y_key
55
+ x_label_key = x_label_key if x_label_key is not None else "date"
56
+ if x_label_key not in data_keys:
57
+ data_keys.append(x_label_key)
58
+ self.data_frame = data_frame[data_keys]
59
+ self.x_ticks: dict = {i: str(self.data_frame[x_label_key][i]) for i in self.data_frame.index}
60
+ self.__index_start = self.get_min_x()
61
+
62
+ def get_min_x(self):
63
+ """
64
+ Returns the minimum x-value in the parent DataFrame.
65
+
66
+ Returns:
67
+ int: The minimum x-value.
68
+
69
+ """
70
+ return self.data_frame.index[0]
71
+
72
+ def get_max_x(self):
73
+ """
74
+ Returns the maximum x-value in the parent DataFrame.
75
+
76
+ Returns:
77
+ int: The maximum x-value.
78
+
79
+ """
80
+ return self.data_frame.index[-1]
81
+
82
+ def get_local_range(self, x_start, x_end):
83
+ """
84
+ Returns the local range of y-values between x_start and x_end.
85
+
86
+ Args:
87
+ x_start (int): The starting x-value.
88
+ x_end (int): The ending x-value.
89
+
90
+ Returns:
91
+ tuple: A tuple containing the minimum and maximum y-values.
92
+
93
+ """
94
+ x_start = int(x_start)
95
+ x_end = int(x_end)
96
+ return pd.to_numeric(self.data_frame.loc[x_start:x_end, self.min_y_key]).min(), pd.to_numeric(
97
+ self.data_frame.loc[x_start:x_end, self.max_y_key]
98
+ ).max()
99
+
100
+ def get_x_ticks(self):
101
+ """
102
+ Returns the x-ticks dictionary.
103
+
104
+ Returns:
105
+ dict: A dictionary mapping index values to x-labels.
106
+
107
+ """
108
+ return self.x_ticks
109
+
110
+ def __len__(self):
111
+ """
112
+ Returns the length of the child DataFrame.
113
+
114
+ Returns:
115
+ int: The length of the child DataFrame.
116
+
117
+ """
118
+ return len(self.data_frame)
119
+
120
+ def __getitem__(self, idx):
121
+ """
122
+ Returns a tuple of data values at the given index.
123
+
124
+ Args:
125
+ idx (int): The index.
126
+
127
+ Returns:
128
+ tuple: A tuple containing the index value and data values.
129
+
130
+ """
131
+
132
+ return tuple([self.data_frame.index[idx]]) + tuple(
133
+ pd.to_numeric(self.data_frame[key][self.__index_start + idx], errors="coerce") for key in self.data_keys
134
+ )
135
+
136
+
137
+ class PricesDataFrame(ChildDataFrame):
138
+ """
139
+ A class representing a DataFrame containing prices data.
140
+
141
+ Parameters:
142
+ data_frame (pd.DataFrame): The parent DataFrame containing the prices data.
143
+ """
144
+
145
+ def __init__(self, data_frame: pd.DataFrame):
146
+ super().__init__(
147
+ data_frame,
148
+ data_keys=["open", "close", "high", "low"],
149
+ max_y_key="high",
150
+ min_y_key="low",
151
+ x_label_key="date",
152
+ )
153
+
154
+
155
+ class VolumeDataFrame(ChildDataFrame):
156
+ """
157
+ A class representing a volume data frame.
158
+
159
+ This class inherits from the ChildDataFrame class and provides additional functionality
160
+ for handling volume data.
161
+
162
+ Args:
163
+ data_frame (pd.DataFrame): The parent data frame from which the volume data frame is derived.
164
+
165
+ Attributes:
166
+ data_keys (list): A list of data keys for the volume data.
167
+ x_label_key (str): The key for the x-axis label in the volume data frame.
168
+
169
+ """
170
+
171
+ def __init__(self, data_frame: pd.DataFrame):
172
+ super().__init__(data_frame, data_keys=["volume"], x_label_key="date")
173
+
174
+
175
+ @dataclass
176
+ class TradeData:
177
+ prices: PricesDataFrame
178
+ volume: VolumeDataFrame
179
+
180
+
181
+ class DataHandler:
182
+ def __init__(self):
183
+ self.day_data: TradeData = None
184
+ self.week_data: TradeData = None
185
+ self.month_data: TradeData = None
186
+
187
+ def save(self, hdf5_path):
188
+ for key, data in zip(
189
+ ["day_data", "week_data", "month_data"], [self.day_data, self.week_data, self.month_data], strict=False
190
+ ):
191
+ price = data.prices.data_frame
192
+ volume = data.volume.data_frame
193
+ volume = volume.drop(columns=["date"])
194
+ df = price.join(volume)
195
+ df.to_hdf(hdf5_path, key=key)
196
+
197
+ def load(self, hdf5_path):
198
+ # self.__df_day = pd.read_hdf(hdf5_path, key="day_data")
199
+ # self.__df_week = pd.read_hdf(hdf5_path, key="week_data")
200
+ # self.__df_month = pd.read_hdf(hdf5_path, key="month_data")
201
+ self.day_data = self._load_data(hdf5_path, "day_data")
202
+ self.week_data = self._load_data(hdf5_path, "week_data")
203
+ self.month_data = self._load_data(hdf5_path, "month_data")
204
+
205
+ def _load_data(self, path, key):
206
+ try:
207
+ df = pd.read_hdf(path, key=key)
208
+ except Exception as e:
209
+ print(f"Error loading data from {path} with key {key}: {e}")
210
+ return None
211
+ return TradeData(PricesDataFrame(df), VolumeDataFrame(df))
212
+
213
+
214
+ class HDF5Handler(DataHandler):
215
+ """
216
+ A class that handles candlestick data from an HDF5 file.
217
+
218
+ Attributes:
219
+ day_data (TradeData): Candlestick data for daily intervals.
220
+ week_data (TradeData): Candlestick data for weekly intervals.
221
+ month_data (TradeData): Candlestick data for monthly intervals.
222
+
223
+ Methods:
224
+ __init__(hdf5_path): Initializes the HDF5Handler object.
225
+
226
+ """
227
+
228
+ def __init__(self, hdf5_path: str) -> None:
229
+ super().__init__()
230
+ self.load(hdf5_path)