seolpyo-mplchart 1.4.1__py3-none-any.whl → 2.0.0.3__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.
- seolpyo_mplchart/__init__.py +144 -308
- seolpyo_mplchart/_chart/__init__.py +137 -0
- seolpyo_mplchart/_chart/_base.py +217 -0
- seolpyo_mplchart/_chart/_cursor/__init__.py +2 -0
- seolpyo_mplchart/_chart/_cursor/_artist.py +217 -0
- seolpyo_mplchart/_chart/_cursor/_cursor.py +165 -0
- seolpyo_mplchart/_chart/_cursor/_info.py +187 -0
- seolpyo_mplchart/_chart/_draw/__init__.py +2 -0
- seolpyo_mplchart/_chart/_draw/_artist.py +50 -0
- seolpyo_mplchart/_chart/_draw/_data.py +314 -0
- seolpyo_mplchart/_chart/_draw/_draw.py +103 -0
- seolpyo_mplchart/_chart/_draw/_lim.py +265 -0
- seolpyo_mplchart/_chart/_slider/__init__.py +1 -0
- seolpyo_mplchart/_chart/_slider/_base.py +268 -0
- seolpyo_mplchart/_chart/_slider/_data.py +105 -0
- seolpyo_mplchart/_chart/_slider/_mouse.py +176 -0
- seolpyo_mplchart/_chart/_slider/_nav.py +204 -0
- seolpyo_mplchart/_chart/test.py +121 -0
- seolpyo_mplchart/_config/__init__.py +3 -0
- seolpyo_mplchart/_config/ax.py +28 -0
- seolpyo_mplchart/_config/candle.py +30 -0
- seolpyo_mplchart/_config/config.py +21 -0
- seolpyo_mplchart/_config/cursor.py +49 -0
- seolpyo_mplchart/_config/figure.py +41 -0
- seolpyo_mplchart/_config/format.py +51 -0
- seolpyo_mplchart/_config/ma.py +15 -0
- seolpyo_mplchart/_config/slider/__init__.py +2 -0
- seolpyo_mplchart/_config/slider/config.py +24 -0
- seolpyo_mplchart/_config/slider/figure.py +20 -0
- seolpyo_mplchart/_config/slider/nav.py +9 -0
- seolpyo_mplchart/_config/unit.py +19 -0
- seolpyo_mplchart/_config/utils.py +67 -0
- seolpyo_mplchart/_config/volume.py +26 -0
- seolpyo_mplchart/_cursor.py +27 -25
- seolpyo_mplchart/_draw.py +7 -18
- seolpyo_mplchart/_slider.py +26 -20
- seolpyo_mplchart/test.py +172 -56
- seolpyo_mplchart/xl_to_dict.py +47 -0
- seolpyo_mplchart-2.0.0.3.dist-info/METADATA +710 -0
- seolpyo_mplchart-2.0.0.3.dist-info/RECORD +50 -0
- {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.0.0.3.dist-info}/WHEEL +1 -1
- seolpyo_mplchart-1.4.1.dist-info/METADATA +0 -57
- seolpyo_mplchart-1.4.1.dist-info/RECORD +0 -17
- {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.0.0.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from matplotlib.backend_bases import MouseEvent, MouseButton, cursors
|
|
2
|
+
|
|
3
|
+
from ._data import BaseMixin as Base
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MouseMoveMixin(Base):
|
|
7
|
+
in_slider = False
|
|
8
|
+
is_click_slider = False
|
|
9
|
+
|
|
10
|
+
def _erase_crossline(self):
|
|
11
|
+
boolen = super()._erase_crossline()
|
|
12
|
+
if boolen:
|
|
13
|
+
return boolen
|
|
14
|
+
|
|
15
|
+
seg = self.collection_slider_vline.get_segments()
|
|
16
|
+
if seg:
|
|
17
|
+
self.collection_slider_vline.set_segments([])
|
|
18
|
+
return True
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
def _on_move_action(self, e):
|
|
22
|
+
super()._on_move_action(e)
|
|
23
|
+
|
|
24
|
+
if self.in_slider:
|
|
25
|
+
self._restore_region(self.is_click_slider)
|
|
26
|
+
self._on_move_slider(e)
|
|
27
|
+
self.figure.canvas.blit()
|
|
28
|
+
elif self.in_price_chart or self.in_volume_chart:
|
|
29
|
+
self._restore_region()
|
|
30
|
+
self._draw_crossline(e)
|
|
31
|
+
self.figure.canvas.blit()
|
|
32
|
+
else:
|
|
33
|
+
if self._erase_crossline():
|
|
34
|
+
self._restore_region()
|
|
35
|
+
self.figure.canvas.blit()
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
def _on_move_action(self, e: MouseEvent):
|
|
39
|
+
self._check_ax(e)
|
|
40
|
+
|
|
41
|
+
self.intx = None
|
|
42
|
+
if self.in_slider or self.in_price_chart or self.in_volume_chart:
|
|
43
|
+
self._get_x(e)
|
|
44
|
+
|
|
45
|
+
self._set_cursor(e)
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
def _set_cursor(self, e: MouseEvent):
|
|
49
|
+
# 마우스 커서 변경
|
|
50
|
+
if self.is_click_slider:
|
|
51
|
+
return
|
|
52
|
+
elif not self.in_slider:
|
|
53
|
+
self.figure.canvas.set_cursor(cursors.POINTER)
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
navleft, navright = self.navcoordinate
|
|
57
|
+
if navleft == navright:
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
x = e.xdata.__round__()
|
|
61
|
+
|
|
62
|
+
leftmin = navleft - self._navLineWidth
|
|
63
|
+
leftmax = navleft + self._navLineWidth_half
|
|
64
|
+
rightmin = navright - self._navLineWidth_half
|
|
65
|
+
rightmax = navright + self._navLineWidth
|
|
66
|
+
if x < leftmin:
|
|
67
|
+
self.figure.canvas.set_cursor(cursors.POINTER)
|
|
68
|
+
elif x <= leftmax:
|
|
69
|
+
self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
70
|
+
elif x < rightmin:
|
|
71
|
+
self.figure.canvas.set_cursor(cursors.MOVE)
|
|
72
|
+
elif x <= rightmax:
|
|
73
|
+
self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
74
|
+
else:
|
|
75
|
+
self.figure.canvas.set_cursor(cursors.POINTER)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
def _check_ax(self, e: MouseEvent):
|
|
79
|
+
ax = e.inaxes
|
|
80
|
+
if not ax or e.xdata is None or e.ydata is None:
|
|
81
|
+
self.in_slider, self.in_price_chart, self.in_volume_chart = (False, False, False)
|
|
82
|
+
else:
|
|
83
|
+
if ax is self.ax_slider:
|
|
84
|
+
self.in_slider = True
|
|
85
|
+
self.in_price_chart = False
|
|
86
|
+
self.in_volume_chart = False
|
|
87
|
+
elif ax is self.ax_price:
|
|
88
|
+
self.in_slider = False
|
|
89
|
+
self.in_price_chart = True
|
|
90
|
+
self.in_volume_chart = False
|
|
91
|
+
elif ax is self.ax_volume:
|
|
92
|
+
self.in_slider = False
|
|
93
|
+
self.in_price_chart = False
|
|
94
|
+
self.in_volume_chart = True
|
|
95
|
+
else:
|
|
96
|
+
self.in_slider = False
|
|
97
|
+
self.in_price_chart = False
|
|
98
|
+
self.in_volume_chart = False
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
def _on_move_slider(self, e: MouseEvent):
|
|
102
|
+
x = e.xdata
|
|
103
|
+
if self.intx is not None:
|
|
104
|
+
renderer = self.figure.canvas.renderer
|
|
105
|
+
seg = [((x, self.slider_ymin), (x, self.slider_ymax))]
|
|
106
|
+
self.collection_slider_vline.set_segments(seg)
|
|
107
|
+
self.collection_slider_vline.draw(renderer)
|
|
108
|
+
|
|
109
|
+
if self.in_slider:
|
|
110
|
+
self.artist_text_slider.set_text(f'{self.df["date"][self.intx]}')
|
|
111
|
+
self.artist_text_slider.set_x(x)
|
|
112
|
+
self.artist_text_slider.draw(renderer)
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
def _draw_crossline(self, e: MouseEvent):
|
|
116
|
+
x = e.xdata
|
|
117
|
+
self.collection_slider_vline.set_segments([((x-0.5, self.slider_ymin), (x-0.5, self.slider_ymax))])
|
|
118
|
+
self.collection_slider_vline.draw(self.figure.canvas.renderer)
|
|
119
|
+
return super()._draw_crossline(e)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ClickMixin(MouseMoveMixin):
|
|
123
|
+
x_click = None
|
|
124
|
+
is_move = False
|
|
125
|
+
click_navleft, click_navright = (False, False)
|
|
126
|
+
|
|
127
|
+
def _connect_events(self):
|
|
128
|
+
super()._connect_events()
|
|
129
|
+
|
|
130
|
+
self.figure.canvas.mpl_connect('button_press_event', lambda x: self.on_click(x))
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
def on_click(self, e: MouseEvent):
|
|
134
|
+
self._on_click(e)
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
def _on_click(self, e: MouseEvent):
|
|
138
|
+
if self.in_slider and not self.is_click_slider:
|
|
139
|
+
if e.button == MouseButton.LEFT:
|
|
140
|
+
self._on_click_slider(e)
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
def _on_click_slider(self, e: MouseEvent):
|
|
144
|
+
self.background_with_nav_pre = self.background_with_nav
|
|
145
|
+
|
|
146
|
+
self.is_click_slider = True
|
|
147
|
+
self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
148
|
+
|
|
149
|
+
navleft, navright = self.navcoordinate
|
|
150
|
+
x = e.xdata.__round__()
|
|
151
|
+
|
|
152
|
+
leftmax = navleft + self._navLineWidth_half
|
|
153
|
+
rightmin = navright - self._navLineWidth_half
|
|
154
|
+
|
|
155
|
+
grater_than_left = leftmax < x
|
|
156
|
+
less_then_right = x < rightmin
|
|
157
|
+
if grater_than_left and less_then_right:
|
|
158
|
+
self.is_move = True
|
|
159
|
+
self.x_click = x
|
|
160
|
+
else:
|
|
161
|
+
leftmin = navleft - self._navLineWidth
|
|
162
|
+
rightmax = navright + self._navLineWidth
|
|
163
|
+
if not grater_than_left and leftmin <= x:
|
|
164
|
+
self.click_navleft = True
|
|
165
|
+
self.x_click = navright
|
|
166
|
+
elif not less_then_right and x <= rightmax:
|
|
167
|
+
self.click_navright = True
|
|
168
|
+
self.x_click = navleft
|
|
169
|
+
else:
|
|
170
|
+
self.x_click = x
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class BaseMixin(ClickMixin):
|
|
175
|
+
pass
|
|
176
|
+
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
from matplotlib.backend_bases import MouseEvent, MouseButton, cursors
|
|
2
|
+
|
|
3
|
+
from ._mouse import BaseMixin as Base
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SliderSelectMixin(Base):
|
|
7
|
+
limit_ma = 8_000
|
|
8
|
+
|
|
9
|
+
def _on_move_slider(self, e):
|
|
10
|
+
if self.is_click_slider:
|
|
11
|
+
self._set_navcoordinate(e)
|
|
12
|
+
return super()._on_move_slider(e)
|
|
13
|
+
|
|
14
|
+
def _set_navcoordinate(self, e: MouseEvent):
|
|
15
|
+
navmin, navmax = self.navcoordinate
|
|
16
|
+
|
|
17
|
+
x = e.xdata.__int__()
|
|
18
|
+
if self.is_move:
|
|
19
|
+
xsub = self.x_click - x
|
|
20
|
+
navmin, navmax = (navmin-xsub, navmax-xsub)
|
|
21
|
+
|
|
22
|
+
# 값 보정
|
|
23
|
+
if navmax < 0:
|
|
24
|
+
navmin, navmax = (navmin-navmax, 0)
|
|
25
|
+
if self.index_list[-1] < navmin:
|
|
26
|
+
navmin, navmax = (self.index_list[-1], self.index_list[-1] + (navmax-navmin))
|
|
27
|
+
|
|
28
|
+
self.navcoordinate = (navmin, navmax)
|
|
29
|
+
self.x_click = x
|
|
30
|
+
|
|
31
|
+
self.axis(navmin, xmax=navmax+1, simpler=True, draw_ma=(navmax-navmin < self.limit_ma))
|
|
32
|
+
|
|
33
|
+
self._axis_navigator(navmin, navmax)
|
|
34
|
+
self.collection_navigator.draw(self.figure.canvas.renderer)
|
|
35
|
+
|
|
36
|
+
self.draw_artists()
|
|
37
|
+
self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
|
|
38
|
+
self._restore_region()
|
|
39
|
+
else:
|
|
40
|
+
navmin, navmax = (x, self.x_click) if x < self.x_click else (self.x_click, x)
|
|
41
|
+
|
|
42
|
+
# 슬라이더가 차트를 벗어나지 않도록 선택 영역 제한
|
|
43
|
+
if navmax < 0 or self.index_list[-1] < navmin:
|
|
44
|
+
seg = self.collection_navigator.get_segments()
|
|
45
|
+
navmin, navmax = (int(seg[1][0][0]), int(seg[3][0][0]))
|
|
46
|
+
|
|
47
|
+
nsub = navmax - navmin
|
|
48
|
+
min_distance = 5 if not self.min_distance or self.min_distance < 5 else self.min_distance
|
|
49
|
+
if nsub < min_distance:
|
|
50
|
+
self._restore_region(False, False)
|
|
51
|
+
self._axis_navigator(navmin, navmax)
|
|
52
|
+
self.collection_navigator.draw(self.figure.canvas.renderer)
|
|
53
|
+
else:
|
|
54
|
+
self.axis(navmin, xmax=navmax+1, simpler=True, draw_ma=(nsub < self.limit_ma))
|
|
55
|
+
self._axis_navigator(navmin, navmax)
|
|
56
|
+
|
|
57
|
+
self.collection_navigator.draw(self.figure.canvas.renderer)
|
|
58
|
+
|
|
59
|
+
self.draw_artists()
|
|
60
|
+
self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
|
|
61
|
+
self._restore_region(False, True)
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ReleaseMixin(SliderSelectMixin):
|
|
66
|
+
def _connect_events(self):
|
|
67
|
+
super()._connect_events()
|
|
68
|
+
|
|
69
|
+
self.figure.canvas.mpl_connect('button_release_event', lambda x: self.on_release(x))
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
def on_release(self, e: MouseEvent):
|
|
73
|
+
self._on_release(e)
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
def _on_release(self, e: MouseEvent):
|
|
77
|
+
if self.in_slider and self.is_click_slider:
|
|
78
|
+
if e.button == MouseButton.LEFT:
|
|
79
|
+
self._on_release_slider(e)
|
|
80
|
+
self.axis(self.vxmin, xmax=self.vxmax, simpler=False, draw_ma=True)
|
|
81
|
+
self.figure.canvas.draw()
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
def _on_release_slider(self, e: MouseEvent):
|
|
85
|
+
if not self.is_move:
|
|
86
|
+
seg = self.collection_navigator.get_segments()
|
|
87
|
+
navmin, navmax = (int(seg[1][0][0]), int(seg[3][0][0]))
|
|
88
|
+
nsub = navmax - navmin
|
|
89
|
+
min_distance = 5 if not self.min_distance or self.min_distance < 5 else self.min_distance
|
|
90
|
+
if min_distance <= nsub:
|
|
91
|
+
self.navcoordinate = (navmin, navmax)
|
|
92
|
+
else:
|
|
93
|
+
self.background_with_nav = self.background_with_nav_pre
|
|
94
|
+
navmin, navmax = self.navcoordinate
|
|
95
|
+
self.axis(navmin, xmax=navmax+1, simpler=False, draw_ma=True)
|
|
96
|
+
self._restore_region(False, True)
|
|
97
|
+
self.figure.canvas.blit()
|
|
98
|
+
self._axis_navigator(*self.navcoordinate)
|
|
99
|
+
|
|
100
|
+
self.is_click_slider = False
|
|
101
|
+
self.is_move = False
|
|
102
|
+
self.click_navleft, self.click_navright = (False, False)
|
|
103
|
+
|
|
104
|
+
# self.figure.canvas.draw()
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ChartClickMixin(ReleaseMixin):
|
|
109
|
+
is_click_chart = False
|
|
110
|
+
|
|
111
|
+
def _on_click(self, e: MouseEvent):
|
|
112
|
+
if e.button == MouseButton.LEFT:
|
|
113
|
+
if (
|
|
114
|
+
not self.is_click_chart
|
|
115
|
+
and (self.in_price_chart or self.in_volume_chart)
|
|
116
|
+
):
|
|
117
|
+
self._on_click_chart(e)
|
|
118
|
+
elif not self.is_click_slider and self.in_slider:
|
|
119
|
+
self._on_click_slider(e)
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
def _on_click_chart(self, e: MouseEvent):
|
|
123
|
+
self.is_click_chart = True
|
|
124
|
+
x = e.xdata.__int__()
|
|
125
|
+
self.x_click = x - self.navcoordinate[0]
|
|
126
|
+
self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
def _on_release(self, e):
|
|
130
|
+
if e.button == MouseButton.LEFT:
|
|
131
|
+
if (
|
|
132
|
+
self.is_click_chart
|
|
133
|
+
and (self.in_price_chart or self.in_volume_chart)
|
|
134
|
+
):
|
|
135
|
+
self._on_release_chart(e)
|
|
136
|
+
self.axis(self.vxmin, xmax=self.vxmax, simpler=False, draw_ma=True)
|
|
137
|
+
self.figure.canvas.draw()
|
|
138
|
+
elif self.is_click_slider and self.in_slider:
|
|
139
|
+
self._on_release_slider(e)
|
|
140
|
+
self.axis(self.vxmin, xmax=self.vxmax, simpler=False, draw_ma=True)
|
|
141
|
+
self.figure.canvas.draw()
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
def _on_release_chart(self, e):
|
|
145
|
+
self.is_click_chart = False
|
|
146
|
+
self.figure.canvas.set_cursor(cursors.POINTER)
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
def _set_cursor(self, e):
|
|
150
|
+
if self.is_click_chart:
|
|
151
|
+
return
|
|
152
|
+
return super()._set_cursor(e)
|
|
153
|
+
|
|
154
|
+
def _on_move_action(self, e):
|
|
155
|
+
super()._on_move_action(e)
|
|
156
|
+
|
|
157
|
+
if self.in_slider:
|
|
158
|
+
self._restore_region(self.is_click_slider)
|
|
159
|
+
self._on_move_slider(e)
|
|
160
|
+
self.figure.canvas.blit()
|
|
161
|
+
elif self.in_price_chart or self.in_volume_chart:
|
|
162
|
+
self._restore_region(self.is_click_chart)
|
|
163
|
+
if not self.is_click_chart:
|
|
164
|
+
self._draw_crossline(e)
|
|
165
|
+
else:
|
|
166
|
+
self._move_chart(e)
|
|
167
|
+
self.figure.canvas.blit()
|
|
168
|
+
else:
|
|
169
|
+
if self._erase_crossline():
|
|
170
|
+
self._restore_region()
|
|
171
|
+
self.figure.canvas.blit()
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
def _move_chart(self, e: MouseEvent):
|
|
175
|
+
left, right = self.navcoordinate
|
|
176
|
+
x = e.xdata.__int__() - left
|
|
177
|
+
xsub = x - self.x_click
|
|
178
|
+
if not xsub:
|
|
179
|
+
self.collection_navigator.draw(self.figure.canvas.renderer)
|
|
180
|
+
self.draw_artists()
|
|
181
|
+
else:
|
|
182
|
+
left, right = (left-xsub, right-xsub)
|
|
183
|
+
if right < 0 or self.df.index[-1] < left:
|
|
184
|
+
self._restore_region()
|
|
185
|
+
else:
|
|
186
|
+
self.x_click = x
|
|
187
|
+
self.navcoordinate = (left, right)
|
|
188
|
+
self.axis(left, xmax=right+1, simpler=True, draw_ma=(right-left < self.limit_ma))
|
|
189
|
+
self._axis_navigator(left, right)
|
|
190
|
+
self.collection_navigator.draw(self.figure.canvas.renderer)
|
|
191
|
+
|
|
192
|
+
self.draw_artists()
|
|
193
|
+
self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
|
|
194
|
+
self._restore_region()
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class BaseMixin(ChartClickMixin):
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class Chart(BaseMixin):
|
|
203
|
+
pass
|
|
204
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
name_pkg = 'seolpyo_mplchart'
|
|
4
|
+
path_pkg = Path(__file__)
|
|
5
|
+
while path_pkg.name != name_pkg:
|
|
6
|
+
path_pkg = path_pkg.parent
|
|
7
|
+
sys.path = [path_pkg.parent.__str__()] + sys.path
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
import pandas as pd
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
path_file = path_pkg / 'sample' / 'samsung.txt'
|
|
16
|
+
with open(path_file, 'r', encoding='utf-8') as txt:
|
|
17
|
+
data = json.load(txt)
|
|
18
|
+
df = pd.DataFrame(data[:100])
|
|
19
|
+
|
|
20
|
+
def test_base():
|
|
21
|
+
from seolpyo_mplchart._chart._base import Base
|
|
22
|
+
Base()
|
|
23
|
+
plt.show()
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
def test_draw():
|
|
27
|
+
from seolpyo_mplchart._chart._draw import Chart
|
|
28
|
+
c = Chart()
|
|
29
|
+
# c.CONFIG.CANDLE.FACECOLOR.bull_fall = 'y'
|
|
30
|
+
# c.CONFIG.CANDLE.EDGECOLOR.bull_fall = 'k'
|
|
31
|
+
# c.CONFIG.CANDLE.FACECOLOR.bear_rise = 'k'
|
|
32
|
+
# c.CONFIG.CANDLE.EDGECOLOR.bear_rise = 'pink'
|
|
33
|
+
# c.CONFIG.VOLUME.EDGECOLOR.rise = 'k'
|
|
34
|
+
# c.CONFIG.AX.facecolor = 'k'
|
|
35
|
+
# c.CONFIG.AX.TICK.edgecolor = 'yellow'
|
|
36
|
+
# c.CONFIG.AX.TICK.fontcolor = 'pink'
|
|
37
|
+
# c.set_color()
|
|
38
|
+
# c.volume = None
|
|
39
|
+
c.set_data(df)
|
|
40
|
+
plt.show()
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_cursor():
|
|
45
|
+
from seolpyo_mplchart._chart._cursor._info import Chart
|
|
46
|
+
from seolpyo_mplchart._config import DEFAULTCONFIG_EN
|
|
47
|
+
c = Chart()
|
|
48
|
+
# c.CONFIG = DEFAULTCONFIG_EN
|
|
49
|
+
c.refresh()
|
|
50
|
+
c.fraction = True
|
|
51
|
+
c.set_data(df)
|
|
52
|
+
plt.show()
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
def test_slider():
|
|
56
|
+
from seolpyo_mplchart._chart._slider import Chart
|
|
57
|
+
c = Chart()
|
|
58
|
+
c.set_data(df)
|
|
59
|
+
plt.show()
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == '__main__':
|
|
64
|
+
# test_base()
|
|
65
|
+
# test_draw()
|
|
66
|
+
# test_cursor()
|
|
67
|
+
test_slider()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
import seolpyo_mplchart as mc
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# class Chart(mc.SliderChart):
|
|
74
|
+
# format_candleinfo = mc.format_candleinfo_ko + '\nCustom info: {ci}'
|
|
75
|
+
# format_volumeinfo = mc.format_volumeinfo_ko
|
|
76
|
+
# min_distance = 2
|
|
77
|
+
|
|
78
|
+
# def __init__(self, *args, **kwargs):
|
|
79
|
+
# super().__init__(*args, **kwargs)
|
|
80
|
+
# self.collection_candle.set_linewidth(1.5)
|
|
81
|
+
# return
|
|
82
|
+
|
|
83
|
+
# def get_info_kwargs(self, is_price, **kwargs):
|
|
84
|
+
# if is_price:
|
|
85
|
+
# kwargs['ci'] = 'You can add Custom text Info or Change text info.'
|
|
86
|
+
# kwargs['close'] = 'You can Change close price info.'
|
|
87
|
+
# return kwargs
|
|
88
|
+
|
|
89
|
+
# def get_candle_segment(self, *, x, left, right, top, bottom, is_up, high, low):
|
|
90
|
+
# if is_up:
|
|
91
|
+
# return (
|
|
92
|
+
# (x, top), (right, top), (x, top),
|
|
93
|
+
# (x, high),
|
|
94
|
+
# (x, low),
|
|
95
|
+
# (x, bottom), (left, bottom), (x, bottom),
|
|
96
|
+
# )
|
|
97
|
+
# else:
|
|
98
|
+
# return (
|
|
99
|
+
# (x, bottom), (right, bottom), (x, bottom),
|
|
100
|
+
# (x, high),
|
|
101
|
+
# (x, low),
|
|
102
|
+
# (x, top), (left, top), (x, top),
|
|
103
|
+
# )
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# C = Chart()
|
|
108
|
+
# path_file = Path(__file__).parent / 'sample/samsung.txt'
|
|
109
|
+
# # C.format_candleinfo = mc.format_candleinfo_ko
|
|
110
|
+
# # C.format_volumeinfo = mc.format_volumeinfo_ko
|
|
111
|
+
# # C.volume = None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# with open(path_file, 'r', encoding='utf-8') as txt:
|
|
115
|
+
# data = json.load(txt)
|
|
116
|
+
# df = pd.DataFrame(data[:100])
|
|
117
|
+
|
|
118
|
+
# C.set_data(df)
|
|
119
|
+
|
|
120
|
+
# mc.show()
|
|
121
|
+
# mc.close()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class Grid:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self.visible = True
|
|
6
|
+
self.linewidth = 0.7
|
|
7
|
+
self.color: str|tuple[float, float, float, float] = '#d0d0d0'
|
|
8
|
+
self.linestyle = '-'
|
|
9
|
+
self.dashes = (1, 0)
|
|
10
|
+
self.axis = 'both'
|
|
11
|
+
|
|
12
|
+
GRID = Grid()
|
|
13
|
+
|
|
14
|
+
class TickData:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.edgecolor: str|tuple[float, float, float, float] = 'k'
|
|
17
|
+
self.fontcolor: str|tuple[float, float, float, float] = 'k'
|
|
18
|
+
|
|
19
|
+
TICK = TickData()
|
|
20
|
+
|
|
21
|
+
class AxData:
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self.facecolor: str|tuple[float, float, float, float] = '#fafafa'
|
|
24
|
+
self.GRID = GRID
|
|
25
|
+
self.TICK = TICK
|
|
26
|
+
|
|
27
|
+
AX = AxData()
|
|
28
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class CandleFaceColorData:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self.bull_rise: str|tuple[float, float, float, float] = '#FF2400'
|
|
6
|
+
self.bull_fall: str|tuple[float, float, float, float] = 'w'
|
|
7
|
+
self.bear_fall: str|tuple[float, float, float, float] = '#1E90FF'
|
|
8
|
+
self.bear_rise: str|tuple[float, float, float, float] = 'w'
|
|
9
|
+
|
|
10
|
+
CANDLEFACECOLOR = CandleFaceColorData()
|
|
11
|
+
|
|
12
|
+
class CandleEdgeColorData:
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.bull_rise: str|tuple[float, float, float, float] = '#FF2400'
|
|
15
|
+
self.bull_fall: str|tuple[float, float, float, float] = '#FF2400'
|
|
16
|
+
self.bear_fall: str|tuple[float, float, float, float] = '#1E90FF'
|
|
17
|
+
self.bear_rise: str|tuple[float, float, float, float] = '#1E90FF'
|
|
18
|
+
self.doji: str|tuple[float, float, float, float] = 'k'
|
|
19
|
+
|
|
20
|
+
CANDLEEDGECOLOR = CandleEdgeColorData()
|
|
21
|
+
|
|
22
|
+
class CandleData:
|
|
23
|
+
def __init__(self):
|
|
24
|
+
self.half_width = 0.24
|
|
25
|
+
self.line_color: str|tuple[float, float, float, float] = 'k'
|
|
26
|
+
self.FACECOLOR = CANDLEFACECOLOR
|
|
27
|
+
self.EDGECOLOR = CANDLEEDGECOLOR
|
|
28
|
+
|
|
29
|
+
CANDLE = CandleData()
|
|
30
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from . import figure, ax, candle, volume, ma, unit, cursor, format
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ConfigData:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.FIGURE = figure.FIGURE
|
|
7
|
+
self.UNIT = unit.UNIT
|
|
8
|
+
self.AX = ax.AX
|
|
9
|
+
self.CANDLE = candle.CANDLE
|
|
10
|
+
self.VOLUME = volume.VOLUME
|
|
11
|
+
self.MA = ma.MA
|
|
12
|
+
self.CURSOR = cursor.CURSOR
|
|
13
|
+
self.FORMAT = format.FORMAT
|
|
14
|
+
|
|
15
|
+
DEFAULTCONFIG = ConfigData()
|
|
16
|
+
|
|
17
|
+
DEFAULTCONFIG_EN = ConfigData()
|
|
18
|
+
DEFAULTCONFIG_EN.UNIT = unit.UNIT_EN
|
|
19
|
+
DEFAULTCONFIG_EN.MA = ma.MA_EN
|
|
20
|
+
DEFAULTCONFIG_EN.FORMAT = format.FORMAT_EN
|
|
21
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class CrossLineData:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self.edgecolor = 'k'
|
|
6
|
+
self.linewidth = 1
|
|
7
|
+
self.linestyle = '-'
|
|
8
|
+
|
|
9
|
+
CROSSLINE = CrossLineData()
|
|
10
|
+
|
|
11
|
+
class BBoxData:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.boxstyle = 'round'
|
|
14
|
+
self.facecolor = 'w'
|
|
15
|
+
self.edgecolor = 'k'
|
|
16
|
+
|
|
17
|
+
BBOX = BBoxData()
|
|
18
|
+
|
|
19
|
+
class Text:
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.color = 'k'
|
|
22
|
+
self.BBOX = BBOX
|
|
23
|
+
|
|
24
|
+
def to_dict(self):
|
|
25
|
+
data = {}
|
|
26
|
+
for k, v in self.__dict__.items():
|
|
27
|
+
if k == 'BBOX':
|
|
28
|
+
k = k.lower()
|
|
29
|
+
v = self.BBOX.__dict__
|
|
30
|
+
data[k] = v
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
TEXT = Text()
|
|
34
|
+
|
|
35
|
+
class Box:
|
|
36
|
+
def __init__(self):
|
|
37
|
+
self.edgecolor = 'k'
|
|
38
|
+
self.linewidth = 1.2
|
|
39
|
+
|
|
40
|
+
BOX = Box()
|
|
41
|
+
|
|
42
|
+
class Cursor:
|
|
43
|
+
def __init__(self):
|
|
44
|
+
self.CROSSLINE = CROSSLINE
|
|
45
|
+
self.TEXT = TEXT
|
|
46
|
+
self.BOX = BOX
|
|
47
|
+
|
|
48
|
+
CURSOR = Cursor()
|
|
49
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class RatioData:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self.legend = 2
|
|
6
|
+
self.price = 18
|
|
7
|
+
self.volume = 5
|
|
8
|
+
|
|
9
|
+
RATIO = RatioData()
|
|
10
|
+
|
|
11
|
+
class WatermarkData:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.alpha = 0.2
|
|
14
|
+
self.color = 'k'
|
|
15
|
+
self.fontsize = 20
|
|
16
|
+
|
|
17
|
+
WATERMARK = WatermarkData()
|
|
18
|
+
|
|
19
|
+
class AdjustData:
|
|
20
|
+
def __init__(self):
|
|
21
|
+
# 여백
|
|
22
|
+
self.top = 0.95
|
|
23
|
+
self.bottom = 0.05
|
|
24
|
+
self.left = 0.01
|
|
25
|
+
self.right = 0.93
|
|
26
|
+
# 플롯간 간격
|
|
27
|
+
self.wspace = 0
|
|
28
|
+
self.hspace = 0
|
|
29
|
+
|
|
30
|
+
ADJUST = AdjustData()
|
|
31
|
+
|
|
32
|
+
class FigureData:
|
|
33
|
+
def __init__(self):
|
|
34
|
+
self.facecolor: str|tuple[float, float, float, float] = 'w'
|
|
35
|
+
self.figsize = (14, 7)
|
|
36
|
+
self.RATIO = RATIO
|
|
37
|
+
self.ADJUST = ADJUST
|
|
38
|
+
self.WATERMARK = WATERMARK
|
|
39
|
+
|
|
40
|
+
FIGURE = FigureData()
|
|
41
|
+
|