seolpyo-mplchart 1.2.0__tar.gz → 1.3.0__tar.gz

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: seolpyo-mplchart
3
- Version: 1.2.0
3
+ Version: 1.3.0
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
@@ -32,18 +32,24 @@ Ethereum: 0x1c5fb8a5e0b1153cd4116c91736bd16fabf83520
32
32
 
33
33
 
34
34
  # Document
35
- [English](https://white.seolpyo.com/entry/148/)
35
+ [English Document](https://white.seolpyo.com/entry/148/)
36
36
 
37
- [한글](https://white.seolpyo.com/entry/147/)
37
+ [한글 설명서](https://white.seolpyo.com/entry/147/)
38
38
 
39
39
 
40
- # Sample
40
+ # Sample Image
41
+ ## sample
41
42
  ![sample gif](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/sample.gif)
42
43
 
44
+ ## tkinter sample
43
45
  ![tkinter sample gif](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/with%20tkinter.gif)
44
46
 
47
+ ## English format sample
45
48
  ![english sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/change%20color.png)
46
49
 
50
+ ## Korean format sample
47
51
  ![korean sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/sample%20kor.png)
48
52
 
53
+
54
+ # 40,000 data sample
49
55
  ![40,000 sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/40000.gif)
@@ -5,18 +5,24 @@ Ethereum: 0x1c5fb8a5e0b1153cd4116c91736bd16fabf83520
5
5
 
6
6
 
7
7
  # Document
8
- [English](https://white.seolpyo.com/entry/148/)
8
+ [English Document](https://white.seolpyo.com/entry/148/)
9
9
 
10
- [한글](https://white.seolpyo.com/entry/147/)
10
+ [한글 설명서](https://white.seolpyo.com/entry/147/)
11
11
 
12
12
 
13
- # Sample
13
+ # Sample Image
14
+ ## sample
14
15
  ![sample gif](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/sample.gif)
15
16
 
17
+ ## tkinter sample
16
18
  ![tkinter sample gif](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/with%20tkinter.gif)
17
19
 
20
+ ## English format sample
18
21
  ![english sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/change%20color.png)
19
22
 
23
+ ## Korean format sample
20
24
  ![korean sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/sample%20kor.png)
21
25
 
26
+
27
+ # 40,000 data sample
22
28
  ![40,000 sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/40000.gif)
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
8
8
 
9
9
  [project]
10
10
  name = "seolpyo-mplchart"
11
- version = "1.2.0"
11
+ version = "1.3.0"
12
12
  dependencies = [
13
13
  "matplotlib >= 3.7.0",
14
14
  "pandas >= 2.0.0",
@@ -281,7 +281,7 @@ class SliderChart(_BaseSliderChart):
281
281
  pass
282
282
 
283
283
 
284
- def set_theme(chart: OnlyChart|CursorChart|SliderChart, theme: Literal['light', 'dark']='dark'):
284
+ def set_theme(chart: SliderChart|CursorChart|OnlyChart, theme: Literal['light', 'dark']='dark'):
285
285
  initialized = hasattr(chart, 'ax_price')
286
286
 
287
287
  if theme == 'dark':
@@ -312,10 +312,8 @@ def set_theme(chart: OnlyChart|CursorChart|SliderChart, theme: Literal['light',
312
312
  if hasattr(chart, 'navigator'): chart.navigator.set_edgecolor([chart.color_navigator_cover, chart.color_navigator_line])
313
313
 
314
314
  if hasattr(chart, 'df'):
315
- chart.set_candlecolor, chart.set_volumecolor = (True, True)
316
- chart._get_color_segment()
317
- chart.figure.canvas.draw()
318
-
315
+ chart.set_data(chart.df, sort_df=False, calc_ma=False, set_candlecolor=True, set_volumecolor=True, calc_info=False, change_lim=False)
316
+ chart.draw_canvas()
319
317
  elif theme == 'light':
320
318
  chart.color_background = '#fafafa'
321
319
  chart.color_tick, chart.color_tick_label = ('k', 'k')
@@ -344,9 +342,8 @@ def set_theme(chart: OnlyChart|CursorChart|SliderChart, theme: Literal['light',
344
342
  if hasattr(chart, 'navigator'): chart.navigator.set_edgecolor([chart.color_navigator_cover, chart.color_navigator_line])
345
343
 
346
344
  if hasattr(chart, 'df'):
347
- chart.set_candlecolor, chart.set_volumecolor = (True, True)
348
- chart._get_color_segment()
349
- chart.figure.canvas.draw()
345
+ chart.set_data(chart.df, sort_df=False, calc_ma=False, set_candlecolor=True, set_volumecolor=True, calc_info=False, change_lim=False)
346
+ chart.draw_canvas()
350
347
  else: raise ValueError(f'You send wrong arg.\n{theme=}')
351
348
 
352
349
  return chart
@@ -56,6 +56,9 @@ class Base:
56
56
 
57
57
  unit_price, unit_volume = ('원', '주')
58
58
 
59
+ def draw_canvas(self): return self.figure.canvas.draw()
60
+ def blit_canvas(self): return self.figure.canvas.blit()
61
+
59
62
  def __init__(self, *args, **kwargs):
60
63
  # 기본 툴바 비활성화
61
64
  plt.rcParams['toolbar'] = 'None'
@@ -103,40 +103,48 @@ class CollectionMixin(BM):
103
103
  return
104
104
 
105
105
 
106
- _set_key = {'rate', 'compare', 'rate_open', 'rate_high', 'rate_low', 'rate_volume', '_boxheight', '_boxmin', '_boxmax', '_volumeboxmax',}
106
+ _set_key = {
107
+ 'compare', 'rate',
108
+ 'rate_open', 'rate_high', 'rate_low',
109
+ 'compare_volume', 'rate_volume',
110
+ 'space_box_candle',
111
+ 'bottom_box_candle', 'top_box_candle',
112
+ 'max_box_volume',
113
+ }
107
114
 
108
115
  class DataMixin(CollectionMixin):
109
- def _validate_column_key(self):
110
- super()._validate_column_key()
111
- for i in ['date', 'Open', 'high', 'low', 'close', 'volume']:
116
+ def _validate_column_key(self, df):
117
+ super()._validate_column_key(df)
118
+
119
+ for i in ('date', 'Open', 'high', 'low', 'close', 'volume'):
112
120
  v = getattr(self, i)
113
121
  if v in _set_key: raise Exception(f'you can not set "{i}" to column key.\nself.{i}={v!r}')
114
122
  return
115
123
 
116
- def _generate_data(self, df, sort_df, calc_ma, set_candlecolor, set_volumecolor, calc_info, *_, **__):
124
+ def _generate_data(self, df: pd.DataFrame, sort_df, calc_ma, set_candlecolor, set_volumecolor, calc_info, *_, **__):
117
125
  super()._generate_data(df, sort_df, calc_ma, set_candlecolor, set_volumecolor, *_, **__)
118
126
 
119
127
  if not calc_info:
120
128
  keys = set(df.keys())
121
- list_key = ['rate', 'compare', 'rate_open', 'rate_high', 'rate_low',]
122
- if self.volume: list_key.append('rate_volume')
129
+ list_key = ['compare', 'rate', 'rate_open', 'rate_high', 'rate_low',]
130
+ if self.volume: list_key += ['compare_volume', 'rate_volume',]
123
131
  for i in list_key:
124
132
  if i not in keys:
125
133
  raise Exception(f'"{i}" column not in DataFrame.\nadd column or set calc_info=True.')
126
134
  else:
127
- self.df['compare'] = (self.df[self.close] - self.df['_pre']).fillna(0)
135
+ self.df['compare'] = (self.df[self.close] - self.df['close_pre']).fillna(0)
128
136
  self.df['rate'] = (self.df['compare'] / self.df[self.close] * 100).__round__(2).fillna(0)
129
- self.df['rate_open'] = ((self.df[self.Open] - self.df['_pre']) / self.df[self.close] * 100).__round__(2).fillna(0)
130
- self.df['rate_high'] = ((self.df[self.high] - self.df['_pre']) / self.df[self.close] * 100).__round__(2).fillna(0)
131
- self.df['rate_low'] = ((self.df[self.low] - self.df['_pre']) / self.df[self.close] * 100).__round__(2).fillna(0)
137
+ self.df['rate_open'] = ((self.df[self.Open] - self.df['close_pre']) / self.df[self.close] * 100).__round__(2).fillna(0)
138
+ self.df['rate_high'] = ((self.df[self.high] - self.df['close_pre']) / self.df[self.close] * 100).__round__(2).fillna(0)
139
+ self.df['rate_low'] = ((self.df[self.low] - self.df['close_pre']) / self.df[self.close] * 100).__round__(2).fillna(0)
132
140
  if self.volume:
133
141
  self.df['compare_volume'] = (self.df[self.volume] - self.df[self.volume].shift(1)).fillna(0)
134
142
  self.df['rate_volume'] = (self.df['compare_volume'] / self.df[self.volume].shift(1) * 100).__round__(2).fillna(0)
135
143
 
136
- self.df['_boxheight'] = (self.df[self.high] - self.df[self.low]) / 5
137
- self.df['_boxmin'] = self.df[self.low] - self.df['_boxheight']
138
- self.df['_boxmax'] = self.df[self.high] + self.df['_boxheight']
139
- if self.volume: self.df['_volumeboxmax'] = self.df[self.volume] * 1.13
144
+ self.df['space_box_candle'] = (self.df[self.high] - self.df[self.low]) / 5
145
+ self.df['bottom_box_candle'] = self.df[self.low] - self.df['space_box_candle']
146
+ 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
140
148
  return
141
149
 
142
150
  def _set_lim(self, xmin, xmax, simpler=False, set_ma=True):
@@ -245,8 +253,8 @@ class LineMixin(EventMixin):
245
253
  self.text_date_volume.draw(renderer)
246
254
 
247
255
  # 캔들 강조
248
- low = self.df['_boxmin'][index]
249
- high = self.df['_boxmax'][index]
256
+ low = self.df['bottom_box_candle'][index]
257
+ high = self.df['top_box_candle'][index]
250
258
  sub = high - low
251
259
  if sub < self.min_candleboxheight:
252
260
  sub = (self.min_candleboxheight - sub) / 2
@@ -75,7 +75,7 @@ class CollectionMixin(Base):
75
75
 
76
76
  def change_tick_color(self, color):
77
77
  for ax in (self.ax_price, self.ax_volume):
78
- for i in ['top', 'bottom', 'left', 'right']: ax.spines[i].set_color(self.color_tick)
78
+ for i in ('top', 'bottom', 'left', 'right'): ax.spines[i].set_color(self.color_tick)
79
79
  ax.tick_params(colors=color)
80
80
  ax.tick_params(colors=color)
81
81
 
@@ -95,7 +95,11 @@ class CollectionMixin(Base):
95
95
  def change_line_color(self, color): return
96
96
 
97
97
 
98
- _set_key = {'_x', '_left', '_right', '_volleft', '_volright', '_top', '_bottom', '_pre', '_zero', '_volymax',}
98
+ _set_key = {
99
+ 'x', 'zero', 'close_pre', 'ymax_volume',
100
+ 'top_candle', 'bottom_candle', 'left_candle', 'right_candle',
101
+ 'left_volume', 'right_volume',
102
+ }
99
103
 
100
104
  class DataMixin(CollectionMixin):
101
105
  df: pd.DataFrame
@@ -116,11 +120,14 @@ class DataMixin(CollectionMixin):
116
120
  set_candlecolor, set_volumecolor = (True, True)
117
121
 
118
122
  def _generate_data(self, df: pd.DataFrame, sort_df, calc_ma, set_candlecolor, set_volumecolor, *_, **__):
119
- self._validate_column_key()
123
+ self.set_candlecolor = set_candlecolor
124
+ self.set_volumecolor = set_volumecolor
125
+
126
+ self._validate_column_key(df)
120
127
 
121
128
  # 오름차순 정렬
122
129
  if sort_df: df = df.sort_values([self.date])
123
- df = df.reset_index()
130
+ df = df.reset_index(drop=True)
124
131
 
125
132
  self.list_index = df.index.tolist()
126
133
  self.xmin, self.xmax = (0, self.list_index[-1])
@@ -138,42 +145,42 @@ class DataMixin(CollectionMixin):
138
145
  if key not in set_key:
139
146
  raise KeyError(f'"{key}" column not found.\nset calc_ma=True or add "{key}" column.')
140
147
 
141
- df['_x'] = df.index + 0.5
142
- df['_left'] = df['_x'] - self.candle_width_half
143
- df['_right'] = df['_x'] + self.candle_width_half
144
- df['_volleft'] = df['_x'] - self.volume_width_half
145
- df['_volright'] = df['_x'] + self.volume_width_half
146
- df.loc[:, '_zero'] = 0
148
+ df['x'] = df.index + 0.5
149
+ df['left_candle'] = df['x'] - self.candle_width_half
150
+ df['right_candle'] = df['x'] + self.candle_width_half
151
+ df['left_volume'] = df['x'] - self.volume_width_half
152
+ df['right_volume'] = df['x'] + self.volume_width_half
153
+ df.loc[:, 'zero'] = 0
154
+
155
+ df['top_candle'] = np.where(df[self.Open] <= df[self.close], df[self.close], df[self.Open])
156
+ df['top_candle'] = np.where(df[self.close] < df[self.Open], df[self.Open], df[self.close])
157
+ df['bottom_candle'] = np.where(df[self.Open] <= df[self.close], df[self.Open], df[self.close])
158
+ df['bottom_candle'] = np.where(df[self.close] < df[self.Open], df[self.close], df[self.Open])
159
+
160
+ df['close_pre'] = df[self.close].shift(1)
161
+ if self.volume: df['ymax_volume'] = df[self.volume] * 1.2
147
162
 
148
- df['_top'] = np.where(df[self.Open] <= df[self.close], df[self.close], df[self.Open])
149
- df['_top'] = np.where(df[self.close] < df[self.Open], df[self.Open], df[self.close])
150
- df['_bottom'] = np.where(df[self.Open] <= df[self.close], df[self.Open], df[self.close])
151
- df['_bottom'] = np.where(df[self.close] < df[self.Open], df[self.close], df[self.Open])
163
+ self.df = df
164
+ return
152
165
 
153
- df['_pre'] = df[self.close].shift(1)
154
- if self.volume: df['_volymax'] = df[self.volume] * 1.2
166
+ def _validate_column_key(self, df: pd.DataFrame):
167
+ for i in ('date', 'Open', 'high', 'low', 'close', 'volume'):
168
+ v = getattr(self, i)
169
+ if v in _set_key: raise Exception(f'you can not set "{i}" to column key.\nself.{i}={v!r}')
155
170
 
156
- if not set_candlecolor:
171
+
172
+ if not self.set_candlecolor:
157
173
  keys = set(df.keys())
158
- for i in ('facecolor', 'edgecolor', 'volumefacecolor',):
174
+ for i in ('facecolor', 'edgecolor', 'facecolor_volume', 'edgecolor_volume',):
159
175
  if i not in keys:
160
176
  raise Exception(f'"{i}" column not in DataFrame.\nadd column or set set_candlecolor=True.')
161
- self.set_candlecolor = set_candlecolor
162
177
 
163
- if not set_volumecolor:
178
+ if not self.set_volumecolor:
164
179
  keys = set(df.keys())
165
- for i in ('volumefacecolor', 'volumeedgecolor',):
180
+ for i in ('facecolor_volume', 'edgecolor_volume',):
166
181
  if i not in keys:
167
182
  raise Exception(f'"{i}" column not in DataFrame.\nadd column or set set_volumecolor=True.')
168
- self.set_volumecolor = set_volumecolor
169
183
 
170
- self.df = df
171
- return
172
-
173
- def _validate_column_key(self):
174
- for i in ('date', 'Open', 'high', 'low', 'close', 'volume'):
175
- v = getattr(self, i)
176
- if v in _set_key: raise Exception(f'you can not set "{i}" to column key.\nself.{i}={v!r}')
177
184
  return
178
185
 
179
186
 
@@ -192,46 +199,46 @@ class SegmentMixin(DataMixin):
192
199
  def _get_segments(self):
193
200
  # 캔들 세그먼트
194
201
  segment_candle = self.df[[
195
- '_x', self.high,
196
- '_x', '_top',
197
- '_left', '_top',
198
- '_left', '_bottom',
199
- '_x', '_bottom',
200
- '_x', self.low,
201
- '_x', '_bottom',
202
- '_right', '_bottom',
203
- '_right', '_top',
204
- '_x', '_top',
205
- '_x', self.high,
206
- '_x', '_top',
202
+ 'x', self.high,
203
+ 'x', 'top_candle',
204
+ 'left_candle', 'top_candle',
205
+ 'left_candle', 'bottom_candle',
206
+ 'x', 'bottom_candle',
207
+ 'x', self.low,
208
+ 'x', 'bottom_candle',
209
+ 'right_candle', 'bottom_candle',
210
+ 'right_candle', 'top_candle',
211
+ 'x', 'top_candle',
212
+ 'x', self.high,
213
+ 'x', 'top_candle',
207
214
  ]].values
208
215
  self.segment_candle = segment_candle.reshape(segment_candle.shape[0], 12, 2)
209
216
 
210
217
  # 심지 세그먼트
211
218
  segment_wick = self.df[[
212
- '_x', self.high,
213
- '_x', self.low,
219
+ 'x', self.high,
220
+ 'x', self.low,
214
221
  ]].values
215
222
  self.segment_candle_wick = segment_wick.reshape(segment_wick.shape[0], 2, 2)
216
223
 
217
224
  # 종가 세그먼트
218
- segment_priceline = segment_wick = self.df[['_x', self.close]].values
225
+ segment_priceline = segment_wick = self.df[['x', self.close]].values
219
226
  self.segment_priceline = segment_priceline.reshape(1, *segment_wick.shape)
220
227
 
221
228
  if self.volume:
222
229
  # 거래량 바 세그먼트
223
230
  segment_volume = self.df[[
224
- '_volleft', '_zero',
225
- '_volleft', self.volume,
226
- '_volright', self.volume,
227
- '_volright', '_zero',
231
+ 'left_volume', 'zero',
232
+ 'left_volume', self.volume,
233
+ 'right_volume', self.volume,
234
+ 'right_volume', 'zero',
228
235
  ]].values
229
236
  self.segment_volume = segment_volume.reshape(segment_volume.shape[0], 4, 2)
230
237
 
231
238
  # 거래량 심지 세그먼트
232
239
  segment_volume_wick = self.df[[
233
- '_x', '_zero',
234
- '_x', self.volume,
240
+ 'x', 'zero',
241
+ 'x', self.volume,
235
242
  ]].values
236
243
  self.segment_volume_wick = segment_volume_wick.reshape(segment_volume_wick.shape[0], 2, 2)
237
244
 
@@ -251,26 +258,26 @@ class SegmentMixin(DataMixin):
251
258
  self.df.loc[self.df[self.close] == self.df[self.Open], ['facecolor', 'edgecolor']] = (self.color_flat, self.color_flat)
252
259
  if self.color_up != self.color_up_down:
253
260
  # 양봉(비우기)
254
- self.df.loc[(self.df['facecolor'] == self.color_up) & (self.df[self.close] <= self.df['_pre']), 'facecolor'] = self.color_up_down
261
+ self.df.loc[(self.df['facecolor'] == self.color_up) & (self.df[self.close] <= self.df['close_pre']), 'facecolor'] = self.color_up_down
255
262
  if self.color_down != self.color_down_up:
256
263
  # 음봉(비우기)
257
- self.df.loc[(self.df['facecolor'] == self.color_down) & (self.df['_pre'] <= self.df[self.close]), ['facecolor']] = self.color_down_up
264
+ self.df.loc[(self.df['facecolor'] == self.color_down) & (self.df['close_pre'] <= self.df[self.close]), ['facecolor']] = self.color_down_up
258
265
 
259
266
  self.facecolor_candle = self.df['facecolor'].values
260
267
  self.edgecolor_candle = self.df['edgecolor'].values
261
268
 
262
269
  if self.set_volumecolor:
263
270
  # 거래량
264
- self.df.loc[:, ['volumefacecolor', 'volumeedgecolor']] = (self.color_volume_up, self.color_volume_up)
271
+ self.df.loc[:, ['facecolor_volume', 'edgecolor_volume']] = (self.color_volume_up, self.color_volume_up)
265
272
  if self.color_up != self.color_down:
266
273
  # 전일대비 하락
267
- self.df.loc[self.df[self.close] < self.df['_pre'], ['volumefacecolor', 'volumeedgecolor']] = (self.color_volume_down, self.color_volume_down)
274
+ self.df.loc[self.df[self.close] < self.df['close_pre'], ['facecolor_volume', 'edgecolor_volume']] = (self.color_volume_down, self.color_volume_down)
268
275
  if self.color_up != self.color_flat:
269
276
  # 전일과 동일
270
- self.df.loc[self.df[self.close] == self.df['_pre'], ['volumefacecolor', 'volumeedgecolor']] = (self.color_volume_flat, self.color_volume_flat)
277
+ self.df.loc[self.df[self.close] == self.df['close_pre'], ['facecolor_volume', 'edgecolor_volume']] = (self.color_volume_flat, self.color_volume_flat)
271
278
 
272
- self.facecolor_volume = self.df['volumefacecolor'].values
273
- self.edgecolor_volume = self.df['volumeedgecolor'].values
279
+ self.facecolor_volume = self.df['facecolor_volume'].values
280
+ self.edgecolor_volume = self.df['edgecolor_volume'].values
274
281
 
275
282
  # 기존 legend 제거
276
283
  legends = self.ax_legend.get_legend()
@@ -307,7 +314,7 @@ class SegmentMixin(DataMixin):
307
314
  # 주가 차트 가격이동평균선
308
315
  key_ma = []
309
316
  for i in reversed(self.list_ma):
310
- key_ma.append('_x')
317
+ key_ma.append('x')
311
318
  key_ma.append(f'ma{i}')
312
319
  segment_ma = self.df[key_ma].values
313
320
  self.segment_ma = segment_ma.reshape(segment_ma.shape[0], len(self.list_ma), 2).swapaxes(0, 1)
@@ -318,7 +325,7 @@ class SegmentMixin(DataMixin):
318
325
  if index_start < 0: index_start = 0
319
326
  if index_end < 1: index_end = 1
320
327
 
321
- index_end += 1
328
+ index_end += 2
322
329
  if indsub < self.limit_candle:
323
330
  self._set_candle_segments(index_start, index_end)
324
331
  elif indsub < self.limit_wick:
@@ -492,7 +499,7 @@ class DrawMixin(EventMixin):
492
499
  self.price_ymin, self.price_ymax = (ymin-yspace, ymax+yspace)
493
500
 
494
501
  # 거래량 차트 ymax
495
- self.volume_ymax = self.df['_volymax'][xmin:xmax].max() if self.volume else 1
502
+ self.volume_ymax = self.df['ymax_volume'][xmin:xmax].max() if self.volume else 1
496
503
 
497
504
  self._set_segments(xmin, xmax, simpler, set_ma)
498
505
  self._change_lim(self.vxmin, self.vxmax)
@@ -125,10 +125,10 @@ class CollectionMixin(PlotMixin):
125
125
 
126
126
  keys = []
127
127
  for i in reversed(self.list_ma):
128
- keys.append('_x')
128
+ keys.append('x')
129
129
  keys.append(f'ma{i}')
130
130
 
131
- segment_slider = self.df[keys + ['_x', self.close] ].values
131
+ segment_slider = self.df[keys + ['x', self.close] ].values
132
132
  segment_slider = segment_slider.reshape(segment_slider.shape[0], len(self.list_ma)+1, 2).swapaxes(0, 1)
133
133
  self.collection_slider.set_segments(segment_slider)
134
134
  return
@@ -186,7 +186,7 @@ class NavigatorMixin(CollectionMixin):
186
186
  # 슬라이더 텍스트 y
187
187
  self.text_slider.set_y(ymax)
188
188
 
189
- self.navigator.set_linewidth([ysub, 5])
189
+ self.navigator.set_linewidth([ysub+ysub, 5])
190
190
 
191
191
  # 네비게이터 라인 선택 범위
192
192
  xsub = self.slider_xmax - self.slider_xmin
@@ -342,6 +342,16 @@ class MouseMoveMixin(BackgroundMixin):
342
342
  self.text_slider.draw(renderer)
343
343
  return
344
344
 
345
+ def _on_move_price_chart(self, e: MouseEvent):
346
+ self.slider_vline.set_segments([((e.xdata, self.slider_ymin), (e.xdata, self.slider_ymax))])
347
+ self.slider_vline.draw(self.figure.canvas.renderer)
348
+ return super()._on_move_price_chart(e)
349
+
350
+ def _on_move_volume_chart(self, e: MouseEvent):
351
+ self.slider_vline.set_segments([((e.xdata, self.slider_ymin), (e.xdata, self.slider_ymax))])
352
+ self.slider_vline.draw(self.figure.canvas.renderer)
353
+ return super()._on_move_volume_chart(e)
354
+
345
355
 
346
356
  class ClickMixin(MouseMoveMixin):
347
357
  x_click = None
@@ -451,7 +461,7 @@ class ReleaseMixin(SliderSelectMixin):
451
461
  return
452
462
 
453
463
  def _on_release(self, e: MouseEvent):
454
- if self.in_slider and self.is_click_slider: self._on_release_slider(e)
464
+ if self.in_slider and self.is_click_slider and e.button == MouseButton.LEFT: self._on_release_slider(e)
455
465
  return
456
466
 
457
467
  def _on_release_slider(self, e: MouseEvent):
@@ -471,6 +481,8 @@ class ReleaseMixin(SliderSelectMixin):
471
481
  self.is_click_slider = False
472
482
  self.is_move = False
473
483
  self.click_navleft, self.click_navright = (False, False)
484
+
485
+ self.draw_canvas()
474
486
  return
475
487
 
476
488
 
@@ -486,13 +498,13 @@ class ChartClickMixin(ReleaseMixin):
486
498
  if self.is_click_chart: return
487
499
 
488
500
  self.is_click_chart = True
489
- self._x_click = e.x.__round__(2)
501
+ self._x_click = e.x
490
502
  self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
491
503
  return
492
504
 
493
505
  def _on_release(self, e):
494
- if self.is_click_chart and (self.in_price_chart or self.in_volume_chart): self._on_release_chart(e)
495
- elif self.is_click_slider and self.in_slider: self._on_release_slider(e)
506
+ if self.is_click_chart and (self.in_price_chart or self.in_volume_chart) and e.button == MouseButton.LEFT: self._on_release_chart(e)
507
+ elif self.is_click_slider and self.in_slider and e.button == MouseButton.LEFT: self._on_release_slider(e)
496
508
  return
497
509
 
498
510
  def _on_release_chart(self, e):
@@ -530,7 +542,7 @@ class ChartClickMixin(ReleaseMixin):
530
542
  return super()._on_move_volume_chart(e)
531
543
 
532
544
  def _move_chart(self, e: MouseEvent):
533
- x = e.x.__round__(2)
545
+ x = e.x
534
546
  left, right = self.navcoordinate
535
547
  nsub = right - left
536
548
  xsub = x - self._x_click
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: seolpyo-mplchart
3
- Version: 1.2.0
3
+ Version: 1.3.0
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
@@ -32,18 +32,24 @@ Ethereum: 0x1c5fb8a5e0b1153cd4116c91736bd16fabf83520
32
32
 
33
33
 
34
34
  # Document
35
- [English](https://white.seolpyo.com/entry/148/)
35
+ [English Document](https://white.seolpyo.com/entry/148/)
36
36
 
37
- [한글](https://white.seolpyo.com/entry/147/)
37
+ [한글 설명서](https://white.seolpyo.com/entry/147/)
38
38
 
39
39
 
40
- # Sample
40
+ # Sample Image
41
+ ## sample
41
42
  ![sample gif](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/sample.gif)
42
43
 
44
+ ## tkinter sample
43
45
  ![tkinter sample gif](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/with%20tkinter.gif)
44
46
 
47
+ ## English format sample
45
48
  ![english sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/change%20color.png)
46
49
 
50
+ ## Korean format sample
47
51
  ![korean sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/sample%20kor.png)
48
52
 
53
+
54
+ # 40,000 data sample
49
55
  ![40,000 sample](https://raw.githubusercontent.com/white-seolpyo/seolpyo-mplchart/refs/heads/main/images/40000.gif)