seolpyo-mplchart 2.0.0.3__py3-none-any.whl → 2.1.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.
Files changed (54) hide show
  1. seolpyo_mplchart/__init__.py +17 -133
  2. seolpyo_mplchart/_chart/__init__.py +39 -31
  3. seolpyo_mplchart/_chart/base/__init__.py +111 -0
  4. seolpyo_mplchart/_chart/base/a_canvas.py +250 -0
  5. seolpyo_mplchart/_chart/base/b_artist.py +143 -0
  6. seolpyo_mplchart/_chart/base/c_draw.py +100 -0
  7. seolpyo_mplchart/_chart/base/d_segment.py +262 -0
  8. seolpyo_mplchart/_chart/base/e_axis.py +267 -0
  9. seolpyo_mplchart/_chart/base/f_background.py +62 -0
  10. seolpyo_mplchart/_chart/base/g_event.py +66 -0
  11. seolpyo_mplchart/_chart/base/h_data.py +138 -0
  12. seolpyo_mplchart/_chart/base/test.py +58 -0
  13. seolpyo_mplchart/_chart/cursor/__init__.py +125 -0
  14. seolpyo_mplchart/_chart/cursor/b_artist.py +130 -0
  15. seolpyo_mplchart/_chart/cursor/c_draw.py +96 -0
  16. seolpyo_mplchart/_chart/cursor/d_segment.py +359 -0
  17. seolpyo_mplchart/_chart/cursor/e_axis.py +65 -0
  18. seolpyo_mplchart/_chart/cursor/g_event.py +233 -0
  19. seolpyo_mplchart/_chart/cursor/h_data.py +61 -0
  20. seolpyo_mplchart/_chart/cursor/test.py +69 -0
  21. seolpyo_mplchart/_chart/slider/__init__.py +169 -0
  22. seolpyo_mplchart/_chart/slider/a_canvas.py +260 -0
  23. seolpyo_mplchart/_chart/slider/b_artist.py +91 -0
  24. seolpyo_mplchart/_chart/slider/c_draw.py +54 -0
  25. seolpyo_mplchart/_chart/slider/d_segment.py +166 -0
  26. seolpyo_mplchart/_chart/slider/e_axis.py +70 -0
  27. seolpyo_mplchart/_chart/slider/f_background.py +37 -0
  28. seolpyo_mplchart/_chart/slider/g_event.py +353 -0
  29. seolpyo_mplchart/_chart/slider/h_data.py +102 -0
  30. seolpyo_mplchart/_chart/slider/test.py +71 -0
  31. seolpyo_mplchart/_config/candle.py +1 -0
  32. seolpyo_mplchart/_config/figure.py +3 -4
  33. seolpyo_mplchart/_config/ma.py +2 -0
  34. seolpyo_mplchart/_config/slider/config.py +2 -2
  35. seolpyo_mplchart/_config/slider/figure.py +3 -4
  36. seolpyo_mplchart/_config/slider/nav.py +3 -2
  37. seolpyo_mplchart/_config/volume.py +1 -0
  38. seolpyo_mplchart/_utils/__init__.py +10 -0
  39. seolpyo_mplchart/_utils/nums.py +67 -0
  40. seolpyo_mplchart/_utils/theme/__init__.py +15 -0
  41. seolpyo_mplchart/_utils/theme/dark.py +57 -0
  42. seolpyo_mplchart/_utils/theme/light.py +56 -0
  43. seolpyo_mplchart/_utils/utils.py +28 -0
  44. seolpyo_mplchart/_utils/xl/__init__.py +15 -0
  45. seolpyo_mplchart/_utils/xl/csv.py +46 -0
  46. seolpyo_mplchart/_utils/xl/xlsx.py +49 -0
  47. seolpyo_mplchart/sample/apple.txt +6058 -0
  48. seolpyo_mplchart/sample/samsung.txt +5938 -0
  49. seolpyo_mplchart/test.py +5 -5
  50. {seolpyo_mplchart-2.0.0.3.dist-info → seolpyo_mplchart-2.1.0.dist-info}/METADATA +21 -13
  51. seolpyo_mplchart-2.1.0.dist-info/RECORD +89 -0
  52. seolpyo_mplchart-2.0.0.3.dist-info/RECORD +0 -50
  53. {seolpyo_mplchart-2.0.0.3.dist-info → seolpyo_mplchart-2.1.0.dist-info}/WHEEL +0 -0
  54. {seolpyo_mplchart-2.0.0.3.dist-info → seolpyo_mplchart-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,260 @@
1
+ import matplotlib.pyplot as plt
2
+ from matplotlib.axes import Axes
3
+ from matplotlib.collections import LineCollection
4
+ from matplotlib.text import Text
5
+
6
+ from ..._config import SLIDERCONFIG, SliderConfigData
7
+ from ..base.a_canvas import CanvasMixin as BaseMixin, Figure
8
+
9
+
10
+ class Base(BaseMixin):
11
+ CONFIG: SliderConfigData
12
+
13
+ def add_axes(self):
14
+ if not self.figure:
15
+ self.figure, *_ = plt.subplots(
16
+ 7, # row 수
17
+ figsize=self.CONFIG.FIGURE.figsize, # 기본 크기
18
+ height_ratios=(
19
+ 0,
20
+ 0,
21
+ 0,
22
+ self.CONFIG.FIGURE.RATIO.price,
23
+ self.CONFIG.FIGURE.RATIO.volume,
24
+ 0,
25
+ 0,
26
+ ) # row 크기 비율
27
+ )
28
+
29
+ self._ax_slider_top, self.ax_none_top, self.ax_legend, self.ax_price, self.ax_volume, self.ax_none_bottom, self._ax_slider_bottom = self.figure.axes
30
+
31
+ return
32
+
33
+
34
+ class FigureMixin(Base):
35
+ key_volume: str
36
+ ax_legend: Axes
37
+
38
+ slider_top = True
39
+
40
+ def _set_figure_ratios(self):
41
+ gs = self.figure.axes[0].get_subplotspec().get_gridspec()
42
+
43
+ ratio_volume = self.CONFIG.FIGURE.RATIO.volume
44
+ if not self.key_volume:
45
+ ratio_volume = 0
46
+
47
+ legend = self.ax_legend.get_legend()
48
+ if not legend:
49
+ if self.slider_top:
50
+ ratios = [
51
+ self.CONFIG.FIGURE.RATIO.slider,
52
+ 0,
53
+ 0,
54
+ self.CONFIG.FIGURE.RATIO.price, ratio_volume,
55
+ 0,
56
+ 0,
57
+ ]
58
+ else:
59
+ ratios = [
60
+ 0,
61
+ 0,
62
+ 0,
63
+ self.CONFIG.FIGURE.RATIO.price, ratio_volume,
64
+ self.CONFIG.FIGURE.RATIO.none,
65
+ self.CONFIG.FIGURE.RATIO.slider,
66
+ ]
67
+ else:
68
+ fig_heihgt = self.figure.get_figheight()
69
+ fig_px = fig_heihgt * (1-self.CONFIG.FIGURE.ADJUST.hspace*2) * self.figure.dpi
70
+ # print(f'{(fig_heihgt, fig_px)=}')
71
+
72
+ # Legend에 Axes 높이 맞추기
73
+ bbox = legend.get_window_extent().transformed(self.figure.transFigure.inverted())
74
+ ax_pos = self.ax_legend.get_position()
75
+ self.ax_legend.set_position([ax_pos.x0, ax_pos.y0, ax_pos.width, bbox.height])
76
+
77
+ legend_height = bbox.height
78
+ legend_px = legend.get_window_extent().height
79
+ # print(f'{(legend_height, legend_px)=}')
80
+
81
+ chart_px = fig_px - legend_height
82
+ chart_ratio = self.CONFIG.FIGURE.RATIO.price + ratio_volume
83
+
84
+ # print(f'{self.CONFIG.FIGURE.RATIO.__dict__=}')
85
+ ratio_none = 0 if self.slider_top else self.CONFIG.FIGURE.RATIO.none
86
+ chart_ratio += self.CONFIG.FIGURE.RATIO.slider
87
+ div_chart = chart_px / chart_ratio
88
+ price_px = div_chart * self.CONFIG.FIGURE.RATIO.price
89
+ volume_px = div_chart * ratio_volume
90
+ slider_px = div_chart * self.CONFIG.FIGURE.RATIO.slider
91
+ none_px = div_chart * ratio_none
92
+ # print(f'{none_px=}')
93
+
94
+ if self.slider_top:
95
+ # 차트 비율
96
+ ratios = [
97
+ slider_px,
98
+ 0,
99
+ legend_px * 3,
100
+ price_px, volume_px,
101
+ 0,
102
+ 0,
103
+ ]
104
+ else:
105
+ ratios = [
106
+ 0,
107
+ 0,
108
+ legend_px * 1.2,
109
+ price_px, volume_px,
110
+ none_px,
111
+ slider_px,
112
+ ]
113
+
114
+ # print(f'{ratios=}')
115
+ gs.set_height_ratios(ratios)
116
+
117
+ self.figure.tight_layout()
118
+
119
+ # 플롯간 간격 설정(Configure subplots)
120
+ self.figure.subplots_adjust(**self.CONFIG.FIGURE.ADJUST.__dict__)
121
+
122
+ return
123
+
124
+ def _set_figure(self):
125
+ self.figure.canvas.manager.set_window_title('Seolpyo MPLChart')
126
+
127
+ # print(f'{self.CONFIG.FIGURE.RATIO.volume=}')
128
+ # print(f'{gs.get_height_ratios()=}')
129
+
130
+ self._set_figure_ratios()
131
+
132
+ self.figure.set_facecolor(self.CONFIG.FIGURE.facecolor)
133
+ return
134
+
135
+
136
+ class AxesMixin(Base):
137
+ slider_top = True
138
+
139
+ ax_slider: Axes = None
140
+
141
+ collection_slider: LineCollection
142
+ collection_nav: LineCollection
143
+ collection_slider_vline: LineCollection
144
+ artist_text_slider: Text
145
+
146
+ def _set_axes(self):
147
+ super()._set_axes()
148
+
149
+ if self.slider_top:
150
+ ax_slider = self._ax_slider_top
151
+ else:
152
+ ax_slider = self._ax_slider_bottom
153
+
154
+ if self.ax_slider and ax_slider is not self.ax_slider:
155
+ # print('move artist')
156
+ # ax_slider 위치가 변경된 경우 artist 이동하기
157
+ artists: list[LineCollection|Text] = [
158
+ self.collection_slider,
159
+ self.collection_nav,
160
+ self.collection_slider_vline,
161
+ self.artist_text_slider,
162
+ ]
163
+ for artist in artists:
164
+ artist.remove()
165
+ artist.set_transform(ax_slider.transData)
166
+ ax_slider.add_artist(artist)
167
+ # axis
168
+ ax_slider.set_xlim(*self.ax_slider.get_xlim())
169
+ ax_slider.set_ylim(*self.ax_slider.get_ylim())
170
+ # tick label
171
+ ax_slider.set_xticks(self.ax_slider.get_xticks(minor=True), minor=True)
172
+ ax_slider.set_xticklabels(self.ax_slider.get_xticklabels(minor=True), minor=True)
173
+
174
+ self.ax_slider = ax_slider
175
+
176
+ self._ax_slider_top.set_label('top slider ax')
177
+ self._ax_slider_bottom.set_label('bottom slider ax')
178
+ self.ax_none_top.set_label('top none ax')
179
+ self.ax_none_bottom.set_label('bottom none ax')
180
+
181
+ self.ax_slider.set_label('slider ax')
182
+
183
+ for ax in (self.ax_none_top, self.ax_none_bottom):
184
+ ax.set_animated(True)
185
+ ax.set_axis_off()
186
+
187
+ self._set_axes_slider()
188
+
189
+ return
190
+
191
+ def _set_axes_slider(self):
192
+ # print(f'{self.slider_top=}')
193
+ formatter = lambda x, _: self.CONFIG.UNIT.func(
194
+ x,
195
+ word=self.CONFIG.UNIT.price,
196
+ digit=self.CONFIG.UNIT.digit+2
197
+ )
198
+ # 공통 설정
199
+ for ax in (self._ax_slider_top, self._ax_slider_bottom):
200
+ ax.yaxis.set_major_formatter(formatter)
201
+ # x tick 외부 눈금 표시하지 않기
202
+ ax.xaxis.set_ticks_position('none')
203
+ # x tick label 제거
204
+ ax.set_xticklabels([])
205
+ # y tick 눈금 표시하지 않기
206
+ ax.yaxis.set_ticks_position('none')
207
+
208
+ # 차트 영역 배경 색상
209
+ ax.set_facecolor(self.CONFIG.AX.facecolor)
210
+
211
+ # Axes 외곽선 색 변경(틱 색과 일치)
212
+ for i in ['top', 'bottom', 'left', 'right']:
213
+ ax.spines[i].set_color(self.CONFIG.AX.TICK.edgecolor)
214
+ # 틱 색상
215
+ ax.tick_params('both', colors=self.CONFIG.AX.TICK.edgecolor)
216
+ # 틱 라벨 색상
217
+ ticklabels: list[Text] = ax.get_xticklabels() + ax.get_yticklabels()
218
+ for ticklabel in ticklabels:
219
+ ticklabel.set_color(self.CONFIG.AX.TICK.fontcolor)
220
+
221
+ # Axes grid(구분선, 격자) 그리기
222
+ # 어째서인지 grid의 zorder 값을 선언해도 1.6을 값으로 한다.
223
+ ax.grid(**self.CONFIG.AX.GRID.__dict__)
224
+
225
+ # major tick mark 길이를 0으로 만들어 튀어나오지 않게 하기
226
+ ax.tick_params('x', which='major', length=0)
227
+ # minor tick mark 색상 변경
228
+ ax.tick_params('x', which='minor', colors=self.CONFIG.AX.TICK.edgecolor)
229
+ # 틱 라벨 색상
230
+ ticklabels: list[Text] = ax.get_xticklabels(minor=True) + ax.get_yticklabels()
231
+ for ticklabel in ticklabels:
232
+ ticklabel.set_color(self.CONFIG.AX.TICK.fontcolor)
233
+
234
+ ax.yaxis.set_ticks_position('right')
235
+ # 상단 슬라이더의 x tick 하단 설정
236
+ self._ax_slider_top.xaxis.set_ticks_position('bottom')
237
+ # 하단 슬라이더의 x tick 상단 설정
238
+ self._ax_slider_bottom.xaxis.set_ticks_position('top')
239
+
240
+ return
241
+
242
+
243
+ class CanvasMixin(FigureMixin, AxesMixin):
244
+ slider_top = True
245
+
246
+ figure: Figure
247
+
248
+ ax_legend: Axes
249
+ ax_price: Axes
250
+ ax_volume: Axes
251
+ ax_slider: Axes
252
+ ax_none: Axes
253
+ _ax_slider_top: Axes
254
+ _ax_slider_bottom: Axes
255
+
256
+ def __init__(self, config=SLIDERCONFIG):
257
+ super().__init__(config=config)
258
+ return
259
+
260
+
@@ -0,0 +1,91 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.collections import LineCollection
3
+ from matplotlib.text import Text
4
+
5
+ from ..._config import SliderConfigData
6
+
7
+
8
+ class Base:
9
+ CONFIG: SliderConfigData
10
+
11
+ ax_slider: Axes
12
+ ax_price: Axes
13
+ ax_volume: Axes
14
+ ax_legend: Axes
15
+
16
+ add_artists: callable
17
+ set_artists: callable
18
+
19
+
20
+ class CollectionMixin(Base):
21
+ def add_artists(self):
22
+ super().add_artists()
23
+
24
+ self.collection_slider = LineCollection([])
25
+ self.collection_nav = LineCollection([])
26
+ self.collection_slider_vline = LineCollection([])
27
+ self.artist_text_slider = Text('')
28
+
29
+ for artist in [
30
+ self.collection_slider,
31
+ self.collection_nav,
32
+ self.collection_slider_vline,
33
+ self.artist_text_slider
34
+ ]:
35
+ self.ax_slider.add_artist(artist)
36
+ return
37
+
38
+ def set_artists(self):
39
+ super().set_artists()
40
+
41
+ self._set_slider_artists()
42
+ self._set_slider_test()
43
+ return
44
+
45
+ def _set_slider_artists(self):
46
+ self.collection_slider.set_animated(True)
47
+ self.collection_slider.set_transform(self.ax_slider.transData)
48
+
49
+ color_overay = self.CONFIG.SLIDER.NAV.facecolor
50
+ color_line = self.CONFIG.SLIDER.NAV.edgecolor
51
+ colors = [color_overay, color_overay, color_line, color_line]
52
+ # print(f'{colors=}')
53
+ self.collection_nav.set_linewidth(0.1)
54
+ self.collection_nav.set_facecolor(colors)
55
+
56
+ # facecolor alpha값 제거
57
+ colors = []
58
+ for c in self.collection_nav.get_facecolor():
59
+ # print(c)
60
+ colors.append(c[:3])
61
+ self.collection_nav.set_facecolor(colors)
62
+
63
+ alpha = self.CONFIG.SLIDER.NAV.alpha
64
+ self.collection_nav.set_alpha([alpha, alpha, 1, 1])
65
+
66
+ self.collection_nav.set_edgecolor([(0, 0, 0, 0) for _ in colors])
67
+ self.collection_nav.set_animated(True)
68
+ self.collection_nav.set_transform(self.ax_slider.transData)
69
+
70
+ kwargs = self.CONFIG.CURSOR.CROSSLINE.__dict__
71
+ kwargs.update({'segments': [], 'animated': True})
72
+ self.collection_slider_vline.set(**kwargs)
73
+
74
+ kwargs = self.CONFIG.CURSOR.TEXT.to_dict()
75
+ kwargs.update({'text': ' ', 'animated': True})
76
+ self.artist_text_slider.set(**kwargs)
77
+ return
78
+
79
+ def _set_slider_test(self):
80
+ kwargs = self.CONFIG.CURSOR.TEXT.to_dict()
81
+ kwargs.update(animated=True, verticalalignment='top', horizontalalignment='center')
82
+ self.artist_text_slider.set(**kwargs)
83
+ return
84
+
85
+
86
+ class ArtistMixin(CollectionMixin):
87
+ collection_slider: LineCollection
88
+ collection_nav: LineCollection
89
+ collection_slider_vline: LineCollection
90
+ artist_text_slider: Text
91
+
@@ -0,0 +1,54 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.collections import LineCollection
3
+ from matplotlib.text import Text
4
+
5
+ from .a_canvas import Figure
6
+
7
+
8
+ class Base:
9
+ figure: Figure
10
+
11
+ ax_slider: Axes
12
+
13
+ collection_slider: LineCollection
14
+ collection_nav: LineCollection
15
+ collection_slider_vline: LineCollection
16
+ artist_text_slider: Text
17
+
18
+
19
+ class SliderMixin(Base):
20
+ def _draw_ax_slider(self):
21
+ renderer = self.figure.canvas.renderer
22
+
23
+ self.ax_slider.xaxis.draw(renderer)
24
+ self.ax_slider.yaxis.draw(renderer)
25
+
26
+ self.collection_slider.draw(renderer)
27
+ c = self.collection_slider.get_edgecolor()
28
+ # print(f'{c=}')
29
+ s = self.collection_slider.get_segments()
30
+ # print(f'{s=}')
31
+ return
32
+
33
+ def _draw_nav(self):
34
+ renderer = self.figure.canvas.renderer
35
+
36
+ self.collection_nav.draw(renderer)
37
+ return
38
+
39
+ def _draw_slider_vline(self):
40
+ renderer = self.figure.canvas.renderer
41
+
42
+ self.collection_slider_vline.draw(renderer)
43
+ return
44
+
45
+ def _draw_slider_text(self):
46
+ renderer = self.figure.canvas.renderer
47
+
48
+ self.artist_text_slider.draw(renderer)
49
+ return
50
+
51
+
52
+ class DrawMixin(SliderMixin):
53
+ pass
54
+
@@ -0,0 +1,166 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.backend_bases import MouseEvent
3
+ from matplotlib.collections import LineCollection
4
+ from matplotlib.text import Text
5
+ import pandas as pd
6
+ import numpy as np
7
+
8
+ from ..._config import SliderConfigData
9
+
10
+
11
+ class Base:
12
+ CONFIG: SliderConfigData
13
+ df: pd.DataFrame
14
+
15
+ ax_slider: Axes
16
+ collection_slider: LineCollection
17
+ collection_nav: LineCollection
18
+ collection_slider_vline: LineCollection
19
+ artist_text_slider: Text
20
+
21
+ set_segments: callable
22
+ set_color_segments: callable
23
+
24
+ slider_ymin: float
25
+ slider_ymax: float
26
+
27
+ slider_xmin: float
28
+ slider_xmax: float
29
+ _nav_width: float
30
+
31
+
32
+ class SliderMixin(Base):
33
+ def _set_slider_collection(self):
34
+ keys = []
35
+ for i in reversed(self.CONFIG.MA.ma_list):
36
+ keys.append('x')
37
+ keys.append(f'ma{i}')
38
+
39
+ series = self.df[keys + ['x', 'close']]
40
+ series['x'] = series['x'] - 0.5
41
+ segment_slider = series.values
42
+ sizes = [segment_slider.shape[0], len(self.CONFIG.MA.ma_list)+1, 2]
43
+ segment_slider = segment_slider.reshape(*sizes).swapaxes(0, 1)
44
+ self.collection_slider.set_segments(segment_slider)
45
+
46
+ linewidth = []
47
+ ma_colors = []
48
+ for n, _ in enumerate(self.CONFIG.MA.ma_list):
49
+ linewidth.append(0.9)
50
+ try:
51
+ ma_colors.append(self.CONFIG.MA.color_list[n])
52
+ except:
53
+ ma_colors.append(self.CONFIG.MA.color_default)
54
+
55
+ self.collection_slider.set_edgecolor(ma_colors + [self.CONFIG.CANDLE.line_color])
56
+
57
+ self.collection_slider.set_linewidth(linewidth + [2.4])
58
+ self.collection_slider.set_edgecolor(ma_colors + [self.CONFIG.CANDLE.line_color])
59
+ return
60
+
61
+ def _set_slider_color_collection(self):
62
+ ma_colors = []
63
+ for n, _ in enumerate(self.CONFIG.MA.ma_list):
64
+ try:
65
+ ma_colors.append(self.CONFIG.MA.color_list[n])
66
+ except:
67
+ ma_colors.append(self.CONFIG.MA.color_default)
68
+
69
+ self.collection_slider.set_edgecolor(ma_colors + [self.CONFIG.CANDLE.line_color])
70
+ return
71
+
72
+
73
+ class VlineMixin(Base):
74
+ in_slider: bool
75
+
76
+ def _set_slider_vline(self, e: MouseEvent):
77
+ xdata = e.xdata
78
+ if xdata is None:
79
+ return
80
+ xdata = round(xdata, 2)
81
+ # print(f'{xdata=}')
82
+ if not self.in_slider:
83
+ xdata -= 0.5
84
+
85
+ seg = [((xdata, self.slider_ymin), (xdata, self.slider_ymax))]
86
+ # print(f'{seg=}')
87
+ self.collection_slider_vline.set_segments(seg)
88
+ return
89
+
90
+ def _set_slider_text(self, e: MouseEvent):
91
+ xdata = e.xdata
92
+ if xdata is None:
93
+ return
94
+ idx = round(xdata)
95
+ if idx < 0:
96
+ return
97
+
98
+ try:
99
+ text = self.df.iloc[idx]['date']
100
+ except:
101
+ return
102
+
103
+ self.artist_text_slider.set_text(text)
104
+ self.artist_text_slider.set_x(round(xdata, 2))
105
+ return 1
106
+
107
+
108
+ class NavMixin(Base):
109
+ segment_nav: np.ndarray
110
+
111
+ def _set_nav_segment(self, xmin, xmax):
112
+ xmin0 = xmin - self._nav_width
113
+ xmin1 = xmin
114
+ xmax0 = xmax
115
+ xmax1 = xmax + self._nav_width
116
+ seg = [
117
+ # 좌측 오버레이
118
+ (
119
+ (self.slider_xmin, self.slider_ymax),
120
+ (xmin0, self.slider_ymax),
121
+ (xmin0, self.slider_ymin),
122
+ (self.slider_xmin, self.slider_ymin),
123
+ ),
124
+ # 우측 오버레이
125
+ (
126
+ (xmax1, self.slider_ymax),
127
+ (self.slider_xmax, self.slider_ymax),
128
+ (self.slider_xmax, self.slider_ymin),
129
+ (xmax1, self.slider_ymin),
130
+ ),
131
+ # 좌측 네비게이터
132
+ (
133
+ (xmin1, self.slider_ymax),
134
+ (xmin1, self.slider_ymin),
135
+ (xmin0, self.slider_ymin),
136
+ (xmin0, self.slider_ymax),
137
+ ),
138
+ # 우측 네비게이터
139
+ (
140
+ (xmax0, self.slider_ymin),
141
+ (xmax0, self.slider_ymax),
142
+ (xmax1, self.slider_ymax),
143
+ (xmax1, self.slider_ymin),
144
+ ),
145
+ ]
146
+ self.segment_nav = np.array(seg)
147
+
148
+ self.collection_nav.set_segments(self.segment_nav)
149
+ return
150
+
151
+
152
+ class SegmentMixin(SliderMixin, VlineMixin, NavMixin):
153
+ segment_nav: np.ndarray
154
+
155
+ def set_segments(self):
156
+ super().set_segments()
157
+
158
+ self._set_slider_collection()
159
+ self._set_slider_color_collection()
160
+ return
161
+
162
+ def set_color_segments(self):
163
+ super().set_color_segments()
164
+
165
+ self._set_slider_color_collection()
166
+ return
@@ -0,0 +1,70 @@
1
+ from matplotlib.collections import LineCollection
2
+ import numpy as np
3
+
4
+
5
+ class Base:
6
+ limit_volume = 200
7
+ limit_ma = 8_000
8
+
9
+ x_click: int
10
+ vxmin: int
11
+ vxmax: int
12
+
13
+ axis: callable
14
+ _set_nav_segment: callable
15
+ set_collections: callable
16
+
17
+ key_volume: str
18
+ segment_volume_wick: np.ndarray
19
+ _set_volume_collection_wick_segments: callable
20
+ _set_ma_collection_segments: callable
21
+ facecolor_volume: np.ndarray
22
+ edgecolor_volume: np.ndarray
23
+ collection_volume: LineCollection
24
+ collection_ma: LineCollection
25
+
26
+
27
+ class Mixin(Base):
28
+ def axis(self, xmin, *, xmax):
29
+ super().axis(xmin, xmax=xmax)
30
+ self._set_nav_segment(self.vxmin, xmax=self.vxmax-1)
31
+ return
32
+
33
+ def _set_volume_collection_wick_segments(self, ind_start, ind_end):
34
+ if not self.key_volume or not self.x_click:
35
+ super()._set_volume_collection_wick_segments(ind_start, ind_end=ind_end)
36
+ else:
37
+ indsub = ind_end - ind_start
38
+ if indsub <= self.limit_volume:
39
+ super()._set_volume_collection_wick_segments(ind_start, ind_end=ind_end)
40
+ else:
41
+ # 일부 거래량만 그리기
42
+ seg_volume = self.segment_volume_wick[ind_start:ind_end]
43
+ values = seg_volume[:, 1, 1]
44
+ top_index = np.argsort(-values)[:self.limit_volume]
45
+ seg = seg_volume[top_index]
46
+ facecolors = self.facecolor_volume[ind_start:ind_end][top_index]
47
+ edgecolors = self.edgecolor_volume[ind_start:ind_end][top_index]
48
+ self.collection_volume.set_segments(seg)
49
+ self.collection_volume.set_linewidth(1.3)
50
+ self.collection_volume.set_facecolor(facecolors)
51
+ self.collection_volume.set_edgecolor(edgecolors)
52
+ return
53
+
54
+ def _set_ma_collection_segments(self, ind_start, ind_end):
55
+ if not self.x_click:
56
+ super()._set_ma_collection_segments(ind_start, ind_end=ind_end)
57
+ else:
58
+ indsub = ind_end - ind_start
59
+ if indsub <= self.limit_ma:
60
+ super()._set_ma_collection_segments(ind_start, ind_end=ind_end)
61
+ else:
62
+ # 이평선 그리지 않기
63
+ self.collection_ma.set_segments([])
64
+ return
65
+
66
+
67
+ class AxisMixin(Mixin):
68
+ limit_volume = 200
69
+ limit_ma = 8_000
70
+
@@ -0,0 +1,37 @@
1
+ from matplotlib.axes import Axes
2
+
3
+
4
+ class Base:
5
+ _background = None
6
+ _background_background = None
7
+
8
+ _creating_background = False
9
+
10
+ draw_background: callable
11
+ _draw_ax: callable
12
+ _draw_ax_slider: callable
13
+ draw_artists: callable
14
+ _draw_nav: callable
15
+
16
+
17
+ class SliderMixin(Base):
18
+ ax_slider: Axes
19
+
20
+ def draw_background(self):
21
+ self._draw_ax(self.ax_slider)
22
+ self._draw_ax_slider()
23
+
24
+ super().draw_background()
25
+ return
26
+
27
+ def draw_artists(self):
28
+ super().draw_artists()
29
+
30
+ self._draw_nav()
31
+ return
32
+
33
+
34
+ class BackgroundMixin(SliderMixin):
35
+ _background = None
36
+ _creating_background = False
37
+