seolpyo-mplchart 0.0.61__py3-none-any.whl → 0.1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -10,10 +10,75 @@ from pathlib import Path
10
10
  from typing import Literal
11
11
 
12
12
  import matplotlib.pyplot as plt
13
+ from matplotlib.figure import Figure
13
14
  import pandas as pd
14
15
 
15
16
 
16
- from .slider import Chart
17
+ from .slider import Chart as CM
18
+
19
+
20
+ __all__ = [
21
+ 'pd',
22
+ 'plt',
23
+
24
+ 'Chart',
25
+
26
+ 'sample',
27
+ 'show',
28
+ 'close',
29
+ ]
30
+
31
+
32
+ class Chart(CM):
33
+ r"""
34
+ You can see the guidance document:
35
+ Korean: https://white.seolpyo.com/entry/147/
36
+ English: https://white.seolpyo.com/entry/148/
37
+
38
+ Variables:
39
+ unit_price, unit_volume: unit for price and volume. default ('원', '주').
40
+
41
+ figsize: figure size if you use plt.show(). default (12, 6).
42
+ ratio_ax_slider, ratio_ax_legend, ratio_ax_price, ratio_ax_volume: Axes ratio. default (3, 2, 18, 5).
43
+ adjust: figure adjust. default dict(top=0.95, bottom=0.05, left=0.01, right=0.93, wspace=0, hspace=0).
44
+ slider_top: ax_slider is located at the top or bottom. default True.
45
+ color_background: color of background. default '#fafafa'.
46
+ color_grid: color of grid. default '#d0d0d0'.
47
+
48
+ df: stock data.
49
+ date: date column key. default 'date'
50
+ Open, high, low, close: price column key. default ('open', 'high', 'low', 'close')
51
+ volume: volume column key. default 'volume'
52
+
53
+ label_ma: moving average legend label format. default '{}일선'
54
+ list_ma: Decide how many days to draw the moving average line. default (5, 20, 60, 120, 240)
55
+ list_macolor: Color the moving average line. If the number of colors is greater than the moving average line, black is applied. default ('darkred', 'fuchsia', 'olive', 'orange', 'navy', 'darkmagenta', 'limegreen', 'darkcyan',)
56
+
57
+ candle_on_ma: Decide whether to draw candles on the moving average line. default True
58
+ color_sliderline: Color of closing price line in ax_slider. default 'k'
59
+ color_navigatorline: Color of left and right dividing lines in selected area. default '#1e78ff'
60
+ color_navigator: Color of unselected area. default 'k'
61
+
62
+ color_up: The color of the candle. When the closing price is greater than the opening price. default '#fe3032'
63
+ color_down: The color of the candle. When the opening price is greater than the opening price. default '#0095ff'
64
+ color_flat: The color of the candle. WWhen the closing price is the same as the opening price. default 'k'
65
+ color_up_down: The color of the candle. If the closing price is greater than the opening price, but is lower than the previous day's closing price. default 'w'
66
+ color_down_up: The color of the candle. If the opening price is greater than the closing price, but is higher than the closing price of the previous day. default 'w'
67
+ colors_volume: The color of the volume bar. default '#1f77b4'
68
+
69
+ lineKwargs: Options applied to horizontal and vertical lines drawn along the mouse position. default dict(edgecolor='k', linewidth=1, linestyle='-')
70
+ textboxKwargs: Options that apply to the information text box. dufault dict(boxstyle='round', facecolor='w')
71
+
72
+ fraction: Decide whether to express information as a fraction. default False
73
+ candleformat: Candle information text format. default '{}\n\n종가:  {}\n등락률: {}\n대비:  {}\n시가:  {}({})\n고가:  {}({})\n저가:  {}({})\n거래량: {}({})'
74
+ volumeformat: Volume information text format. default '{}\n\n거래량   : {}\n거래량증가율: {}'
75
+ digit_price, digit_volume: Number of decimal places expressed in informational text. default (0, 0)
76
+
77
+ min_distance: Minimum number of candles that can be selected with the slider. default 30
78
+ simpler: Decide whether to display candles simply when moving the chart. default False
79
+ limit_volume: Maximum number of volume bars drawn when moving the chart. default 2_000
80
+ """
81
+ pass
17
82
 
18
83
 
19
84
  _name = {'samsung', 'apple'}
@@ -37,7 +102,7 @@ def sample(name: Literal['samsung', 'apple']='samsung'):
37
102
  c.volumeformat = '{}\n\nvolume: {}\nvolume rate: {}'
38
103
  c.set_data(df)
39
104
  show()
40
-
105
+ close()
41
106
  return
42
107
 
43
108
 
@@ -45,5 +110,11 @@ def show():
45
110
  return plt.show()
46
111
 
47
112
 
113
+ def close(fig: int|str|Figure|None='all'):
114
+ return plt.close(fig)
115
+
116
+
48
117
  if __name__ == '__main__':
49
- sample('apple')
118
+ sample('apple')
119
+
120
+
@@ -11,9 +11,6 @@ from .utils import float_to_str
11
11
 
12
12
 
13
13
  class Mixin:
14
- def create_background(self):
15
- "This function works befor canvas.copy_from_bbox()."
16
- return
17
14
  def on_draw(self, e):
18
15
  "This function works if draw event active."
19
16
  return
@@ -64,12 +61,13 @@ class CollectionMixin(DrawMixin):
64
61
  _set_key = {'rate', 'compare', 'rate_open', 'rate_high', 'rate_low', 'rate_volume',}
65
62
 
66
63
  class DataMixin(CollectionMixin):
67
- def _generate_data(self, df: pd.DataFrame):
64
+ def _generate_data(self, df, sort_df=True, calc_ma=True):
68
65
  for i in ['date', 'Open', 'high', 'low', 'close', 'volume']:
69
66
  v = getattr(self, i)
70
67
  if v in _set_key: raise Exception(f'you can not set "self.{i}" value in {_set_key}.\nself.{i}={v!r}')
71
68
 
72
- super()._generate_data(df)
69
+ super()._generate_data(df, sort_df, calc_ma)
70
+ df = self.df
73
71
 
74
72
  df['rate'] = ((df[self.close] - df[self.close].shift(1)) / df[self.close] * 100).__round__(2).fillna(0)
75
73
  df['compare'] = (df[self.close] - df[self.close].shift(1)).fillna(0)
@@ -77,6 +75,8 @@ class DataMixin(CollectionMixin):
77
75
  df['rate_high'] = ((df[self.high] - df[self.close].shift(1)) / df[self.close] * 100).__round__(2).fillna(0)
78
76
  df['rate_low'] = ((df[self.low] - df[self.close].shift(1)) / df[self.close] * 100).__round__(2).fillna(0)
79
77
  df['rate_volume'] = ((df[self.volume] - df[self.volume].shift(1)) / df[self.volume].shift(1) * 100).__round__(2).fillna(0)
78
+
79
+ self.df = df
80
80
  return
81
81
 
82
82
  def set_text_coordante(self, vmin, vmax, pmin, pmax, volmax):
@@ -123,8 +123,8 @@ class LineMixin(DataMixin):
123
123
  self.canvas.blit()
124
124
  return
125
125
 
126
- def set_data(self, df):
127
- super().set_data(df)
126
+ def _set_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True, change_lim=True):
127
+ super()._set_data(df, sort_df, calc_ma, change_lim)
128
128
 
129
129
  self.vmin, self.vmax = (self.xmin, self.xmax)
130
130
  return
@@ -246,8 +246,8 @@ class InfoMixin(LineMixin):
246
246
  volumeformat = '{}\n\n거래량   : {}\n거래량증가율: {}'
247
247
  digit_price, digit_volume = (0, 0)
248
248
 
249
- def set_data(self, df):
250
- super().set_data(df)
249
+ def _set_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True, change_lim=True):
250
+ super()._set_data(df, sort_df, calc_ma, change_lim)
251
251
 
252
252
  # 슬라이더 날짜 텍스트 y 위치
253
253
  y = self._slider_ymax - (self._slider_ymax - self._slider_ymin) / 6
@@ -395,10 +395,6 @@ class CursorMixin(InfoMixin):
395
395
 
396
396
 
397
397
  class Chart(CursorMixin, CM, Mixin):
398
- def _generate_data(self, df):
399
- super()._generate_data(df)
400
- return self.generate_data(df)
401
-
402
398
  def _on_draw(self, e):
403
399
  super()._on_draw(e)
404
400
  return self.on_draw(e)
@@ -407,10 +403,6 @@ class Chart(CursorMixin, CM, Mixin):
407
403
  self.on_pick(e)
408
404
  return super()._on_pick(e)
409
405
 
410
- def _draw_artist(self):
411
- super()._draw_artist()
412
- return self.create_background()
413
-
414
406
  def _blit(self):
415
407
  super()._blit()
416
408
  return self.on_blit()
seolpyo_mplchart/draw.py CHANGED
@@ -9,10 +9,6 @@ from .base import Base
9
9
 
10
10
 
11
11
  class Mixin:
12
- def generate_data(self, df):
13
- "This function works after data generate process is done."
14
- return
15
-
16
12
  def on_blit(self):
17
13
  "This function works after cavas.blit()."
18
14
  return
@@ -27,7 +23,7 @@ class Mixin:
27
23
  return
28
24
 
29
25
 
30
- _set_key = {'x', 'left', 'right', 'top', 'bottom',}
26
+ _set_key = {'zero', 'x', 'left', 'right', 'top', 'bottom',}
31
27
 
32
28
  class DataMixin(Base):
33
29
  df: pd.DataFrame
@@ -48,7 +44,7 @@ class DataMixin(Base):
48
44
  color_down_up = 'w'
49
45
  colors_volume = '#1f77b4'
50
46
 
51
- def _generate_data(self, df: pd.DataFrame):
47
+ def _generate_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True):
52
48
  for i in ['date', 'Open', 'high', 'low', 'close', 'volume']:
53
49
  k: str = getattr(self, i)
54
50
  if k in _set_key: raise Exception(f'you can not set "self.{i}" value in {_set_key}.\nself.{i}={k!r}')
@@ -56,7 +52,13 @@ class DataMixin(Base):
56
52
  dtype = df[k].dtype
57
53
  if not isinstance(dtype, (np.dtypes.Float64DType, np.dtypes.Int64DType, np.dtypes.Float32DType, np.dtypes.Int32DType)): raise TypeError(f'Data column type must be "float64" or "int64" or "float32" or "int32".(excluding "date" column)\ndf[{k!r}].dtype={dtype!r}')
58
54
 
59
- for i in self.list_ma: df[f'ma{i}'] = df[self.close].rolling(i).mean()
55
+ # DataFrame 정렬
56
+ if sort_df:
57
+ df = df.sort_values([self.date]).reset_index()
58
+
59
+ if not self.list_ma: self.list_ma = tuple()
60
+ if calc_ma:
61
+ for i in self.list_ma: df[f'ma{i}'] = df[self.close].rolling(i).mean()
60
62
 
61
63
  candlewidth_half = 0.3
62
64
  volumewidth_half = 0.36
@@ -72,7 +74,7 @@ class DataMixin(Base):
72
74
  df['bottom'] = np.where(df[self.close] < df[self.Open], df[self.close], df[self.Open])
73
75
 
74
76
  # 양봉
75
- df.loc[:, ['facecolor', 'edgecolor']] = (self.color_up, self.color_up)
77
+ df.loc[:, ['zero', 'facecolor', 'edgecolor']] = (0, self.color_up, self.color_up)
76
78
  if self.color_up != self.color_down:
77
79
  # 음봉
78
80
  df.loc[df[self.close] < df[self.Open], ['facecolor', 'edgecolor']] = (self.color_down, self.color_down)
@@ -89,7 +91,6 @@ class DataMixin(Base):
89
91
  self.df = df
90
92
  return
91
93
 
92
-
93
94
  class CollectionMixin(DataMixin):
94
95
  color_sliderline = 'k'
95
96
 
@@ -123,57 +124,55 @@ class CollectionMixin(DataMixin):
123
124
 
124
125
  return
125
126
 
126
- def _get_candlesegment(self, s: pd.Series):
127
- v = s.values
128
- segment = (
129
- (v[0], v[3]), # 심지 상단
130
- (v[0], v[5]), # 몸통 상단
131
- (v[1], v[5]), # 몸통 상단 좌측
132
- (v[1], v[6]), # 몸통 하단 좌측
133
- (v[0], v[6]), # 몸통 하단
134
- (v[0], v[4]), # 심지 하단
135
- (v[0], v[6]), # 몸통 하단
136
- (v[2], v[6]), # 몸통 하단 우측
137
- (v[2], v[5]), # 몸통 상단 우측
138
- (v[0], v[5]), # 몸통 상단
139
- )
140
- return segment
141
-
142
- def _get_volumesegment(self, s: pd.Series):
143
- v = s.values
144
- segment = (
145
- (v[0], 0), # 몸통 하단 좌측
146
- (v[0], v[2]), # 몸통 상단 좌측
147
- (v[1], v[2]), # 몸통 상단 우측
148
- (v[1], 0), # 몸통 하단 우측
149
- )
150
- return segment
151
-
152
127
  def _set_collection(self):
153
- self.df.loc[:, ['candlesegment']] = self.df[['x', 'left', 'right', self.high, self.low, 'top', 'bottom']].agg(self._get_candlesegment, axis=1)
154
- self.df.loc[:, ['volumesegment']] = self.df[['vleft', 'vright', self.volume]].agg(self._get_volumesegment, axis=1)
128
+ candleseg = self.df[[
129
+ 'x', self.high,
130
+ 'x', 'top',
131
+ 'left', 'top',
132
+ 'left', 'bottom',
133
+ 'x', 'bottom',
134
+ 'x', self.low,
135
+ 'x', 'bottom',
136
+ 'right', 'bottom',
137
+ 'right', 'top',
138
+ 'x', 'top',
139
+ ]].values
140
+ candleseg = candleseg.reshape(candleseg.shape[0], 10, 2)
141
+
142
+ self.candlecollection.set_segments(candleseg)
143
+ self.candlecollection.set_facecolor(self.df['facecolor'].values)
144
+ self.candlecollection.set_edgecolor(self.df['edgecolor'].values)
145
+
146
+ volseg = self.df[[
147
+ 'left', 'zero',
148
+ 'left', self.volume,
149
+ 'right', self.volume,
150
+ 'right', 'zero',
151
+ ]].values
152
+ volseg = volseg.reshape(volseg.shape[0], 4, 2)
153
+
154
+ self.volumecollection.set_segments(volseg)
155
155
 
156
156
  self._set_macollection()
157
157
 
158
158
  # 가격이동평균선
159
- segments = list(reversed(self._masegment.values()))
159
+ maseg = reversed(self._masegment.values())
160
160
  colors, widths = ([], [])
161
161
  for i in reversed(self._macolors.values()): (colors.append(i), widths.append(1))
162
- self.macollection.set_segments(segments)
162
+ self.macollection.set_segments(maseg)
163
163
  self.macollection.set_edgecolor(colors)
164
164
 
165
165
  # 슬라이더 선형차트
166
- segments.append(self.df[['x', self.close]].apply(tuple, axis=1).tolist())
166
+ keys = []
167
+ for i in reversed(self.list_ma):
168
+ keys.append('x')
169
+ keys.append(f'ma{i}')
170
+ sliderseg = self.df[keys + ['x', self.close]].values
171
+ sliderseg = sliderseg.reshape(sliderseg.shape[0], self.list_ma.__len__()+1, 2).swapaxes(0, 1)
167
172
  (colors.append(self.color_sliderline), widths.append(1.8))
168
- self.slidercollection.set_segments(segments)
173
+ self.slidercollection.set_segments(sliderseg)
169
174
  self.slidercollection.set_edgecolor(colors)
170
175
  self.slidercollection.set_linewidth(widths)
171
-
172
- self.candlecollection.set_segments(self.df['candlesegment'])
173
- self.candlecollection.set_facecolor(self.df['facecolor'].values)
174
- self.candlecollection.set_edgecolor(self.df['edgecolor'].values)
175
-
176
- self.volumecollection.set_segments(self.df['volumesegment'])
177
176
  return
178
177
 
179
178
  def _set_macollection(self):
@@ -188,7 +187,10 @@ class CollectionMixin(DataMixin):
188
187
  try: c = self.list_macolor[n]
189
188
  except: c = self.color_sliderline
190
189
  self._macolors[i] = c
191
- self._masegment[i] = self.df[['x', f'ma{i}']].apply(tuple, axis=1).tolist()
190
+ # seg = self.df['x', f'ma{i}'].values
191
+ seg = self.df.loc[self.df[f'ma{i}'] != np.nan, ['x', f'ma{i}']].values
192
+ # print(f'{seg[:5]=}')
193
+ self._masegment[i] = seg
192
194
 
193
195
  handles.append(Line2D([0, 1], [0, 1], color=c, linewidth=5, label=i))
194
196
  labels.append(self.label_ma.format(i))
@@ -300,25 +302,29 @@ class BackgroundMixin(CollectionMixin):
300
302
  self.canvas.renderer.restore_region(self.background)
301
303
  return
302
304
 
303
-
304
305
  class DrawMixin(BackgroundMixin):
305
- def set_data(self, df: pd.DataFrame):
306
- self._generate_data(df)
306
+ def set_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True, change_lim=True):
307
+ self._set_data(df, sort_df, calc_ma, change_lim)
308
+ return self.df
309
+
310
+ def _set_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True, change_lim=True):
311
+ self._generate_data(df, sort_df, calc_ma)
307
312
  self._set_collection()
308
- self._draw_collection()
313
+ self._draw_collection(change_lim)
309
314
  return
310
315
 
311
- def _draw_collection(self):
316
+ def _draw_collection(self, change_lim=True):
312
317
  xmax = self.df['x'].values[-1] + 1
313
318
 
314
319
  xspace = xmax / 40
315
320
  self.xmin, self.xmax = (-xspace, xmax+xspace)
316
321
  # 슬라이더 xlim
317
322
  self.ax_slider.set_xlim(self.xmin, self.xmax)
318
- # 주가 xlim
319
- self.ax_price.set_xlim(self.xmin, self.xmax)
320
- # 거래량 xlim
321
- self.ax_volume.set_xlim(self.xmin, self.xmax)
323
+ if change_lim:
324
+ # 주가 xlim
325
+ self.ax_price.set_xlim(self.xmin, self.xmax)
326
+ # 거래량 xlim
327
+ self.ax_volume.set_xlim(self.xmin, self.xmax)
322
328
 
323
329
  ymin, ymax = (self.df[self.low].min(), self.df[self.high].max())
324
330
  ysub = (ymax - ymin) / 15
@@ -329,19 +335,15 @@ class DrawMixin(BackgroundMixin):
329
335
 
330
336
  # 주가 ylim
331
337
  self._price_ymin, self._price_ymax = (ymin-ysub, ymax+ysub)
332
- self.ax_price.set_ylim(self._price_ymin, self._price_ymax)
338
+ if change_lim: self.ax_price.set_ylim(self._price_ymin, self._price_ymax)
333
339
 
334
340
  # 거래량 ylim
335
341
  self._vol_ymax = self.df[self.volume].max() * 1.2
336
- self.ax_volume.set_ylim(0, self._vol_ymax)
342
+ if change_lim: self.ax_volume.set_ylim(0, self._vol_ymax)
337
343
  return
338
344
 
339
345
 
340
346
  class Chart(DrawMixin, Mixin):
341
- def _generate_data(self, df):
342
- super()._generate_data(df)
343
- return self.generate_data(self.df)
344
-
345
347
  def _on_draw(self, e):
346
348
  super()._on_draw(e)
347
349
  return self.on_draw(e)
@@ -21,6 +21,10 @@ class Mixin(CursorMixin):
21
21
  def on_release(self, e):
22
22
  "This function works if mouse button release event active."
23
23
  return
24
+ def draw_artist(self):
25
+ "This function works before canvas.blit()."
26
+ return
27
+
24
28
 
25
29
  class NavgatorMixin(Mixin):
26
30
  min_distance = 30
@@ -39,8 +43,8 @@ class NavgatorMixin(Mixin):
39
43
  self.ax_slider.add_artist(self.navigator)
40
44
  return
41
45
 
42
- def set_data(self, df):
43
- super().set_data(df)
46
+ def _set_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True, change_lim=True):
47
+ super()._set_data(df, sort_df, calc_ma, False)
44
48
 
45
49
  # 네비게이터 라인 선택 영역
46
50
  xsub = self.xmax - self.xmin
@@ -152,13 +156,12 @@ class BackgroundMixin(NavgatorMixin):
152
156
 
153
157
 
154
158
  class DrawMixin(BackgroundMixin):
155
- def set_data(self, df):
156
- super().set_data(df)
159
+ def _set_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True, change_lim=True):
160
+ super()._set_data(df, sort_df, calc_ma, change_lim)
157
161
 
158
162
  # 네비게이터 높이 설정
159
- if 0 < self._slider_ymin: ysub = self._slider_ymax
160
- else: ysub = self._slider_ymax - self._slider_ymin
161
- self._ymiddle = ysub / 2
163
+ ysub = self._slider_ymax - self._slider_ymin
164
+ self._ymiddle = self._slider_ymax - ysub / 2
162
165
  self.navigator.set_linewidth((ysub, 5))
163
166
  return
164
167
 
@@ -252,8 +255,8 @@ class LimMixin(DrawMixin):
252
255
  self._lim()
253
256
 
254
257
  self._restore_region(empty=True)
255
- self._creating_background = False
256
- self._create_background()
258
+ self._copy_bbox()
259
+ self._restore_region()
257
260
  self._blit()
258
261
  return
259
262
 
@@ -315,6 +318,7 @@ class LimMixin(DrawMixin):
315
318
  class SimpleMixin(LimMixin):
316
319
  simpler = False
317
320
  limit_volume = 1_500
321
+ default_left, default_right = (180, 10)
318
322
 
319
323
  def __init__(self, *args, **kwargs):
320
324
  super().__init__(*args, **kwargs)
@@ -330,35 +334,34 @@ class SimpleMixin(LimMixin):
330
334
  self.ax_volume.add_collection(self.blitvolume)
331
335
  return
332
336
 
333
- def set_data(self, df):
334
- super().set_data(df)
337
+ def _set_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True, change_lim=True):
338
+ super()._set_data(df, sort_df, calc_ma, False)
335
339
 
336
- seg = self.df[['x', self.high, self.low]].agg(get_wickline, axis=1)
340
+ seg = self.df[['x', self.high, 'x', self.low]].values
341
+ seg = seg.reshape(seg.shape[0], 2, 2)
337
342
  self.blitcandle.set_segments(seg)
338
343
  self.blitcandle.set_edgecolor(self.df['edgecolor'])
339
- self.priceline.set_verts(pd.array([self.df[['x', self.close]].apply(tuple, axis=1).to_list()]))
340
344
 
341
- volmax = self.df[self.volume].max()
345
+ pseg = self.df[['x', self.close]].values
346
+ self.priceline.set_verts(pseg.reshape(1, *pseg.shape))
347
+
342
348
  l = self.df.__len__()
343
349
  if l < self.limit_volume:
344
- volseg = self.df.loc[:, ['x', self.volume]].agg(get_volumeline, axis=1)
350
+ volseg = self.df.loc[:, ['x', 'zero', 'x', self.volume]].values
345
351
  else:
346
- n, step = (1, 1 / self.limit_volume)
347
- for _ in range(self.limit_volume):
348
- n -= step
349
- volmin = volmax * n
350
- length = self.df.loc[volmin < self.df[self.volume]].__len__()
351
- if self.limit_volume < length: break
352
-
353
- volseg = self.df.loc[volmin < self.df[self.volume], ['x', self.volume]].agg(get_volumeline, axis=1)
354
- self.blitvolume.set_segments(volseg)
355
-
356
- index = self.df.index[-1]
357
- if index < 120: self._navcoordinate = (int(self.xmin)-1, int(self.xmax)+1)
358
- else: self._navcoordinate = (index-80, index+10)
352
+ v = self.df[['x', 'zero', 'x', self.volume]].sort_values([self.volume], axis=0, ascending=False)
353
+ volseg = v[:self.limit_volume].values
354
+
355
+ self.blitvolume.set_segments(volseg.reshape(volseg.shape[0], 2, 2))
356
+
357
+ if change_lim:
358
+ index = self.df.index[-1]
359
+ if index < self.default_left + self.default_right: self._navcoordinate = (int(self.xmin)-1, int(self.xmax)+1)
360
+ else: self._navcoordinate = (index-self.default_left, index+self.default_right)
361
+
359
362
  self._set_navigator(*self._navcoordinate)
360
363
  self._lim()
361
- return self._draw()
364
+ return
362
365
 
363
366
  def _draw_blit_artist(self):
364
367
  renderer = self.canvas.renderer
@@ -435,8 +438,8 @@ class ClickMixin(SimpleMixin):
435
438
  self.is_click_chart = False
436
439
 
437
440
  self._restore_region(empty=True)
438
- self._creating_background = False
439
- self._create_background()
441
+ self._copy_bbox()
442
+ self._restore_region()
440
443
  self._blit()
441
444
  return
442
445
 
@@ -503,58 +506,6 @@ class SliderMixin(ClickMixin):
503
506
 
504
507
 
505
508
  class Chart(SliderMixin, CM, Mixin):
506
- r"""
507
- You can see the guidance document:
508
- Korean: https://white.seolpyo.com/entry/147/
509
- English: https://white.seolpyo.com/entry/148/
510
-
511
- Variables:
512
- unit_price, unit_volume: unit for price and volume. default ('원', '주').
513
-
514
- figsize: figure size if you use plt.show(). default (12, 6).
515
- ratio_ax_slider, ratio_ax_legend, ratio_ax_price, ratio_ax_volume: Axes ratio. default (3, 2, 18, 5).
516
- adjust: figure adjust. default dict(top=0.95, bottom=0.05, left=0.01, right=0.93, wspace=0, hspace=0).
517
- slider_top: ax_slider is located at the top or bottom. default True.
518
- color_background: color of background. default '#fafafa'.
519
- color_grid: color of grid. default '#d0d0d0'.
520
-
521
- df: stock data.
522
- date: date column key. default 'date'
523
- Open, high, low, close: price column key. default ('open', 'high', 'low', 'close')
524
- volume: volume column key. default 'volume'
525
-
526
- label_ma: moving average legend label format. default '{}일선'
527
- list_ma: Decide how many days to draw the moving average line. default (5, 20, 60, 120, 240)
528
- list_macolor: Color the moving average line. If the number of colors is greater than the moving average line, black is applied. default ('darkred', 'fuchsia', 'olive', 'orange', 'navy', 'darkmagenta', 'limegreen', 'darkcyan',)
529
-
530
- candle_on_ma: Decide whether to draw candles on the moving average line. default True
531
- color_sliderline: Color of closing price line in ax_slider. default 'k'
532
- color_navigatorline: Color of left and right dividing lines in selected area. default '#1e78ff'
533
- color_navigator: Color of unselected area. default 'k'
534
-
535
- color_up: The color of the candle. When the closing price is greater than the opening price. default '#fe3032'
536
- color_down: The color of the candle. When the opening price is greater than the opening price. default '#0095ff'
537
- color_flat: The color of the candle. WWhen the closing price is the same as the opening price. default 'k'
538
- color_up_down: The color of the candle. If the closing price is greater than the opening price, but is lower than the previous day's closing price. default 'w'
539
- color_down_up: The color of the candle. If the opening price is greater than the closing price, but is higher than the closing price of the previous day. default 'w'
540
- colors_volume: The color of the volume bar. default '#1f77b4'
541
-
542
- lineKwargs: Options applied to horizontal and vertical lines drawn along the mouse position. default dict(edgecolor='k', linewidth=1, linestyle='-')
543
- textboxKwargs: Options that apply to the information text box. dufault dict(boxstyle='round', facecolor='w')
544
-
545
- fraction: Decide whether to express information as a fraction. default False
546
- candleformat: Candle information text format. default '{}\n\n종가:  {}\n등락률: {}\n대비:  {}\n시가:  {}({})\n고가:  {}({})\n저가:  {}({})\n거래량: {}({})'
547
- volumeformat: Volume information text format. default '{}\n\n거래량   : {}\n거래량증가율: {}'
548
- digit_price, digit_volume: Number of decimal places expressed in informational text. default (0, 0)
549
-
550
- min_distance: Minimum number of candles that can be selected with the slider. default 30
551
- simpler: Decide whether to display candles simply when moving the chart. default False
552
- limit_volume: Maximum number of volume bars drawn when moving the chart. default 2_000
553
- """
554
- def _generate_data(self, df):
555
- super()._generate_data(df)
556
- return self.generate_data(self.df)
557
-
558
509
  def _on_draw(self, e):
559
510
  super()._on_draw(e)
560
511
  return self.on_draw(e)
@@ -565,7 +516,10 @@ class Chart(SliderMixin, CM, Mixin):
565
516
 
566
517
  def _draw_artist(self):
567
518
  super()._draw_artist()
568
- return self.create_background()
519
+ return self.draw_artist()
520
+ def _draw_blit_artist(self):
521
+ super()._draw_blit_artist()
522
+ return self.draw_artist()
569
523
 
570
524
  def _blit(self):
571
525
  super()._blit()
@@ -597,3 +551,4 @@ if __name__ == '__main__':
597
551
  t2 = time() - t
598
552
  print(f'{t2=}')
599
553
  plt.show()
554
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seolpyo-mplchart
3
- Version: 0.0.61
3
+ Version: 0.1.0.1
4
4
  Summary: Fast candlestick chart using Python. Includes navigator, slider, navigation, and text information display functions
5
5
  Author-email: white-seolpyo <white-seolpyo@naver.com>
6
6
  License: MIT License
@@ -0,0 +1,13 @@
1
+ seolpyo_mplchart/__init__.py,sha256=HITw-PVvJA4AHOO_gOAsbBxNvEbou-JLtivnKA_dn0A,5157
2
+ seolpyo_mplchart/base.py,sha256=vQ4OOBm3nGwjJ4wjDLaD_3LGxYzlP6AWpI6SZrZiwnQ,3600
3
+ seolpyo_mplchart/cursor.py,sha256=_Pzg8WvfOpZYzN5uNLO9LH2wrRB_gZTNGkqmzsjaaPc,17309
4
+ seolpyo_mplchart/draw.py,sha256=yl813StbNuWC0_3QfS9ECvc0Z5buaIudsqR_qW8d3f4,13021
5
+ seolpyo_mplchart/slider.py,sha256=K-vPj2dsSyXZDELrgn6Ry5VqORgUc65gS2SsTFm5TmE,19725
6
+ seolpyo_mplchart/test.py,sha256=cW2hoaVbRtoSXlpmA4i1BKHBjI3-FAqYq__kryxkrC8,1007
7
+ seolpyo_mplchart/utils.py,sha256=-8cq4-WwiqKQxtwu3NPxOVTDDvoWH28tu4OTWr4hPTg,1208
8
+ seolpyo_mplchart/data/apple.txt,sha256=0izAfweu1lLsC0IwVthdVlo9reG8KGbKGTSX5knI5Zc,1380864
9
+ seolpyo_mplchart/data/samsung.txt,sha256=UejaSkbzr4E5K3lkelCT0yJiWUPfmViBEaTyoXyphIs,2476424
10
+ seolpyo_mplchart-0.1.0.1.dist-info/METADATA,sha256=goxKxwzreRSlKSiPffJSYfG-9prsp1586ZPuf9pFbnU,2178
11
+ seolpyo_mplchart-0.1.0.1.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
12
+ seolpyo_mplchart-0.1.0.1.dist-info/top_level.txt,sha256=KgqFn7rKWize7OjMaTCHxKm9ie6vqnyb5c8fN7y_tSo,17
13
+ seolpyo_mplchart-0.1.0.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.7.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,13 +0,0 @@
1
- seolpyo_mplchart/__init__.py,sha256=bsfjo70OG4go5ZNaFziLpXPMZ9wWSGQdx1ujuaGBzCQ,1289
2
- seolpyo_mplchart/base.py,sha256=vQ4OOBm3nGwjJ4wjDLaD_3LGxYzlP6AWpI6SZrZiwnQ,3600
3
- seolpyo_mplchart/cursor.py,sha256=JI4Fg935unIrisr_n5oIA7gxqVuXaX5-US7n34CADyg,17377
4
- seolpyo_mplchart/draw.py,sha256=ai8dpiz1ot4L9fphA3SRKhWMhNcWzvHUvQnZYo5p94I,12963
5
- seolpyo_mplchart/slider.py,sha256=dXfjTfwEYCcnVDHwn59u8-tiz8aB3HssiOLfuctp4tk,23159
6
- seolpyo_mplchart/test.py,sha256=cW2hoaVbRtoSXlpmA4i1BKHBjI3-FAqYq__kryxkrC8,1007
7
- seolpyo_mplchart/utils.py,sha256=-8cq4-WwiqKQxtwu3NPxOVTDDvoWH28tu4OTWr4hPTg,1208
8
- seolpyo_mplchart/data/apple.txt,sha256=0izAfweu1lLsC0IwVthdVlo9reG8KGbKGTSX5knI5Zc,1380864
9
- seolpyo_mplchart/data/samsung.txt,sha256=UejaSkbzr4E5K3lkelCT0yJiWUPfmViBEaTyoXyphIs,2476424
10
- seolpyo_mplchart-0.0.61.dist-info/METADATA,sha256=ahfx_SZU85fOOUODMLSjQQfnKwDBq2TQZrap-eiLl4U,2177
11
- seolpyo_mplchart-0.0.61.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
12
- seolpyo_mplchart-0.0.61.dist-info/top_level.txt,sha256=KgqFn7rKWize7OjMaTCHxKm9ie6vqnyb5c8fN7y_tSo,17
13
- seolpyo_mplchart-0.0.61.dist-info/RECORD,,