seolpyo-mplchart 1.4.1__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 (83) hide show
  1. seolpyo_mplchart/__init__.py +53 -333
  2. seolpyo_mplchart/_chart/__init__.py +145 -0
  3. seolpyo_mplchart/_chart/_base.py +217 -0
  4. seolpyo_mplchart/_chart/_cursor/__init__.py +2 -0
  5. seolpyo_mplchart/_chart/_cursor/_artist.py +217 -0
  6. seolpyo_mplchart/_chart/_cursor/_cursor.py +165 -0
  7. seolpyo_mplchart/_chart/_cursor/_info.py +187 -0
  8. seolpyo_mplchart/_chart/_draw/__init__.py +2 -0
  9. seolpyo_mplchart/_chart/_draw/_artist.py +50 -0
  10. seolpyo_mplchart/_chart/_draw/_data.py +314 -0
  11. seolpyo_mplchart/_chart/_draw/_draw.py +103 -0
  12. seolpyo_mplchart/_chart/_draw/_lim.py +265 -0
  13. seolpyo_mplchart/_chart/_slider/__init__.py +1 -0
  14. seolpyo_mplchart/_chart/_slider/_base.py +268 -0
  15. seolpyo_mplchart/_chart/_slider/_data.py +105 -0
  16. seolpyo_mplchart/_chart/_slider/_mouse.py +176 -0
  17. seolpyo_mplchart/_chart/_slider/_nav.py +204 -0
  18. seolpyo_mplchart/_chart/base/__init__.py +111 -0
  19. seolpyo_mplchart/_chart/base/a_canvas.py +250 -0
  20. seolpyo_mplchart/_chart/base/b_artist.py +143 -0
  21. seolpyo_mplchart/_chart/base/c_draw.py +100 -0
  22. seolpyo_mplchart/_chart/base/d_segment.py +262 -0
  23. seolpyo_mplchart/_chart/base/e_axis.py +267 -0
  24. seolpyo_mplchart/_chart/base/f_background.py +62 -0
  25. seolpyo_mplchart/_chart/base/g_event.py +66 -0
  26. seolpyo_mplchart/_chart/base/h_data.py +138 -0
  27. seolpyo_mplchart/_chart/base/test.py +58 -0
  28. seolpyo_mplchart/_chart/cursor/__init__.py +125 -0
  29. seolpyo_mplchart/_chart/cursor/b_artist.py +130 -0
  30. seolpyo_mplchart/_chart/cursor/c_draw.py +96 -0
  31. seolpyo_mplchart/_chart/cursor/d_segment.py +359 -0
  32. seolpyo_mplchart/_chart/cursor/e_axis.py +65 -0
  33. seolpyo_mplchart/_chart/cursor/g_event.py +233 -0
  34. seolpyo_mplchart/_chart/cursor/h_data.py +61 -0
  35. seolpyo_mplchart/_chart/cursor/test.py +69 -0
  36. seolpyo_mplchart/_chart/slider/__init__.py +169 -0
  37. seolpyo_mplchart/_chart/slider/a_canvas.py +260 -0
  38. seolpyo_mplchart/_chart/slider/b_artist.py +91 -0
  39. seolpyo_mplchart/_chart/slider/c_draw.py +54 -0
  40. seolpyo_mplchart/_chart/slider/d_segment.py +166 -0
  41. seolpyo_mplchart/_chart/slider/e_axis.py +70 -0
  42. seolpyo_mplchart/_chart/slider/f_background.py +37 -0
  43. seolpyo_mplchart/_chart/slider/g_event.py +353 -0
  44. seolpyo_mplchart/_chart/slider/h_data.py +102 -0
  45. seolpyo_mplchart/_chart/slider/test.py +71 -0
  46. seolpyo_mplchart/_chart/test.py +121 -0
  47. seolpyo_mplchart/_config/__init__.py +3 -0
  48. seolpyo_mplchart/_config/ax.py +28 -0
  49. seolpyo_mplchart/_config/candle.py +31 -0
  50. seolpyo_mplchart/_config/config.py +21 -0
  51. seolpyo_mplchart/_config/cursor.py +49 -0
  52. seolpyo_mplchart/_config/figure.py +40 -0
  53. seolpyo_mplchart/_config/format.py +51 -0
  54. seolpyo_mplchart/_config/ma.py +17 -0
  55. seolpyo_mplchart/_config/slider/__init__.py +2 -0
  56. seolpyo_mplchart/_config/slider/config.py +24 -0
  57. seolpyo_mplchart/_config/slider/figure.py +19 -0
  58. seolpyo_mplchart/_config/slider/nav.py +10 -0
  59. seolpyo_mplchart/_config/unit.py +19 -0
  60. seolpyo_mplchart/_config/utils.py +67 -0
  61. seolpyo_mplchart/_config/volume.py +27 -0
  62. seolpyo_mplchart/_cursor.py +27 -25
  63. seolpyo_mplchart/_draw.py +7 -18
  64. seolpyo_mplchart/_slider.py +26 -20
  65. seolpyo_mplchart/_utils/__init__.py +10 -0
  66. seolpyo_mplchart/_utils/nums.py +67 -0
  67. seolpyo_mplchart/_utils/theme/__init__.py +15 -0
  68. seolpyo_mplchart/_utils/theme/dark.py +57 -0
  69. seolpyo_mplchart/_utils/theme/light.py +56 -0
  70. seolpyo_mplchart/_utils/utils.py +28 -0
  71. seolpyo_mplchart/_utils/xl/__init__.py +15 -0
  72. seolpyo_mplchart/_utils/xl/csv.py +46 -0
  73. seolpyo_mplchart/_utils/xl/xlsx.py +49 -0
  74. seolpyo_mplchart/sample/apple.txt +6058 -0
  75. seolpyo_mplchart/sample/samsung.txt +5938 -0
  76. seolpyo_mplchart/test.py +172 -56
  77. seolpyo_mplchart/xl_to_dict.py +47 -0
  78. seolpyo_mplchart-2.1.0.dist-info/METADATA +718 -0
  79. seolpyo_mplchart-2.1.0.dist-info/RECORD +89 -0
  80. {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.dist-info}/WHEEL +1 -1
  81. seolpyo_mplchart-1.4.1.dist-info/METADATA +0 -57
  82. seolpyo_mplchart-1.4.1.dist-info/RECORD +0 -17
  83. {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,250 @@
1
+ import matplotlib.pyplot as plt
2
+ import matplotlib.style as mplstyle
3
+ from matplotlib.artist import Artist
4
+ from matplotlib.axes import Axes
5
+ from matplotlib.backends.backend_agg import FigureCanvasAgg, RendererAgg
6
+ from matplotlib.backend_bases import FigureManagerBase
7
+ from matplotlib.figure import Figure as Fig
8
+ from matplotlib.text import Text
9
+
10
+ try: plt.switch_backend('TkAgg')
11
+ except: pass
12
+
13
+ # 한글 깨짐 문제 방지
14
+ try: plt.rcParams['font.family'] ='Malgun Gothic'
15
+ except: pass
16
+
17
+ from ..._config import DEFAULTCONFIG, ConfigData
18
+
19
+
20
+ mplstyle.use('fast')
21
+
22
+
23
+ class Canvas(FigureCanvasAgg):
24
+ manager: FigureManagerBase
25
+ renderer = RendererAgg
26
+
27
+ class Figure(Fig):
28
+ canvas: Canvas
29
+
30
+
31
+ class Base:
32
+ CONFIG: ConfigData
33
+ figure: Figure = None
34
+
35
+ def add_axes(self):
36
+ if not self.figure:
37
+ self.figure, *_ = plt.subplots(
38
+ 3, # row 수
39
+ figsize=self.CONFIG.FIGURE.figsize, # 기본 크기
40
+ height_ratios=(
41
+ 1,
42
+ self.CONFIG.FIGURE.RATIO.price,
43
+ self.CONFIG.FIGURE.RATIO.volume,
44
+ ) # row 크기 비율
45
+ )
46
+
47
+ self.ax_legend, self.ax_price, self.ax_volume = self.figure.axes
48
+ self.ax_legend.set_label('legend ax')
49
+ self.ax_price.set_label('price ax')
50
+ self.ax_volume.set_label('volume ax')
51
+
52
+ return
53
+
54
+
55
+ class FigureMixin(Base):
56
+ key_volume: str
57
+ ax_legend: Axes
58
+
59
+ def _set_figure_ratios(self):
60
+ gs = self.figure.axes[0].get_subplotspec().get_gridspec()
61
+
62
+ ratio_volume = self.CONFIG.FIGURE.RATIO.volume
63
+ if not self.key_volume:
64
+ ratio_volume = 0
65
+
66
+ legend = self.ax_legend.get_legend()
67
+ if not legend:
68
+ ratios = [
69
+ 0,
70
+ self.CONFIG.FIGURE.RATIO.price, ratio_volume
71
+ ]
72
+ else:
73
+ fig_heihgt = self.figure.get_figheight()
74
+ fig_px = fig_heihgt * (1-self.CONFIG.FIGURE.ADJUST.hspace*2) * self.figure.dpi
75
+ # print(f'{(fig_heihgt, fig_px)=}')
76
+
77
+ # Legend에 Axes 높이 맞추기
78
+ bbox = legend.get_window_extent().transformed(self.figure.transFigure.inverted())
79
+ ax_pos = self.ax_legend.get_position()
80
+ self.ax_legend.set_position([ax_pos.x0, ax_pos.y0, ax_pos.width, bbox.height])
81
+
82
+ legend_height = bbox.height
83
+ legend_px = legend.get_window_extent().height
84
+ # print(f'{(legend_height, legend_px)=}')
85
+
86
+ chart_px = fig_px - legend_height
87
+ chart_ratio = self.CONFIG.FIGURE.RATIO.price + ratio_volume
88
+ div_chart = chart_px / chart_ratio
89
+ price_px = div_chart * self.CONFIG.FIGURE.RATIO.price
90
+ volume_px = div_chart * ratio_volume
91
+
92
+ # 차트 비율 변경
93
+ ratios = [
94
+ legend_px * 1.2,
95
+ price_px, volume_px
96
+ ]
97
+ # print(f'{ratios=}')
98
+ gs.set_height_ratios(ratios)
99
+
100
+ self.figure.tight_layout()
101
+
102
+ # 플롯간 간격 설정(Configure subplots)
103
+ self.figure.subplots_adjust(**self.CONFIG.FIGURE.ADJUST.__dict__)
104
+
105
+ return
106
+
107
+ def _set_figure(self):
108
+ self.figure.canvas.manager.set_window_title('Seolpyo MPLChart')
109
+
110
+ # print(f'{self.CONFIG.FIGURE.RATIO.volume=}')
111
+ # print(f'{gs.get_height_ratios()=}')
112
+
113
+ self._set_figure_ratios()
114
+
115
+ self.figure.set_facecolor(self.CONFIG.FIGURE.facecolor)
116
+ return
117
+
118
+
119
+ class AxesMixin(FigureMixin):
120
+ def _set_axes(self):
121
+ # ax 요소 animated 처리
122
+ for ax in self.figure.axes:
123
+ ax.patch.set_animated(True)
124
+
125
+ # ax 경계선
126
+ for spine in ax.spines.values():
127
+ spine.set_animated(True)
128
+
129
+ for axis in (ax.xaxis, ax.yaxis):
130
+ axis.set_animated(True)
131
+ axis.label.set_animated(True)
132
+ for tick in axis.get_major_ticks() + axis.get_minor_ticks():
133
+ artists: list[Artist] = [
134
+ tick.tick1line, tick.tick2line,
135
+ tick.gridline,
136
+ tick.label1, tick.label2
137
+ ]
138
+ for artist in artists:
139
+ if artist is not None:
140
+ artist.set_animated(True)
141
+
142
+ # y ticklabel foramt 설정
143
+ self.ax_price.yaxis.set_major_formatter(
144
+ lambda x, _: self.CONFIG.UNIT.func(
145
+ x,
146
+ word=self.CONFIG.UNIT.price,
147
+ digit=self.CONFIG.UNIT.digit+(0 if self.CONFIG.UNIT.digit % 1 else 2)
148
+ )
149
+ )
150
+ self.ax_volume.yaxis.set_major_formatter(
151
+ lambda x, _: self.CONFIG.UNIT.func(
152
+ x,
153
+ word=self.CONFIG.UNIT.volume,
154
+ digit=self.CONFIG.UNIT.digit_volume
155
+ )
156
+ )
157
+
158
+ if not self.key_volume:
159
+ # tick 그리지 않기
160
+ self.ax_volume.set_yticklabels([])
161
+ self.ax_volume.set_yticks([])
162
+
163
+ # 공통 설정
164
+ for ax in (self.ax_price, self.ax_volume):
165
+ ax.xaxis.set_animated(True)
166
+ ax.yaxis.set_animated(True)
167
+
168
+ # x tick 외부 눈금 표시하지 않기
169
+ ax.xaxis.set_ticks_position('none')
170
+ # x tick label 제거
171
+ ax.set_xticklabels([])
172
+ # y tick 위치를 우측으로 이동
173
+ ax.tick_params(left=False, right=True, labelleft=False, labelright=True)
174
+
175
+ # 차트 영역 배경 색상
176
+ ax.set_facecolor(self.CONFIG.AX.facecolor)
177
+
178
+ # Axes 외곽선 색 변경(틱 색과 일치)
179
+ for i in ['top', 'bottom', 'left', 'right']:
180
+ ax.spines[i].set_color(self.CONFIG.AX.TICK.edgecolor)
181
+ # 틱 색상
182
+ ax.tick_params('both', colors=self.CONFIG.AX.TICK.edgecolor)
183
+ # 틱 라벨 색상
184
+ ticklabels: list[Text] = ax.get_xticklabels() + ax.get_yticklabels()
185
+ for ticklabel in ticklabels:
186
+ ticklabel.set_color(self.CONFIG.AX.TICK.fontcolor)
187
+
188
+ # Axes grid(구분선, 격자) 그리기
189
+ # 어째서인지 grid의 zorder 값을 선언해도 1.6을 값으로 한다.
190
+ ax.grid(**self.CONFIG.AX.GRID.__dict__)
191
+
192
+ # 거래량 차트의 x tick 외부 눈금 표시하기
193
+ self.ax_volume.xaxis.set_ticks_position('bottom')
194
+ # major tick mark 길이를 0으로 만들어 튀어나오지 않게 하기
195
+ self.ax_volume.tick_params('x', which='major', length=0)
196
+ # minor tick mark 색상 변경
197
+ self.ax_volume.tick_params('x', which='minor', colors=self.CONFIG.AX.TICK.edgecolor)
198
+ return
199
+
200
+
201
+ class LegendMixin(AxesMixin):
202
+ def _set_axes_legend(self):
203
+ # 이평선 라벨 axis 그리지 않기
204
+ self.ax_legend.set_axis_off()
205
+ self.ax_legend.xaxis.set_animated(True)
206
+ self.ax_legend.yaxis.set_animated(True)
207
+ self.ax_legend.set_animated(True)
208
+
209
+ # 이평선 라벨 Axes 배경색
210
+ legends = self.ax_legend.get_legend()
211
+ if legends:
212
+ legends.get_frame().set_facecolor(self.CONFIG.AX.facecolor)
213
+
214
+ # 이평선 라벨 Axes 테두리색
215
+ legends = self.ax_legend.get_legend()
216
+ if legends:
217
+ legends.get_frame().set_edgecolor(self.CONFIG.AX.TICK.edgecolor)
218
+
219
+ # 이평선 라벨 폰트 색상
220
+ fontcolor = self.CONFIG.AX.TICK.fontcolor
221
+ legends = self.ax_legend.get_legend()
222
+ if legends:
223
+ legend_labels: list[Text] = legends.texts
224
+ for i in legend_labels:
225
+ i.set_color(fontcolor)
226
+ return
227
+
228
+
229
+ class CanvasMixin(LegendMixin):
230
+ figure: Figure
231
+ ax_legend: Axes
232
+ ax_price: Axes
233
+ ax_volume: Axes
234
+
235
+ def __init__(self, config=DEFAULTCONFIG):
236
+ # 기본 툴바 비활성화
237
+ plt.rcParams['toolbar'] = 'None'
238
+ # plt.rcParams['figure.dpi'] = 600
239
+
240
+ self.CONFIG = config
241
+ self.add_axes()
242
+ self.set_canvas()
243
+ return
244
+
245
+ def set_canvas(self):
246
+ self._set_axes()
247
+ self._set_axes_legend()
248
+ self._set_figure()
249
+ return
250
+
@@ -0,0 +1,143 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.collections import LineCollection
3
+ from matplotlib.lines import Line2D
4
+ from matplotlib.text import Text
5
+
6
+ from ..._config import ConfigData
7
+
8
+
9
+ class WatermarkMixin:
10
+ CONFIG: ConfigData
11
+
12
+ watermark = 'seolpyo mplchart'
13
+
14
+ ax_price: Axes
15
+
16
+ def _add_watermark(self):
17
+ self.artist_watermark = Text(
18
+ x=0.5, y=0.5, text='',
19
+ animated=True,
20
+ horizontalalignment='center', verticalalignment='center',
21
+ transform=self.ax_price.transAxes
22
+ )
23
+ self.ax_price.add_artist(self.artist_watermark)
24
+ return
25
+
26
+ def _set_watermark(self):
27
+ self.artist_watermark.set(animated=True, horizontalalignment='center', verticalalignment='center',)
28
+ self.artist_watermark.set_text(self.watermark)
29
+ self.artist_watermark.set_fontsize(self.CONFIG.FIGURE.WATERMARK.fontsize)
30
+ self.artist_watermark.set_color(self.CONFIG.FIGURE.WATERMARK.color)
31
+ self.artist_watermark.set_alpha(self.CONFIG.FIGURE.WATERMARK.alpha)
32
+ return
33
+
34
+
35
+ class CollectionMixin:
36
+ ax_price: Axes
37
+ ax_volume: Axes
38
+ ax_legend: Axes
39
+
40
+ def _add_collections(self):
41
+ self.collection_candle = LineCollection([], animated=True, linewidths=0.8)
42
+ self.ax_price.add_collection(self.collection_candle)
43
+
44
+ self.collection_ma = LineCollection([], animated=True, linewidths=1)
45
+ self.ax_price.add_collection(self.collection_ma)
46
+
47
+ self.collection_volume = LineCollection([], animated=True, linewidths=1)
48
+ self.ax_volume.add_collection(self.collection_volume)
49
+ return
50
+
51
+ def set_collection_candle(self, segment, facecolors, edgecolors):
52
+ self.collection_candle.set_segments(segment)
53
+ self.collection_candle.set_facecolor(facecolors)
54
+ self.collection_candle.set_edgecolor(edgecolors)
55
+ self.collection_candle.set_transform(self.ax_price.transData)
56
+ return
57
+
58
+ def set_collection_volume(self, segment, facecolors, edgecolors):
59
+ self.collection_volume.set_segments(segment)
60
+ self.collection_volume.set_facecolor(facecolors)
61
+ self.collection_volume.set_edgecolor(edgecolors)
62
+ self.collection_volume.set_transform(self.ax_volume.transData)
63
+ self.collection_volume.set_antialiased(False)
64
+ return
65
+
66
+
67
+ class MaMixin:
68
+ CONFIG: ConfigData
69
+ _visible_ma = set()
70
+
71
+ ax_legend: Axes
72
+ ax_price: Axes
73
+ collection_ma: LineCollection
74
+ _set_figure_ratios: callable
75
+
76
+ def set_collection_ma(self, segment, edgecolors):
77
+ self.collection_ma.set_segments(segment)
78
+ self.collection_ma.set_facecolor([])
79
+ self.collection_ma.set_edgecolor(edgecolors)
80
+ # print(self.collection_ma.get_linewidth())
81
+ self.collection_ma.set_linewidth(self.CONFIG.MA.linewidth)
82
+ self.collection_ma.set_transform(self.ax_price.transData)
83
+ return
84
+
85
+ def _set_legends(self):
86
+ legends = self.ax_legend.get_legend()
87
+ if legends:
88
+ legends.remove()
89
+
90
+ self._visible_ma.clear()
91
+
92
+ label_list, handle_list, edgecolor_list = ([], [], [])
93
+ # Legend Ax에 표시하는 선 segment
94
+ arr = [0, 1]
95
+ for n, ma in enumerate(self.CONFIG.MA.ma_list):
96
+ self._visible_ma.add(ma)
97
+ label_list.append(self.CONFIG.MA.format.format(ma))
98
+ try:
99
+ color = self.CONFIG.MA.color_list[n]
100
+ except:
101
+ color = self.CONFIG.MA.color_default
102
+ edgecolor_list.append(color)
103
+ handle_list.append(Line2D(arr, ydata=arr, color=color, linewidth=5, label=ma))
104
+
105
+ self.set_collection_ma([], edgecolors=edgecolor_list)
106
+
107
+ # 가격이동평균선 legend 생성
108
+ if handle_list:
109
+ legends = self.ax_legend.legend(
110
+ handle_list, label_list, loc='lower left', ncol=self.CONFIG.MA.ncol,
111
+ borderpad=0.55,
112
+ facecolor=self.CONFIG.AX.facecolor, edgecolor=self.CONFIG.AX.TICK.edgecolor,
113
+ labelcolor=self.CONFIG.AX.TICK.fontcolor,
114
+ )
115
+ for handle in legends.legend_handles:
116
+ # legend 클릭시 pick event가 발생할 수 있도록 설정
117
+ handle.set_picker(5)
118
+
119
+ # legend ax 크기 조정
120
+ self._set_figure_ratios()
121
+ return
122
+
123
+
124
+ class ArtistMixin(WatermarkMixin, CollectionMixin, MaMixin):
125
+ artist_watermark: Text
126
+ collection_candle: LineCollection
127
+ collection_volume: LineCollection
128
+ collection_ma: LineCollection
129
+
130
+ _visible_ma: set[int] = set()
131
+
132
+ def add_artists(self):
133
+ self._add_watermark()
134
+ self._set_watermark()
135
+ self._add_collections()
136
+ self._set_legends()
137
+ return
138
+
139
+ def set_artists(self):
140
+ self._set_watermark()
141
+ self._set_legends()
142
+ return
143
+
@@ -0,0 +1,100 @@
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
+ candle_on_ma = True
10
+
11
+ watermark: str
12
+
13
+ figure: Figure
14
+ ax_legend: Axes
15
+ ax_price: Axes
16
+ ax_volume: Axes
17
+
18
+ artist_watermark: Text
19
+ collection_candle: LineCollection
20
+ collection_volume: LineCollection
21
+ collection_ma: LineCollection
22
+
23
+ def draw_chart(self):
24
+ self._draw_ax_price()
25
+ self._draw_ax_volume()
26
+ self._draw_chart_price()
27
+ self._draw_chart_volume()
28
+ return
29
+
30
+ def _draw_chart_volume(self):
31
+ renderer = self.figure.canvas.renderer
32
+
33
+ self.collection_volume.draw(renderer)
34
+ return
35
+
36
+ def _draw_chart_price(self):
37
+ renderer = self.figure.canvas.renderer
38
+ # print(f'{renderer=}')
39
+
40
+ # print(self.collection_candle.get_segments())
41
+ if self.candle_on_ma:
42
+ self.collection_ma.draw(renderer)
43
+ self.collection_candle.draw(renderer)
44
+ else:
45
+ self.collection_candle.draw(renderer)
46
+ self.collection_ma.draw(renderer)
47
+
48
+ if self.watermark:
49
+ if self.watermark != self.artist_watermark.get_text():
50
+ self.artist_watermark.set_text(self.watermark)
51
+ self.artist_watermark.draw(renderer)
52
+ return
53
+
54
+ def draw_artists(self):
55
+ return
56
+
57
+ def draw_background(self):
58
+ self._draw_ax(self.ax_price)
59
+ self._draw_ax(self.ax_volume)
60
+
61
+ legend = self.ax_legend.get_legend()
62
+ if legend:
63
+ legend.draw(self.figure.canvas.renderer)
64
+ return
65
+
66
+ def _draw_ax(self, ax: Axes):
67
+ renderer = self.figure.canvas.renderer
68
+
69
+ # ax 배경
70
+ ax.patch.draw(renderer)
71
+
72
+ # ax 외곽선
73
+ for spine in ax.spines.values():
74
+ spine.draw(renderer)
75
+
76
+ return
77
+
78
+ def _draw_ax_volume(self):
79
+ renderer = self.figure.canvas.renderer
80
+
81
+ # grid, tick, ticklabel
82
+ ax = self.ax_volume
83
+ for axis in (ax.xaxis, ax.yaxis):
84
+ axis.draw(renderer)
85
+ return
86
+
87
+ def _draw_ax_price(self):
88
+ renderer = self.figure.canvas.renderer
89
+ # print(f'{renderer=}')
90
+
91
+ # grid, tick, ticklabel
92
+ ax = self.ax_price
93
+ for axis in (ax.xaxis, ax.yaxis):
94
+ axis.draw(renderer)
95
+ return
96
+
97
+
98
+ class DrawMixin(Base):
99
+ candle_on_ma = True
100
+