seolpyo-mplchart 1.3.1.1__py3-none-any.whl → 1.4.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.

Potentially problematic release.


This version of seolpyo-mplchart might be problematic. Click here for more details.

@@ -297,19 +297,19 @@ def set_theme(chart: SliderChart|CursorChart|OnlyChart, theme: Literal['light',
297
297
  chart.color_volume_up, chart.color_volume_down = ('#32CD32', '#FF4500')
298
298
  chart.color_volume_flat = 'w'
299
299
 
300
- chart.list_macolor = ('#FFFF00', '#7FFF00', '#00FFFF', '#FFA07A', '#FF00FF')
300
+ chart.list_macolor = ('#1E90FF', '#FFA500', '#FF1493', '#FFFF00', '#00CED1')
301
301
 
302
302
  chart.lineKwargs = {'edgecolor': 'w'}
303
303
  chart.color_box = 'w'
304
304
  chart.textboxKwargs = {'facecolor': 'k', 'edgecolor': 'w'}
305
305
  chart.textKwargs = {'color': 'w'}
306
- chart.color_navigator_cover, chart.color_navigator_line = ('w', '#FF2400')
306
+ chart.color_navigator_cover, chart.color_navigator_line = ('k', '#FF2400')
307
307
 
308
308
  if initialized:
309
309
  chart.change_background_color('k')
310
310
  chart.change_tick_color('w')
311
311
  chart.change_line_color('w')
312
- if hasattr(chart, 'navigator'): chart.navigator.set_edgecolor([chart.color_navigator_cover, chart.color_navigator_line])
312
+ if hasattr(chart, 'navigator'): chart.collection_navigator.set_edgecolor([chart.color_navigator_cover, chart.color_navigator_line])
313
313
 
314
314
  if hasattr(chart, 'df'):
315
315
  chart.set_data(chart.df, sort_df=False, calc_ma=False, set_candlecolor=True, set_volumecolor=True, calc_info=False, change_lim=False)
@@ -324,22 +324,22 @@ def set_theme(chart: SliderChart|CursorChart|OnlyChart, theme: Literal['light',
324
324
  chart.color_flat = 'k'
325
325
  chart.color_up_down, chart.color_down_up = ('w', 'w')
326
326
 
327
- chart.color_volume_up, chart.color_volume_down = ('#FF4D4D', '#5CA8F4')
328
- chart.color_volume_flat = '#A9A9A9'
327
+ chart.color_volume_up, chart.color_volume_down = ('#FF6666', '#5CA8F4')
328
+ chart.color_volume_flat = '#808080'
329
329
 
330
- chart.list_macolor = ('#B22222', '#228B22', '#1E90FF', '#FF8C00', '#4B0082')
330
+ chart.list_macolor = ('#006400', '#8B008B', '#FFA500', '#0000FF', '#FF0000')
331
331
 
332
332
  chart.lineKwargs = {'edgecolor': 'k'}
333
333
  chart.color_box = 'k'
334
334
  chart.textboxKwargs = {'facecolor': 'w', 'edgecolor': 'k'}
335
335
  chart.textKwargs = {'color': 'k'}
336
- chart.color_navigator_cover, chart.color_navigator_line = ('k', '#1e78ff')
336
+ chart.color_navigator_cover, chart.color_navigator_line = ('k', '#1E78FF')
337
337
 
338
338
  if initialized:
339
339
  chart.change_background_color('#fafafa')
340
340
  chart.change_tick_color('k')
341
341
  chart.change_line_color('k')
342
- if hasattr(chart, 'navigator'): chart.navigator.set_edgecolor([chart.color_navigator_cover, chart.color_navigator_line])
342
+ if hasattr(chart, 'navigator'): chart.collection_navigator.set_edgecolor([chart.color_navigator_cover, chart.color_navigator_line])
343
343
 
344
344
  if hasattr(chart, 'df'):
345
345
  chart.set_data(chart.df, sort_df=False, calc_ma=False, set_candlecolor=True, set_volumecolor=True, calc_info=False, change_lim=False)
@@ -34,72 +34,72 @@ class CollectionMixin(BM):
34
34
  textKwargs.update({'animated': True, 'bbox': textboxKwargs, 'horizontalalignment': '', 'verticalalignment': ''})
35
35
  (textKwargs.pop('horizontalalignment'), textKwargs.pop('verticalalignment'))
36
36
 
37
- self.price_crossline = LineCollection(**lineKwargs)
38
- self.ax_price.add_artist(self.price_crossline)
39
- self.text_date_price = Text(**textKwargs, horizontalalignment='center', verticalalignment='bottom')
40
- self.ax_price.add_artist(self.text_date_price)
41
- self.text_price = Text(**textKwargs, horizontalalignment='left', verticalalignment='center')
42
- self.ax_price.add_artist(self.text_price)
43
-
44
- self.volume_crossline = LineCollection(**lineKwargs)
45
- self.ax_volume.add_artist(self.volume_crossline)
46
- self.text_date_volume = Text(**textKwargs, horizontalalignment='center', verticalalignment='top')
47
- self.ax_volume.add_artist(self.text_date_volume)
48
- self.text_volume = Text(**textKwargs, horizontalalignment='left', verticalalignment='center')
49
- self.ax_volume.add_artist(self.text_volume)
50
-
51
- self.price_box = LineCollection([], animated=True, linewidth=1.2, edgecolor=self.color_box)
52
- self.ax_price.add_artist(self.price_box)
53
- self.text_price_info = Text(**textKwargs, horizontalalignment='left', verticalalignment='top')
54
- self.ax_price.add_artist(self.text_price_info)
55
-
56
- self.volume_box = LineCollection([], animated=True, linewidth=1.2, edgecolor=self.color_box)
57
- self.ax_volume.add_artist(self.volume_box)
58
- self.text_volume_info = Text(**textKwargs, horizontalalignment='left', verticalalignment='top')
59
- self.ax_volume.add_artist(self.text_volume_info)
37
+ self.collection_price_crossline = LineCollection(**lineKwargs)
38
+ self.ax_price.add_artist(self.collection_price_crossline)
39
+ self.artist_text_date_price = Text(**textKwargs, horizontalalignment='center', verticalalignment='bottom')
40
+ self.ax_price.add_artist(self.artist_text_date_price)
41
+ self.artist_text_price = Text(**textKwargs, horizontalalignment='left', verticalalignment='center')
42
+ self.ax_price.add_artist(self.artist_text_price)
43
+
44
+ self.collection_volume_crossline = LineCollection(**lineKwargs)
45
+ self.ax_volume.add_artist(self.collection_volume_crossline)
46
+ self.artist_text_date_volume = Text(**textKwargs, horizontalalignment='center', verticalalignment='top')
47
+ self.ax_volume.add_artist(self.artist_text_date_volume)
48
+ self.artist_text_volume = Text(**textKwargs, horizontalalignment='left', verticalalignment='center')
49
+ self.ax_volume.add_artist(self.artist_text_volume)
50
+
51
+ self.collection_price_box = LineCollection([], animated=True, linewidth=1.2, edgecolor=self.color_box)
52
+ self.ax_price.add_artist(self.collection_price_box)
53
+ self.artist_text_price_info = Text(**textKwargs, horizontalalignment='left', verticalalignment='top')
54
+ self.ax_price.add_artist(self.artist_text_price_info)
55
+
56
+ self.collection_volume_box = LineCollection([], animated=True, linewidth=1.2, edgecolor=self.color_box)
57
+ self.ax_volume.add_artist(self.collection_volume_box)
58
+ self.artist_text_volume_info = Text(**textKwargs, horizontalalignment='left', verticalalignment='top')
59
+ self.ax_volume.add_artist(self.artist_text_volume_info)
60
60
  return
61
61
 
62
62
  def change_background_color(self, color):
63
63
  super().change_background_color(color)
64
64
 
65
- self.text_price.set_backgroundcolor(color)
66
- self.text_volume.set_backgroundcolor(color)
65
+ self.artist_text_price.set_backgroundcolor(color)
66
+ self.artist_text_volume.set_backgroundcolor(color)
67
67
 
68
- self.text_date_price.set_backgroundcolor(color)
69
- self.text_date_volume.set_backgroundcolor(color)
68
+ self.artist_text_date_price.set_backgroundcolor(color)
69
+ self.artist_text_date_volume.set_backgroundcolor(color)
70
70
 
71
- self.text_price_info.set_backgroundcolor(color)
72
- self.text_volume_info.set_backgroundcolor(color)
71
+ self.artist_text_price_info.set_backgroundcolor(color)
72
+ self.artist_text_volume_info.set_backgroundcolor(color)
73
73
  return
74
74
 
75
75
  def change_text_color(self, color):
76
76
  super().change_text_color(color)
77
77
 
78
- self.text_price.set_color(color)
79
- self.text_volume.set_color(color)
78
+ self.artist_text_price.set_color(color)
79
+ self.artist_text_volume.set_color(color)
80
80
 
81
- self.text_date_price.set_color(color)
82
- self.text_date_volume.set_color(color)
81
+ self.artist_text_date_price.set_color(color)
82
+ self.artist_text_date_volume.set_color(color)
83
83
 
84
- self.text_price_info.set_color(color)
85
- self.text_volume_info.set_color(color)
84
+ self.artist_text_price_info.set_color(color)
85
+ self.artist_text_volume_info.set_color(color)
86
86
  return
87
87
 
88
88
  def change_line_color(self, color):
89
- self.price_crossline.set_edgecolor(color)
90
- self.volume_crossline.set_edgecolor(color)
89
+ self.collection_price_crossline.set_edgecolor(color)
90
+ self.collection_volume_crossline.set_edgecolor(color)
91
91
 
92
- self.price_box.set_edgecolor(color)
93
- self.volume_box.set_edgecolor(color)
92
+ self.collection_price_box.set_edgecolor(color)
93
+ self.collection_volume_box.set_edgecolor(color)
94
94
 
95
- self.text_price.get_bbox_patch().set_edgecolor(color)
96
- self.text_volume.get_bbox_patch().set_edgecolor(color)
95
+ self.artist_text_price.get_bbox_patch().set_edgecolor(color)
96
+ self.artist_text_volume.get_bbox_patch().set_edgecolor(color)
97
97
 
98
- self.text_date_price.get_bbox_patch().set_edgecolor(color)
99
- self.text_date_volume.get_bbox_patch().set_edgecolor(color)
98
+ self.artist_text_date_price.get_bbox_patch().set_edgecolor(color)
99
+ self.artist_text_date_volume.get_bbox_patch().set_edgecolor(color)
100
100
 
101
- self.text_price_info.get_bbox_patch().set_edgecolor(color)
102
- self.text_volume_info.get_bbox_patch().set_edgecolor(color)
101
+ self.artist_text_price_info.get_bbox_patch().set_edgecolor(color)
102
+ self.artist_text_volume_info.get_bbox_patch().set_edgecolor(color)
103
103
  return
104
104
 
105
105
 
@@ -121,6 +121,10 @@ class DataMixin(CollectionMixin):
121
121
  if v in _set_key: raise Exception(f'you can not set "{i}" to column key.\nself.{i}={v!r}')
122
122
  return
123
123
 
124
+ def set_data(self, df, sort_df=True, calc_ma=True, set_candlecolor=True, set_volumecolor=True, calc_info=True, *args, **kwargs):
125
+ super().set_data(df, sort_df, calc_ma, set_candlecolor, set_volumecolor, calc_info, *args, **kwargs)
126
+ return
127
+
124
128
  def _generate_data(self, df: pd.DataFrame, sort_df, calc_ma, set_candlecolor, set_volumecolor, calc_info, *_, **__):
125
129
  super()._generate_data(df, sort_df, calc_ma, set_candlecolor, set_volumecolor, *_, **__)
126
130
 
@@ -144,19 +148,20 @@ class DataMixin(CollectionMixin):
144
148
  self.df['space_box_candle'] = (self.df[self.high] - self.df[self.low]) / 5
145
149
  self.df['bottom_box_candle'] = self.df[self.low] - self.df['space_box_candle']
146
150
  self.df['top_box_candle'] = self.df[self.high] + self.df['space_box_candle']
147
- if self.volume: self.df['max_box_volume'] = self.df[self.volume] * 1.13
151
+ self.df['height_box_candle'] = self.df['top_box_candle'] - self.df['bottom_box_candle']
152
+ if self.volume: self.df['max_box_volume'] = self.df[self.volume] * 1.15
148
153
  return
149
154
 
150
155
  def _set_lim(self, xmin, xmax, simpler=False, set_ma=True):
151
156
  super()._set_lim(xmin, xmax, simpler, set_ma)
152
157
 
153
158
  psub = (self.price_ymax - self.price_ymin)
154
- self.min_candleboxheight = psub / 8
159
+ self.min_height_box_candle = psub / 8
155
160
 
156
161
  pydistance = psub / 20
157
- self.text_date_price.set_y(self.price_ymin + pydistance)
162
+ self.artist_text_date_price.set_y(self.price_ymin + pydistance)
158
163
 
159
- self.min_volumeboxheight = self.volume_ymax / 4
164
+ self.min_height_box_volume = self.volume_ymax / 4
160
165
 
161
166
  vxsub = self.vxmax - self.vxmin
162
167
  self.vmiddle = self.vxmax - int((vxsub) / 2)
@@ -167,11 +172,11 @@ class DataMixin(CollectionMixin):
167
172
  self.veighth = self.vxmin + int((vxsub) / 8)
168
173
 
169
174
  yvolume = self.volume_ymax * 0.85
170
- self.text_date_volume.set_y(yvolume)
175
+ self.artist_text_date_volume.set_y(yvolume)
171
176
 
172
177
  # 정보 텍스트박스
173
- self.text_price_info.set_y(self.price_ymax - pydistance)
174
- self.text_volume_info.set_y(yvolume)
178
+ self.artist_text_price_info.set_y(self.price_ymax - pydistance)
179
+ self.artist_text_volume_info.set_y(yvolume)
175
180
  return
176
181
 
177
182
 
@@ -200,9 +205,15 @@ class EventMixin(DataMixin):
200
205
  if not ax or e.xdata is None or e.ydata is None:
201
206
  self.in_price_chart, self.in_volume_chart = (False, False)
202
207
  else:
203
- self.in_price_chart = ax is self.ax_price
204
- self.in_volume_chart = False if self.in_price_chart else ax is self.ax_volume
205
-
208
+ if ax is self.ax_price:
209
+ self.in_price_chart = True
210
+ self.in_volume_chart = False
211
+ elif ax is self.ax_volume:
212
+ self.in_price_chart = False
213
+ self.in_volume_chart = True
214
+ else:
215
+ self.in_price_chart = False
216
+ self.in_volume_chart = False
206
217
  return
207
218
 
208
219
  def _get_x(self, e: MouseEvent):
@@ -214,112 +225,159 @@ class EventMixin(DataMixin):
214
225
  return
215
226
 
216
227
 
217
- class LineMixin(EventMixin):
228
+ class CrossLineMixin(EventMixin):
218
229
  digit_price, digit_volume = (0, 0)
219
230
  in_candle, in_volumebar = (False, False)
220
231
 
221
232
  def _on_move(self, e):
222
233
  super()._on_move(e)
223
234
 
224
- self._restore_region()
225
-
226
- if self.in_price_chart: self._on_move_price_chart(e)
227
- elif self.in_volume_chart: self._on_move_volume_chart(e)
228
-
229
- self._blit()
235
+ if self.in_price_chart or self.in_volume_chart:
236
+ self._restore_region()
237
+ self._draw_crossline(e, self.in_price_chart)
238
+ self.figure.canvas.blit()
239
+ else:
240
+ if self._erase_crossline():
241
+ self._restore_region()
242
+ self.figure.canvas.blit()
230
243
  return
231
244
 
232
- def _on_move_price_chart(self, e: MouseEvent):
233
- x, y = (e.xdata, e.ydata)
234
-
235
- self.price_crossline.set_segments([((x, self.price_ymin), (x, self.price_ymax)), ((self.vxmin, y), (self.vxmax, y))])
236
- self.volume_crossline.set_segments([((x, 0), (x, self.volume_ymax))])
237
- self._draw_crossline()
238
-
239
- renderer = self.figure.canvas.renderer
245
+ def _erase_crossline(self):
246
+ seg = self.collection_price_crossline.get_segments()
247
+ if seg:
248
+ self.collection_price_crossline.set_segments([])
249
+ return True
250
+ return False
240
251
 
241
- # 가격
242
- self.text_price.set_text(f'{float_to_str(y, self.digit_price)}{self.unit_price}')
243
- self.text_price.set_x(self.v0 if self.veighth < x else self.vsixth)
244
- self.text_price.set_y(y)
245
- self.text_price.draw(renderer)
252
+ def _draw_crossline(self, e: MouseEvent, in_price_chart):
253
+ x, y = (e.xdata, e.ydata)
246
254
 
247
- index = self.intx
248
- if index is None: self.in_candle = False
255
+ if in_price_chart:
256
+ self.collection_price_crossline.set_segments([((x, self.price_ymin), (x, self.price_ymax)), ((self.vxmin, y), (self.vxmax, y))])
257
+ self.collection_volume_crossline.set_segments([((x, 0), (x, self.volume_ymax))])
249
258
  else:
250
- # 기준시간 표시
251
- self.text_date_volume.set_text(f'{self.df[self.date][index]}')
252
- self.text_date_volume.set_x(x)
253
- self.text_date_volume.draw(renderer)
254
-
255
- # 캔들 강조
256
- low = self.df['bottom_box_candle'][index]
257
- high = self.df['top_box_candle'][index]
258
- sub = high - low
259
- if sub < self.min_candleboxheight:
260
- sub = (self.min_candleboxheight - sub) / 2
261
- low -= sub
262
- high += sub
263
-
264
- if high < y or y < low: self.in_candle = False
265
- else:
266
- self.in_candle = True
267
- x1, x2 = (index-0.3, index+1.4)
268
- self.price_box.set_segments([((x1, high), (x2, high), (x2, low), (x1, low), (x1, high))])
269
- self.price_box.draw(renderer)
270
- return
259
+ self.collection_price_crossline.set_segments([((x, self.price_ymin), (x, self.price_ymax))])
260
+ self.collection_volume_crossline.set_segments([((x, 0), (x, self.volume_ymax)), ((self.vxmin, y), (self.vxmax, y))])
271
261
 
272
- def _draw_crossline(self):
273
262
  renderer = self.figure.canvas.renderer
274
- self.price_crossline.draw(renderer)
275
- self.volume_crossline.draw(renderer)
263
+ self.collection_price_crossline.draw(renderer)
264
+ self.collection_volume_crossline.draw(renderer)
265
+
266
+ self._draw_text_artist(e, in_price_chart)
276
267
  return
277
268
 
278
- def _on_move_volume_chart(self, e: MouseEvent):
269
+ def _draw_text_artist(self, e: MouseEvent, in_price_chart):
279
270
  x, y = (e.xdata, e.ydata)
280
271
 
281
- self.price_crossline.set_segments([((x, self.price_ymin), (x, self.price_ymax))])
282
- self.volume_crossline.set_segments([((x, 0), (x, self.volume_ymax)), ((self.vxmin, y), (self.vxmax, y))])
283
- self._draw_crossline()
284
-
285
- if not self.volume: return
286
-
287
272
  renderer = self.figure.canvas.renderer
273
+ if in_price_chart:
274
+ # 가격
275
+ self.artist_text_price.set_text(f'{float_to_str(y, self.digit_price)}{self.unit_price}')
276
+ self.artist_text_price.set_x(self.v0 if self.veighth < x else self.vsixth)
277
+ self.artist_text_price.set_y(y)
278
+ self.artist_text_price.draw(renderer)
279
+
280
+ if self.intx:
281
+ # 기준시간 표시
282
+ self.artist_text_date_volume.set_text(f'{self.df[self.date][self.intx]}')
283
+ self.artist_text_date_volume.set_x(x)
284
+ self.artist_text_date_volume.draw(renderer)
285
+ else:
286
+ # 거래량
287
+ self.artist_text_volume.set_text(f'{float_to_str(y, self.digit_volume)}{self.unit_volume}')
288
+ self.artist_text_volume.set_x(self.v0 if self.veighth < x else self.vsixth)
289
+ self.artist_text_volume.set_y(y)
290
+ self.artist_text_volume.draw(renderer)
291
+
292
+ if self.intx:
293
+ # 기준시간 표시
294
+ self.artist_text_date_price.set_text(f'{self.df[self.date][self.intx]}')
295
+ self.artist_text_date_price.set_x(x)
296
+ self.artist_text_date_price.draw(renderer)
297
+ return
288
298
 
289
- # 거래량
290
- self.text_volume.set_text(f'{float_to_str(y, self.digit_volume)}{self.unit_volume}')
291
- self.text_volume.set_x(self.v0 if self.veighth < x else self.vsixth)
292
- self.text_volume.set_y(y)
293
- self.text_volume.draw(renderer)
294
299
 
295
- index = self.intx
296
- if index is None: self.in_volumebar = False
297
- else:
298
- # 기준시간 표시
299
- self.text_date_price.set_text(f'{self.df[self.date][index]}')
300
- self.text_date_price.set_x(x)
301
- self.text_date_price.draw(renderer)
300
+ class BoxMixin(CrossLineMixin):
301
+ def _draw_crossline(self, e, in_price_chart):
302
+ super()._draw_crossline(e, in_price_chart)
303
+ self._draw_box_artist(e, in_price_chart)
304
+ return
302
305
 
303
- # 거래량 강조
304
- high = self.df[self.volume][index] * 1.15
305
- low = 0
306
- if high < self.min_volumeboxheight: high = self.min_volumeboxheight
306
+ def _draw_box_artist(self, e: MouseEvent, in_price_chart):
307
+ y = e.ydata
307
308
 
308
- if high < y or y < low: self.in_volumebar = False
309
+ renderer = self.figure.canvas.renderer
310
+ if self.intx:
311
+ if in_price_chart:
312
+ # 박스 크기
313
+ high = self.df['top_box_candle'][self.intx]
314
+ low = self.df['bottom_box_candle'][self.intx]
315
+ height = self.df['height_box_candle'][self.intx]
316
+ if height < self.min_height_box_candle:
317
+ sub = (self.min_height_box_candle - height) / 2
318
+ high, low = (high+sub, low-sub)
319
+
320
+ # 커서가 캔들 사이에 있는지 확인
321
+ if high < y or y < low: self.in_candle = False
322
+ else:
323
+ # 캔들 강조
324
+ self.in_candle = True
325
+ x1, x2 = (self.intx-0.3, self.intx+1.4)
326
+ self.collection_price_box.set_segments([((x1, high), (x2, high), (x2, low), (x1, low), (x1, high))])
327
+ self.collection_price_box.draw(renderer)
309
328
  else:
310
- self.in_volumebar = True
311
- x1, x2 = (index-0.3, index+1.4)
312
- self.volume_box.set_segments([((x1, high), (x2, high), (x2, low), (x1, low), (x1, high))])
313
- self.volume_box.draw(renderer)
329
+ # 거래량 강조
330
+ high = self.df['max_box_volume'][self.intx]
331
+ low = 0
332
+ if high < self.min_height_box_volume: high = self.min_height_box_volume
333
+
334
+ if high < y or y < low: self.in_volumebar = False
335
+ else:
336
+ self.in_volumebar = True
337
+ x1, x2 = (self.intx-0.3, self.intx+1.4)
338
+ self.collection_volume_box.set_segments([((x1, high), (x2, high), (x2, low), (x1, low), (x1, high))])
339
+ self.collection_volume_box.draw(renderer)
314
340
  return
315
341
 
316
342
 
317
- format_candleinfo_ko = '{dt}\n\n종가:  {close}\n등락률: {rate}\n대비:  {compare}\n시가:  {open}({rate_open})\n고가:  {high}({rate_high})\n저가:  {low}({rate_low})\n거래량: {volume}({rate_volume})'
318
- format_volumeinfo_ko = '{dt}\n\n거래량:    {volume}\n거래량증가율: {rate_volume}\n대비:     {compare}'
319
- format_candleinfo_en = '{dt}\n\nclose: {close}\nrate: {rate}\ncompare: {compare}\nopen: {open}({rate_open})\nhigh: {high}({rate_high})\nlow: {low}({rate_low})\nvolume: {volume}({rate_volume})'
320
- format_volumeinfo_en = '{dt}\n\nvolume: {volume}\nvolume rate: {rate_volume}\ncompare: {compare}'
321
-
322
- class InfoMixin(LineMixin):
343
+ format_candleinfo_ko = """\
344
+ {dt}
345
+
346
+ 종가:  {close}
347
+ 등락률: {rate}
348
+ 대비:  {compare}
349
+ 시가:  {open}({rate_open})
350
+ 고가:  {high}({rate_high})
351
+ 저가:  {low}({rate_low})
352
+ 거래량: {volume}({rate_volume})\
353
+ """
354
+ format_volumeinfo_ko = """\
355
+ {dt}
356
+
357
+ 거래량:    {volume}
358
+ 거래량증가율: {rate_volume}
359
+ 대비:     {compare}\
360
+ """
361
+ format_candleinfo_en = """\
362
+ {dt}
363
+
364
+ close: {close}
365
+ rate: {rate}
366
+ compare: {compare}
367
+ open: {open}({rate_open})
368
+ high: {high}({rate_high})
369
+ low: {low}({rate_low})
370
+ volume: {volume}({rate_volume})\
371
+ """
372
+ format_volumeinfo_en = """\
373
+ {dt}
374
+
375
+ volume: {volume}
376
+ volume rate: {rate_volume}
377
+ compare: {compare}\
378
+ """
379
+
380
+ class InfoMixin(BoxMixin):
323
381
  fraction = False
324
382
  format_candleinfo = format_candleinfo_ko
325
383
  format_volumeinfo = format_volumeinfo_ko
@@ -330,58 +388,58 @@ class InfoMixin(LineMixin):
330
388
  self._length_text = self.df[(self.volume if self.volume else self.high)].apply(lambda x: len(f'{x:,}')).max()
331
389
  return
332
390
 
333
- def _on_move_price_chart(self, e):
334
- super()._on_move_price_chart(e)
391
+ def _draw_box_artist(self, e, in_price_chart):
392
+ super()._draw_box_artist(e, in_price_chart)
335
393
 
336
- # 캔들 강조 확인
337
- if not self.in_candle: return
394
+ if self.intx:
395
+ if self.in_candle: self._draw_candle_info_artist(e)
396
+ elif self.in_volumebar: self._draw_volume_info_artist(e)
397
+ return
338
398
 
399
+ def _draw_candle_info_artist(self, e: MouseEvent):
339
400
  # 캔들 정보
340
- self.text_price_info.set_text(self._get_info(self.intx))
401
+ self.artist_text_price_info.set_text(self._get_info(self.intx))
341
402
 
342
- if self.vmiddle < e.xdata: self.text_price_info.set_x(self.v0)
403
+ # 정보 텍스트를 중앙에 몰리게 설정할 수도 있지만,
404
+ # 그런 경우 차트를 가리므로 좌우 끝단에 위치하도록 설정
405
+ if self.vmiddle < e.xdata:
406
+ self.artist_text_price_info.set_x(self.v0)
343
407
  else:
344
- # self.text_price_info.set_x(self.vmax - self.x_distance)
345
- # self.text_price_info.set_horizontalalignment('right')
408
+ # self.artist_text_price_info.set_x(self.vmax - self.x_distance)
409
+ # self.artist_text_price_info.set_horizontalalignment('right')
346
410
  # 텍스트박스 크기 가져오기
347
- bbox = self.text_price_info.get_window_extent().transformed(self.ax_price.transData.inverted())
411
+ bbox = self.artist_text_price_info.get_window_extent().transformed(self.ax_price.transData.inverted())
348
412
  width = bbox.x1 - bbox.x0
349
- self.text_price_info.set_x(self.v1 - width)
413
+ self.artist_text_price_info.set_x(self.v1 - width)
350
414
 
351
- self.text_price_info.draw(self.figure.canvas.renderer)
415
+ self.artist_text_price_info.draw(self.figure.canvas.renderer)
352
416
  return
353
417
 
354
- def _on_move_volume_chart(self, e):
355
- super()._on_move_volume_chart(e)
356
-
357
- # 거래량 강조 확인
358
- if not self.in_volumebar: return
359
-
418
+ def _draw_volume_info_artist(self, e: MouseEvent):
360
419
  # 거래량 정보
361
- self.text_volume_info.set_text(self._get_info(self.intx, is_price=False))
420
+ self.artist_text_volume_info.set_text(self._get_info(self.intx, is_price=False))
362
421
 
363
- if self.vmiddle < e.xdata: self.text_volume_info.set_x(self.v0)
422
+ if self.vmiddle < e.xdata: self.artist_text_volume_info.set_x(self.v0)
364
423
  else:
365
- # self.text_volume_info.set_x(self.vmax - self.x_distance)
366
- # self.text_volume_info.set_horizontalalignment('right')
424
+ # self.artist_text_volume_info.set_x(self.vmax - self.x_distance)
425
+ # self.artist_text_volume_info.set_horizontalalignment('right')
367
426
  # 텍스트박스 크기 가져오기
368
- bbox = self.text_volume_info.get_window_extent().transformed(self.ax_price.transData.inverted())
427
+ bbox = self.artist_text_volume_info.get_window_extent().transformed(self.ax_price.transData.inverted())
369
428
  width = bbox.x1 - bbox.x0
370
- self.text_volume_info.set_x(self.v1 - width)
429
+ self.artist_text_volume_info.set_x(self.v1 - width)
371
430
 
372
- self.text_volume_info.draw(self.figure.canvas.renderer)
431
+ self.artist_text_volume_info.draw(self.figure.canvas.renderer)
373
432
  return
374
433
 
375
434
  def _get_info(self, index, is_price=True):
376
435
  dt = self.df[self.date][index]
377
- if not self.volume:
378
- v, vr = ('-', '-%')
436
+ if not self.volume: v, vr = ('-', '-')
379
437
  else:
380
438
  v = self.df[self.volume][index]
381
439
  v = float_to_str(v, self.digit_volume)
382
440
  # if not v % 1: v = int(v)
383
441
  vr = self.df['rate_volume'][index]
384
- vr = f'{vr:+06,.2f}%'
442
+ vr = f'{vr:+06,.2f}'
385
443
 
386
444
  if is_price:
387
445
  o, h, l, c = (self.df[self.Open][index], self.df[self.high][index], self.df[self.low][index], self.df[self.close][index])
@@ -418,7 +476,7 @@ class InfoMixin(LineMixin):
418
476
  open=f'{o:>{self._length_text}}{self.unit_price}', rate_open=f'{Or:+06,.2f}%',
419
477
  high=f'{h:>{self._length_text}}{self.unit_price}', rate_high=f'{hr:+06,.2f}%',
420
478
  low=f'{l:>{self._length_text}}{self.unit_price}', rate_low=f'{lr:+06,.2f}%',
421
- volume=f'{v:>{self._length_text}}{self.unit_volume}', rate_volume=vr,
479
+ volume=f'{v:>{self._length_text}}{self.unit_volume}', rate_volume=f'{vr}%',
422
480
  )
423
481
  else:
424
482
  o, h, l, c = (float_to_str(o, self.digit_price), float_to_str(h, self.digit_price), float_to_str(l, self.digit_price), float_to_str(c, self.digit_price))
@@ -432,7 +490,7 @@ class InfoMixin(LineMixin):
432
490
  open=f'{o:>{self._length_text}}{self.unit_price}', rate_open=f'{Or:+06,.2f}%',
433
491
  high=f'{h:>{self._length_text}}{self.unit_price}', rate_high=f'{hr:+06,.2f}%',
434
492
  low=f'{l:>{self._length_text}}{self.unit_price}', rate_low=f'{lr:+06,.2f}%',
435
- volume=f'{v:>{self._length_text}}{self.unit_volume}', rate_volume=vr,
493
+ volume=f'{v:>{self._length_text}}{self.unit_volume}', rate_volume=f'{vr}%',
436
494
  )
437
495
  elif self.volume:
438
496
  compare = self.df['compare_volume'][index]
@@ -440,10 +498,11 @@ class InfoMixin(LineMixin):
440
498
  text = self.format_volumeinfo.format(
441
499
  dt=dt,
442
500
  volume=f'{v:>{self._length_text}}{self.unit_volume}',
443
- rate_volume=f'{vr:>{self._length_text}}',
501
+ rate_volume=f'{vr:>{self._length_text}}%',
444
502
  compare=f'{com:>{self._length_text}}{self.unit_volume}',
445
503
  )
446
504
  else: text = ''
505
+
447
506
  return text
448
507
 
449
508
 
@@ -452,17 +511,13 @@ class BaseMixin(InfoMixin):
452
511
 
453
512
 
454
513
  class Chart(BaseMixin, Mixin):
455
- def _add_collection(self):
456
- super()._add_collection()
457
- return self.add_artist()
458
-
459
514
  def _draw_artist(self):
460
515
  super()._draw_artist()
461
516
  return self.draw_artist()
462
517
 
463
- def _get_segments(self):
464
- self.generate_data()
465
- return super()._get_segments()
518
+ def _set_lim(self, xmin, xmax, simpler=False, set_ma=True):
519
+ super()._set_lim(xmin, xmax, simpler, set_ma)
520
+ return self.on_change_xlim(xmin, xmax, simpler, set_ma)
466
521
 
467
522
  def _on_draw(self, e):
468
523
  super()._on_draw(e)
@@ -472,21 +527,6 @@ class Chart(BaseMixin, Mixin):
472
527
  self.on_pick(e)
473
528
  return super()._on_pick(e)
474
529
 
475
- def _set_candle_segments(self, index_start, index_end):
476
- super()._set_candle_segments(index_start, index_end)
477
- self.set_segment(index_start, index_end)
478
- return
479
-
480
- def _set_wick_segments(self, index_start, index_end, simpler=False):
481
- super()._set_wick_segments(index_start, index_end, simpler)
482
- self.set_segment(index_start, index_end, simpler)
483
- return
484
-
485
- def _set_line_segments(self, index_start, index_end, simpler=False, set_ma=True):
486
- super()._set_line_segments(index_start, index_end, simpler, set_ma)
487
- self.set_segment(index_start, index_end, simpler, set_ma)
488
- return
489
-
490
530
  def _on_move(self, e):
491
531
  super()._on_move(e)
492
532
  return self.on_move(e)