seolpyo-mplchart 1.3.1.2__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.
- seolpyo_mplchart/__init__.py +8 -8
- seolpyo_mplchart/_cursor.py +221 -181
- seolpyo_mplchart/_draw.py +211 -235
- seolpyo_mplchart/_slider.py +136 -137
- {seolpyo_mplchart-1.3.1.2.dist-info → seolpyo_mplchart-1.4.0.dist-info}/METADATA +1 -1
- {seolpyo_mplchart-1.3.1.2.dist-info → seolpyo_mplchart-1.4.0.dist-info}/RECORD +8 -8
- {seolpyo_mplchart-1.3.1.2.dist-info → seolpyo_mplchart-1.4.0.dist-info}/WHEEL +0 -0
- {seolpyo_mplchart-1.3.1.2.dist-info → seolpyo_mplchart-1.4.0.dist-info}/top_level.txt +0 -0
seolpyo_mplchart/_draw.py
CHANGED
|
@@ -10,16 +10,12 @@ from ._base import Base
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class Mixin:
|
|
13
|
-
def add_artist(self):
|
|
14
|
-
"This method work when ```__init__()``` run."
|
|
15
|
-
return
|
|
16
|
-
|
|
17
13
|
def draw_artist(self):
|
|
18
14
|
"This method work before ```figure.canvas.blit()```."
|
|
19
15
|
return
|
|
20
16
|
|
|
21
|
-
def
|
|
22
|
-
"This method work
|
|
17
|
+
def on_change_xlim(self, xmin, xmax, simpler=False, set_ma=True):
|
|
18
|
+
"This method work if xlim change."
|
|
23
19
|
return
|
|
24
20
|
|
|
25
21
|
def on_draw(self, e):
|
|
@@ -30,10 +26,6 @@ class Mixin:
|
|
|
30
26
|
"If draw pick active, This method work."
|
|
31
27
|
return
|
|
32
28
|
|
|
33
|
-
def set_segment(self, xmin, xmax, simpler=False, set_ma=True):
|
|
34
|
-
"This method work if xlim change."
|
|
35
|
-
return
|
|
36
|
-
|
|
37
29
|
|
|
38
30
|
class CollectionMixin(Base):
|
|
39
31
|
facecolor_volume, edgecolor_volume = ('#1f77b4', 'k')
|
|
@@ -46,23 +38,23 @@ class CollectionMixin(Base):
|
|
|
46
38
|
return
|
|
47
39
|
|
|
48
40
|
def _add_collection(self):
|
|
49
|
-
self.
|
|
50
|
-
|
|
41
|
+
self.artist_watermark = Text(
|
|
42
|
+
x=0.5, y=0.5, text=self.watermark,
|
|
43
|
+
animated=True,
|
|
44
|
+
fontsize=20, color=self.color_tick_label, alpha=0.2,
|
|
45
|
+
horizontalalignment='center', verticalalignment='center',
|
|
46
|
+
transform=self.ax_price.transAxes
|
|
47
|
+
)
|
|
48
|
+
self.ax_price.add_artist(self.artist_watermark)
|
|
51
49
|
|
|
52
50
|
self.collection_candle = LineCollection([], animated=True, linewidths=0.8)
|
|
53
51
|
self.ax_price.add_collection(self.collection_candle)
|
|
54
52
|
|
|
53
|
+
self.collection_ma = LineCollection([], animated=True, linewidths=1)
|
|
54
|
+
self.ax_price.add_collection(self.collection_ma)
|
|
55
|
+
|
|
55
56
|
self.collection_volume = LineCollection([], animated=True, linewidths=1)
|
|
56
57
|
self.ax_volume.add_collection(self.collection_volume)
|
|
57
|
-
|
|
58
|
-
x = (self.adjust['right']-self.adjust['left']) / 2
|
|
59
|
-
self.text_watermark = Text(
|
|
60
|
-
x=x, y=0.51, text=self.watermark,
|
|
61
|
-
animated=True,
|
|
62
|
-
fontsize=20, color=self.color_tick_label, alpha=0.2,
|
|
63
|
-
horizontalalignment='center', verticalalignment='center',
|
|
64
|
-
)
|
|
65
|
-
self.figure.add_artist(self.text_watermark)
|
|
66
58
|
return
|
|
67
59
|
|
|
68
60
|
def change_background_color(self, color):
|
|
@@ -86,7 +78,7 @@ class CollectionMixin(Base):
|
|
|
86
78
|
return
|
|
87
79
|
|
|
88
80
|
def change_text_color(self, color):
|
|
89
|
-
self.
|
|
81
|
+
self.artist_watermark.set_color(color)
|
|
90
82
|
legends = self.ax_legend.get_legend()
|
|
91
83
|
if legends:
|
|
92
84
|
for i in legends.texts: i.set_color(color)
|
|
@@ -95,13 +87,64 @@ class CollectionMixin(Base):
|
|
|
95
87
|
def change_line_color(self, color): return
|
|
96
88
|
|
|
97
89
|
|
|
90
|
+
class DrawMixin(CollectionMixin):
|
|
91
|
+
candle_on_ma = True
|
|
92
|
+
|
|
93
|
+
def __init__(self, *args, **kwargs):
|
|
94
|
+
super().__init__(*args, **kwargs)
|
|
95
|
+
|
|
96
|
+
self._connect_event()
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
def _connect_event(self):
|
|
100
|
+
self.figure.canvas.mpl_connect('draw_event', lambda x: self._on_draw(x))
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
def _on_draw(self, e):
|
|
104
|
+
self._draw_artist()
|
|
105
|
+
self.figure.canvas.blit()
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
def _draw_artist(self):
|
|
109
|
+
self._draw_ax_price()
|
|
110
|
+
self._draw_ax_volume()
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
def _draw_ax_price(self):
|
|
114
|
+
renderer = self.figure.canvas.renderer
|
|
115
|
+
|
|
116
|
+
self.ax_price.xaxis.draw(renderer)
|
|
117
|
+
self.ax_price.yaxis.draw(renderer)
|
|
118
|
+
|
|
119
|
+
if self.candle_on_ma:
|
|
120
|
+
self.collection_ma.draw(renderer)
|
|
121
|
+
self.collection_candle.draw(renderer)
|
|
122
|
+
else:
|
|
123
|
+
self.collection_candle.draw(renderer)
|
|
124
|
+
self.collection_ma.draw(renderer)
|
|
125
|
+
|
|
126
|
+
if self.watermark:
|
|
127
|
+
self.artist_watermark.set_text(self.watermark)
|
|
128
|
+
self.artist_watermark.draw(renderer)
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
def _draw_ax_volume(self):
|
|
132
|
+
renderer = self.figure.canvas.renderer
|
|
133
|
+
|
|
134
|
+
self.ax_volume.xaxis.draw(renderer)
|
|
135
|
+
self.ax_volume.yaxis.draw(renderer)
|
|
136
|
+
|
|
137
|
+
self.collection_volume.draw(renderer)
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
|
|
98
141
|
_set_key = {
|
|
99
142
|
'x', 'zero', 'close_pre', 'ymax_volume',
|
|
100
143
|
'top_candle', 'bottom_candle', 'left_candle', 'right_candle',
|
|
101
144
|
'left_volume', 'right_volume',
|
|
102
145
|
}
|
|
103
146
|
|
|
104
|
-
class DataMixin(
|
|
147
|
+
class DataMixin(DrawMixin):
|
|
105
148
|
df: pd.DataFrame
|
|
106
149
|
|
|
107
150
|
date = 'date'
|
|
@@ -114,8 +157,8 @@ class DataMixin(CollectionMixin):
|
|
|
114
157
|
color_flat = 'k'
|
|
115
158
|
color_up_down, color_down_up = ('w', 'w')
|
|
116
159
|
|
|
117
|
-
color_volume_up, color_volume_down = ('#
|
|
118
|
-
color_volume_flat = '#
|
|
160
|
+
color_volume_up, color_volume_down = ('#FF5555', '#5CA8F4')
|
|
161
|
+
color_volume_flat = '#909090'
|
|
119
162
|
|
|
120
163
|
set_candlecolor, set_volumecolor = (True, True)
|
|
121
164
|
|
|
@@ -168,7 +211,6 @@ class DataMixin(CollectionMixin):
|
|
|
168
211
|
v = getattr(self, i)
|
|
169
212
|
if v in _set_key: raise Exception(f'you can not set "{i}" to column key.\nself.{i}={v!r}')
|
|
170
213
|
|
|
171
|
-
|
|
172
214
|
if not self.set_candlecolor:
|
|
173
215
|
keys = set(df.keys())
|
|
174
216
|
for i in ('facecolor', 'edgecolor', 'facecolor_volume', 'edgecolor_volume',):
|
|
@@ -180,23 +222,14 @@ class DataMixin(CollectionMixin):
|
|
|
180
222
|
for i in ('facecolor_volume', 'edgecolor_volume',):
|
|
181
223
|
if i not in keys:
|
|
182
224
|
raise Exception(f'"{i}" column not in DataFrame.\nadd column or set set_volumecolor=True.')
|
|
183
|
-
|
|
184
225
|
return
|
|
185
226
|
|
|
186
227
|
|
|
187
|
-
class
|
|
188
|
-
_visible_ma = set()
|
|
189
|
-
|
|
190
|
-
limit_candle = 800
|
|
191
|
-
limit_wick = 4_000
|
|
192
|
-
limit_volume = 200
|
|
193
|
-
|
|
228
|
+
class CandleSegmentMixin(DataMixin):
|
|
194
229
|
color_priceline = 'k'
|
|
195
|
-
|
|
196
|
-
# https://matplotlib.org/stable/gallery/color/named_colors.html
|
|
197
|
-
list_macolor = ('#B22222', '#228B22', '#1E90FF', '#FF8C00', '#4B0082')
|
|
230
|
+
limit_candle = 800
|
|
198
231
|
|
|
199
|
-
def
|
|
232
|
+
def _create_candle_segments(self):
|
|
200
233
|
# 캔들 세그먼트
|
|
201
234
|
segment_candle = self.df[[
|
|
202
235
|
'x', self.high,
|
|
@@ -225,28 +258,10 @@ class SegmentMixin(DataMixin):
|
|
|
225
258
|
segment_priceline = segment_wick = self.df[['x', self.close]].values
|
|
226
259
|
self.segment_priceline = segment_priceline.reshape(1, *segment_wick.shape)
|
|
227
260
|
|
|
228
|
-
|
|
229
|
-
# 거래량 바 세그먼트
|
|
230
|
-
segment_volume = self.df[[
|
|
231
|
-
'left_volume', 'zero',
|
|
232
|
-
'left_volume', self.volume,
|
|
233
|
-
'right_volume', self.volume,
|
|
234
|
-
'right_volume', 'zero',
|
|
235
|
-
]].values
|
|
236
|
-
self.segment_volume = segment_volume.reshape(segment_volume.shape[0], 4, 2)
|
|
237
|
-
|
|
238
|
-
# 거래량 심지 세그먼트
|
|
239
|
-
segment_volume_wick = self.df[[
|
|
240
|
-
'x', 'zero',
|
|
241
|
-
'x', self.volume,
|
|
242
|
-
]].values
|
|
243
|
-
self.segment_volume_wick = segment_volume_wick.reshape(segment_volume_wick.shape[0], 2, 2)
|
|
244
|
-
|
|
245
|
-
self._get_ma_segment()
|
|
246
|
-
self._get_color_segment()
|
|
261
|
+
self._create_candle_color_segments()
|
|
247
262
|
return
|
|
248
263
|
|
|
249
|
-
def
|
|
264
|
+
def _create_candle_color_segments(self):
|
|
250
265
|
if self.set_candlecolor:
|
|
251
266
|
# 양봉
|
|
252
267
|
self.df.loc[:, ['facecolor', 'edgecolor']] = (self.color_up, self.color_up)
|
|
@@ -265,20 +280,48 @@ class SegmentMixin(DataMixin):
|
|
|
265
280
|
|
|
266
281
|
self.facecolor_candle = self.df['facecolor'].values
|
|
267
282
|
self.edgecolor_candle = self.df['edgecolor'].values
|
|
283
|
+
return
|
|
268
284
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
self.df.loc[self.df[self.close] < self.df['close_pre'], ['facecolor_volume', 'edgecolor_volume']] = (self.color_volume_down, self.color_volume_down)
|
|
275
|
-
if self.color_up != self.color_flat:
|
|
276
|
-
# 전일과 동일
|
|
277
|
-
self.df.loc[self.df[self.close] == self.df['close_pre'], ['facecolor_volume', 'edgecolor_volume']] = (self.color_volume_flat, self.color_volume_flat)
|
|
285
|
+
def _set_candle_segments(self, index_start, index_end):
|
|
286
|
+
self.collection_candle.set_segments(self.segment_candle[index_start:index_end])
|
|
287
|
+
self.collection_candle.set_facecolor(self.facecolor_candle[index_start:index_end])
|
|
288
|
+
self.collection_candle.set_edgecolor(self.edgecolor_candle[index_start:index_end])
|
|
289
|
+
return
|
|
278
290
|
|
|
279
|
-
|
|
280
|
-
self.
|
|
291
|
+
def _set_candle_wick_segments(self, index_start, index_end):
|
|
292
|
+
self.collection_candle.set_segments(self.segment_candle_wick[index_start:index_end])
|
|
293
|
+
self.collection_candle.set_facecolor([])
|
|
294
|
+
self.collection_candle.set_edgecolor(self.edgecolor_candle[index_start:index_end])
|
|
295
|
+
return
|
|
296
|
+
|
|
297
|
+
def _set_priceline_segments(self, index_start, index_end):
|
|
298
|
+
self.collection_candle.set_segments(self.segment_priceline[:, index_start:index_end])
|
|
299
|
+
self.collection_candle.set_facecolor([])
|
|
300
|
+
self.collection_candle.set_edgecolor(self.color_priceline)
|
|
301
|
+
return
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class MaSegmentMixin(CandleSegmentMixin):
|
|
305
|
+
format_ma = '{}일선'
|
|
306
|
+
# https://matplotlib.org/stable/gallery/color/named_colors.html
|
|
307
|
+
list_macolor = ('#006400', '#8B008B', '#FFA500', '#0000FF', '#FF0000')
|
|
308
|
+
|
|
309
|
+
_visible_ma = set()
|
|
310
|
+
|
|
311
|
+
def _create_ma_segments(self):
|
|
312
|
+
# 주가 차트 가격이동평균선
|
|
313
|
+
key_ma = []
|
|
314
|
+
for i in reversed(self.list_ma):
|
|
315
|
+
key_ma.append('x')
|
|
316
|
+
key_ma.append(f'ma{i}')
|
|
317
|
+
if key_ma:
|
|
318
|
+
segment_ma = self.df[key_ma].values
|
|
319
|
+
self.segment_ma = segment_ma.reshape(segment_ma.shape[0], len(self.list_ma), 2).swapaxes(0, 1)
|
|
281
320
|
|
|
321
|
+
self._create_ma_color_segments()
|
|
322
|
+
return
|
|
323
|
+
|
|
324
|
+
def _create_ma_color_segments(self):
|
|
282
325
|
# 기존 legend 제거
|
|
283
326
|
legends = self.ax_legend.get_legend()
|
|
284
327
|
if legends: legends.remove()
|
|
@@ -286,7 +329,7 @@ class SegmentMixin(DataMixin):
|
|
|
286
329
|
self._visible_ma.clear()
|
|
287
330
|
|
|
288
331
|
list_handle, list_label, list_color = ([], [], [])
|
|
289
|
-
arr =
|
|
332
|
+
arr = [0, 1]
|
|
290
333
|
for n, i in enumerate(self.list_ma):
|
|
291
334
|
try: c = self.list_macolor[n]
|
|
292
335
|
except: c = self.color_priceline
|
|
@@ -308,106 +351,108 @@ class SegmentMixin(DataMixin):
|
|
|
308
351
|
for i in legends.legend_handles: i.set_picker(5)
|
|
309
352
|
return
|
|
310
353
|
|
|
311
|
-
def
|
|
312
|
-
if not self.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
for i in reversed(self.list_ma):
|
|
317
|
-
key_ma.append('x')
|
|
318
|
-
key_ma.append(f'ma{i}')
|
|
319
|
-
segment_ma = self.df[key_ma].values
|
|
320
|
-
self.segment_ma = segment_ma.reshape(segment_ma.shape[0], len(self.list_ma), 2).swapaxes(0, 1)
|
|
354
|
+
def _set_ma_segments(self, index_start, index_end, set_ma):
|
|
355
|
+
if not set_ma: self.collection_ma.set_segments([])
|
|
356
|
+
else:
|
|
357
|
+
self.collection_ma.set_segments(self.segment_ma[:, index_start:index_end])
|
|
358
|
+
self.collection_ma.set_edgecolor(self.edgecolor_ma)
|
|
321
359
|
return
|
|
322
360
|
|
|
323
|
-
def _set_segments(self, index_start, index_end, simpler, set_ma):
|
|
324
|
-
indsub = index_end - index_start
|
|
325
|
-
if index_start < 0: index_start = 0
|
|
326
|
-
if index_end < 1: index_end = 1
|
|
327
361
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
self._set_candle_segments(index_start, index_end)
|
|
331
|
-
elif indsub < self.limit_wick:
|
|
332
|
-
self._set_wick_segments(index_start, index_end, simpler)
|
|
333
|
-
else:
|
|
334
|
-
self._set_line_segments(index_start, index_end, simpler, set_ma)
|
|
335
|
-
return
|
|
362
|
+
class VolumeSegmentMixin(MaSegmentMixin):
|
|
363
|
+
limit_volume = 200
|
|
336
364
|
|
|
337
|
-
def
|
|
338
|
-
|
|
339
|
-
self.
|
|
340
|
-
|
|
365
|
+
def _create_volume_segments(self):
|
|
366
|
+
# 거래량 바 세그먼트
|
|
367
|
+
segment_volume = self.df[[
|
|
368
|
+
'left_volume', 'zero',
|
|
369
|
+
'left_volume', self.volume,
|
|
370
|
+
'right_volume', self.volume,
|
|
371
|
+
'right_volume', 'zero',
|
|
372
|
+
]].values
|
|
373
|
+
self.segment_volume = segment_volume.reshape(segment_volume.shape[0], 4, 2)
|
|
341
374
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
self.
|
|
346
|
-
|
|
375
|
+
# 거래량 심지 세그먼트
|
|
376
|
+
segment_volume_wick = self.df[[
|
|
377
|
+
'x', 'zero',
|
|
378
|
+
'x', self.volume,
|
|
379
|
+
]].values
|
|
380
|
+
self.segment_volume_wick = segment_volume_wick.reshape(segment_volume_wick.shape[0], 2, 2)
|
|
347
381
|
|
|
348
|
-
self.
|
|
349
|
-
self.collection_ma.set_edgecolor(self.edgecolor_ma)
|
|
382
|
+
self._create_volume_color_segments()
|
|
350
383
|
return
|
|
351
384
|
|
|
352
|
-
def
|
|
353
|
-
self.
|
|
354
|
-
|
|
355
|
-
|
|
385
|
+
def _create_volume_color_segments(self):
|
|
386
|
+
if self.set_volumecolor:
|
|
387
|
+
columns = ['facecolor_volume', 'edgecolor_volume']
|
|
388
|
+
# 거래량
|
|
389
|
+
self.df.loc[:, columns] = (self.color_volume_up, self.color_volume_up)
|
|
390
|
+
if self.color_up != self.color_down:
|
|
391
|
+
# 전일대비 하락
|
|
392
|
+
condition = self.df[self.close] < self.df['close_pre']
|
|
393
|
+
self.df.loc[condition, columns] = (self.color_volume_down, self.color_volume_down)
|
|
394
|
+
if self.color_up != self.color_flat:
|
|
395
|
+
# 전일과 동일
|
|
396
|
+
condition = self.df[self.close] == self.df['close_pre']
|
|
397
|
+
self.df.loc[condition, columns] = (self.color_volume_flat, self.color_volume_flat)
|
|
356
398
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
seg_edgecolor_volume = self.edgecolor_volume[index_start:index_end]
|
|
361
|
-
if simpler:
|
|
362
|
-
values = seg_volume[:, 1, 1]
|
|
363
|
-
top_index = np.argsort(-values)[:self.limit_volume]
|
|
364
|
-
seg_volume = seg_volume[top_index]
|
|
365
|
-
seg_facecolor_volume = seg_facecolor_volume[top_index]
|
|
366
|
-
seg_edgecolor_volume = seg_edgecolor_volume[top_index]
|
|
367
|
-
self.collection_volume.set_segments(seg_volume)
|
|
368
|
-
self.collection_volume.set_linewidth(1.3)
|
|
369
|
-
self.collection_volume.set_facecolor(seg_facecolor_volume)
|
|
370
|
-
self.collection_volume.set_edgecolor(seg_edgecolor_volume)
|
|
371
|
-
|
|
372
|
-
self.collection_ma.set_segments(self.segment_ma[:, index_start:index_end])
|
|
373
|
-
self.collection_ma.set_edgecolor(self.edgecolor_ma)
|
|
374
|
-
return
|
|
375
|
-
|
|
376
|
-
def _set_line_segments(self, index_start, index_end, simpler=False, set_ma=True):
|
|
377
|
-
self.collection_candle.set_segments(self.segment_priceline[:, index_start:index_end])
|
|
378
|
-
self.collection_candle.set_facecolor([])
|
|
379
|
-
self.collection_candle.set_edgecolor(self.color_priceline)
|
|
399
|
+
self.facecolor_volume = self.df['facecolor_volume'].values
|
|
400
|
+
self.edgecolor_volume = self.df['edgecolor_volume'].values
|
|
401
|
+
return
|
|
380
402
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
top_index = np.argsort(-values)[:self.limit_volume]
|
|
388
|
-
seg_volume = seg_volume[top_index]
|
|
389
|
-
seg_facecolor_volume = seg_facecolor_volume[top_index]
|
|
390
|
-
seg_edgecolor_volume = seg_edgecolor_volume[top_index]
|
|
391
|
-
self.collection_volume.set_segments(seg_volume)
|
|
392
|
-
self.collection_volume.set_linewidth(1.3)
|
|
393
|
-
self.collection_volume.set_facecolor(seg_facecolor_volume)
|
|
394
|
-
self.collection_volume.set_edgecolor(seg_edgecolor_volume)
|
|
403
|
+
def _set_volume_segments(self, index_start, index_end):
|
|
404
|
+
self.collection_volume.set_segments(self.segment_volume[index_start:index_end])
|
|
405
|
+
self.collection_volume.set_linewidth(0.7)
|
|
406
|
+
self.collection_volume.set_facecolor(self.facecolor_volume[index_start:index_end])
|
|
407
|
+
self.collection_volume.set_edgecolor(self.edgecolor_volume[index_start:index_end])
|
|
408
|
+
return
|
|
395
409
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
410
|
+
def _set_volume_wick_segments(self, index_start, index_end, simpler):
|
|
411
|
+
seg_volume = self.segment_volume_wick[index_start:index_end]
|
|
412
|
+
seg_facecolor_volume = self.facecolor_volume[index_start:index_end]
|
|
413
|
+
seg_edgecolor_volume = self.edgecolor_volume[index_start:index_end]
|
|
414
|
+
if simpler:
|
|
415
|
+
values = seg_volume[:, 1, 1]
|
|
416
|
+
top_index = np.argsort(-values)[:self.limit_volume]
|
|
417
|
+
seg_volume = seg_volume[top_index]
|
|
418
|
+
seg_facecolor_volume = seg_facecolor_volume[top_index]
|
|
419
|
+
seg_edgecolor_volume = seg_edgecolor_volume[top_index]
|
|
420
|
+
self.collection_volume.set_segments(seg_volume)
|
|
421
|
+
self.collection_volume.set_linewidth(1.3)
|
|
422
|
+
self.collection_volume.set_facecolor(seg_facecolor_volume)
|
|
423
|
+
self.collection_volume.set_edgecolor(seg_edgecolor_volume)
|
|
400
424
|
return
|
|
401
425
|
|
|
402
426
|
|
|
403
|
-
class
|
|
404
|
-
|
|
405
|
-
super().__init__(*args, **kwargs)
|
|
427
|
+
class SegmentMixin(VolumeSegmentMixin):
|
|
428
|
+
limit_wick = 4_000
|
|
406
429
|
|
|
407
|
-
|
|
430
|
+
def _create_segments(self):
|
|
431
|
+
self._create_candle_segments()
|
|
432
|
+
self._create_ma_segments()
|
|
433
|
+
if self.volume: self._create_volume_segments()
|
|
408
434
|
return
|
|
409
435
|
|
|
436
|
+
def _set_collection_segments(self, index_start, index_end, indsub, simpler, set_ma=True):
|
|
437
|
+
if indsub < self.limit_candle:
|
|
438
|
+
self._set_candle_segments(index_start, index_end)
|
|
439
|
+
self._set_ma_segments(index_start, index_end, set_ma)
|
|
440
|
+
if self.volume: self._set_volume_segments(index_start, index_end)
|
|
441
|
+
elif indsub < self.limit_wick:
|
|
442
|
+
self._set_candle_wick_segments(index_start, index_end)
|
|
443
|
+
self._set_ma_segments(index_start, index_end, set_ma)
|
|
444
|
+
if self.volume: self._set_volume_wick_segments(index_start, index_end, simpler)
|
|
445
|
+
else:
|
|
446
|
+
self._set_priceline_segments(index_start, index_end)
|
|
447
|
+
self._set_ma_segments(index_start, index_end, set_ma)
|
|
448
|
+
if self.volume: self._set_volume_wick_segments(index_start, index_end, simpler)
|
|
449
|
+
return
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class EventMixin(SegmentMixin):
|
|
410
453
|
def _connect_event(self):
|
|
454
|
+
super()._connect_event()
|
|
455
|
+
|
|
411
456
|
self.figure.canvas.mpl_connect('pick_event', lambda x: self._on_pick(x))
|
|
412
457
|
return
|
|
413
458
|
|
|
@@ -417,12 +462,9 @@ class EventMixin(SegmentMixin):
|
|
|
417
462
|
|
|
418
463
|
def _pick_ma_action(self, e: PickEvent):
|
|
419
464
|
handle = e.artist
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
else:
|
|
424
|
-
visible = False
|
|
425
|
-
handle.set_alpha(0.2)
|
|
465
|
+
|
|
466
|
+
visible = handle.get_alpha() == 0.2
|
|
467
|
+
handle.set_alpha(1.0 if visible else 0.2)
|
|
426
468
|
|
|
427
469
|
n = int(handle.get_label())
|
|
428
470
|
if visible: self._visible_ma = {i for i in self.list_ma if i in self._visible_ma or i == n}
|
|
@@ -431,68 +473,25 @@ class EventMixin(SegmentMixin):
|
|
|
431
473
|
alphas = [(1 if i in self._visible_ma else 0) for i in reversed(self.list_ma)]
|
|
432
474
|
self.collection_ma.set_alpha(alphas)
|
|
433
475
|
|
|
434
|
-
self._draw()
|
|
435
|
-
return
|
|
436
|
-
|
|
437
|
-
def _draw(self):
|
|
438
476
|
self.figure.canvas.draw()
|
|
439
477
|
return
|
|
440
478
|
|
|
441
479
|
|
|
442
|
-
class
|
|
480
|
+
class LimMixin(EventMixin):
|
|
443
481
|
candle_on_ma = True
|
|
444
482
|
|
|
445
483
|
def set_data(self, df, sort_df=True, calc_ma=True, set_candlecolor=True, set_volumecolor=True, *args, **kwargs):
|
|
446
484
|
self._generate_data(df, sort_df, calc_ma, set_candlecolor, set_volumecolor, *args, **kwargs)
|
|
447
|
-
self.
|
|
485
|
+
self._create_segments()
|
|
448
486
|
|
|
449
487
|
vmin, vmax = self.get_default_lim()
|
|
450
488
|
self._set_lim(vmin, vmax)
|
|
451
489
|
return
|
|
452
490
|
|
|
453
|
-
def _connect_event(self):
|
|
454
|
-
super()._connect_event()
|
|
455
|
-
self.figure.canvas.mpl_connect('draw_event', lambda x: self._on_draw(x))
|
|
456
|
-
return
|
|
457
|
-
|
|
458
|
-
def _on_draw(self, e):
|
|
459
|
-
self._draw_artist()
|
|
460
|
-
self._blit()
|
|
461
|
-
return
|
|
462
|
-
|
|
463
|
-
def _draw_artist(self):
|
|
464
|
-
renderer = self.figure.canvas.renderer
|
|
465
|
-
|
|
466
|
-
self.ax_price.xaxis.draw(renderer)
|
|
467
|
-
self.ax_price.yaxis.draw(renderer)
|
|
468
|
-
|
|
469
|
-
if self.candle_on_ma:
|
|
470
|
-
self.collection_ma.draw(renderer)
|
|
471
|
-
self.collection_candle.draw(renderer)
|
|
472
|
-
else:
|
|
473
|
-
self.collection_candle.draw(renderer)
|
|
474
|
-
self.collection_ma.draw(renderer)
|
|
475
|
-
|
|
476
|
-
if self.watermark:
|
|
477
|
-
self.text_watermark.set_text(self.watermark)
|
|
478
|
-
self.text_watermark.draw(renderer)
|
|
479
|
-
|
|
480
|
-
self.ax_volume.xaxis.draw(renderer)
|
|
481
|
-
self.ax_volume.yaxis.draw(renderer)
|
|
482
|
-
|
|
483
|
-
self.collection_volume.draw(renderer)
|
|
484
|
-
return
|
|
485
|
-
|
|
486
|
-
def _blit(self):
|
|
487
|
-
self.figure.canvas.blit()
|
|
488
|
-
return
|
|
489
|
-
|
|
490
491
|
def _set_lim(self, xmin, xmax, simpler=False, set_ma=True):
|
|
491
|
-
self.vxmin, self.vxmax = (xmin, xmax
|
|
492
|
+
self.vxmin, self.vxmax = (xmin, xmax)
|
|
492
493
|
if xmin < 0: xmin = 0
|
|
493
|
-
if xmax <
|
|
494
|
-
xmax += 1
|
|
495
|
-
if xmin == xmax: xmax += 1
|
|
494
|
+
if xmax < 1: xmax = 1
|
|
496
495
|
|
|
497
496
|
ymin, ymax = (self.df[self.low][xmin:xmax].min(), self.df[self.high][xmin:xmax].max())
|
|
498
497
|
yspace = (ymax - ymin) / 15
|
|
@@ -502,8 +501,9 @@ class DrawMixin(EventMixin):
|
|
|
502
501
|
# 거래량 차트 ymax
|
|
503
502
|
self.volume_ymax = self.df['ymax_volume'][xmin:xmax].max() if self.volume else 1
|
|
504
503
|
|
|
505
|
-
self._set_segments(xmin, xmax, simpler, set_ma)
|
|
506
504
|
self._change_lim(self.vxmin, self.vxmax)
|
|
505
|
+
xsub = xmax - xmin
|
|
506
|
+
self._set_collection_segments(xmin, xmax, xsub, simpler, set_ma)
|
|
507
507
|
return
|
|
508
508
|
|
|
509
509
|
def _change_lim(self, xmin, xmax):
|
|
@@ -519,19 +519,14 @@ class DrawMixin(EventMixin):
|
|
|
519
519
|
return
|
|
520
520
|
|
|
521
521
|
def get_default_lim(self):
|
|
522
|
-
return (0, self.list_index[-1])
|
|
522
|
+
return (0, self.list_index[-1]+1)
|
|
523
523
|
|
|
524
524
|
|
|
525
|
-
class BackgroundMixin(
|
|
525
|
+
class BackgroundMixin(LimMixin):
|
|
526
526
|
background = None
|
|
527
527
|
|
|
528
528
|
_creating_background = False
|
|
529
529
|
|
|
530
|
-
def _connect_event(self):
|
|
531
|
-
self.figure.canvas.mpl_connect('pick_event', lambda x: self._on_pick(x))
|
|
532
|
-
self.figure.canvas.mpl_connect('draw_event', lambda x: self._on_draw(x))
|
|
533
|
-
return
|
|
534
|
-
|
|
535
530
|
def _create_background(self):
|
|
536
531
|
if self._creating_background: return
|
|
537
532
|
|
|
@@ -562,17 +557,13 @@ class BaseMixin(BackgroundMixin):
|
|
|
562
557
|
|
|
563
558
|
|
|
564
559
|
class Chart(BaseMixin, Mixin):
|
|
565
|
-
def _add_collection(self):
|
|
566
|
-
super()._add_collection()
|
|
567
|
-
return self.add_artist()
|
|
568
|
-
|
|
569
560
|
def _draw_artist(self):
|
|
570
561
|
super()._draw_artist()
|
|
571
562
|
return self.draw_artist()
|
|
572
563
|
|
|
573
|
-
def
|
|
574
|
-
|
|
575
|
-
return
|
|
564
|
+
def _set_lim(self, xmin, xmax, simpler=False, set_ma=True):
|
|
565
|
+
super()._set_lim(xmin, xmax, simpler, set_ma)
|
|
566
|
+
return self.on_change_xlim(xmin, xmax, simpler, set_ma)
|
|
576
567
|
|
|
577
568
|
def _on_draw(self, e):
|
|
578
569
|
super()._on_draw(e)
|
|
@@ -582,18 +573,3 @@ class Chart(BaseMixin, Mixin):
|
|
|
582
573
|
self.on_pick(e)
|
|
583
574
|
return super()._on_pick(e)
|
|
584
575
|
|
|
585
|
-
def _set_candle_segments(self, index_start, index_end):
|
|
586
|
-
super()._set_candle_segments(index_start, index_end)
|
|
587
|
-
self.set_segment(index_start, index_end)
|
|
588
|
-
return
|
|
589
|
-
|
|
590
|
-
def _set_wick_segments(self, index_start, index_end, simpler=False):
|
|
591
|
-
super()._set_wick_segments(index_start, index_end, simpler)
|
|
592
|
-
self.set_segment(index_start, index_end, simpler)
|
|
593
|
-
return
|
|
594
|
-
|
|
595
|
-
def _set_line_segments(self, index_start, index_end, simpler=False, set_ma=True):
|
|
596
|
-
super()._set_line_segments(index_start, index_end, simpler, set_ma)
|
|
597
|
-
self.set_segment(index_start, index_end, simpler, set_ma)
|
|
598
|
-
return
|
|
599
|
-
|