seolpyo-mplchart 2.0.0.3__py3-none-any.whl → 2.1.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.
- seolpyo_mplchart/__init__.py +17 -133
- seolpyo_mplchart/_chart/__init__.py +39 -31
- seolpyo_mplchart/_chart/base/__init__.py +111 -0
- seolpyo_mplchart/_chart/base/a_canvas.py +250 -0
- seolpyo_mplchart/_chart/base/b_artist.py +143 -0
- seolpyo_mplchart/_chart/base/c_draw.py +100 -0
- seolpyo_mplchart/_chart/base/d_segment.py +262 -0
- seolpyo_mplchart/_chart/base/e_axis.py +267 -0
- seolpyo_mplchart/_chart/base/f_background.py +62 -0
- seolpyo_mplchart/_chart/base/g_event.py +66 -0
- seolpyo_mplchart/_chart/base/h_data.py +138 -0
- seolpyo_mplchart/_chart/base/test.py +58 -0
- seolpyo_mplchart/_chart/cursor/__init__.py +125 -0
- seolpyo_mplchart/_chart/cursor/b_artist.py +130 -0
- seolpyo_mplchart/_chart/cursor/c_draw.py +96 -0
- seolpyo_mplchart/_chart/cursor/d_segment.py +359 -0
- seolpyo_mplchart/_chart/cursor/e_axis.py +65 -0
- seolpyo_mplchart/_chart/cursor/g_event.py +233 -0
- seolpyo_mplchart/_chart/cursor/h_data.py +61 -0
- seolpyo_mplchart/_chart/cursor/test.py +69 -0
- seolpyo_mplchart/_chart/slider/__init__.py +169 -0
- seolpyo_mplchart/_chart/slider/a_canvas.py +260 -0
- seolpyo_mplchart/_chart/slider/b_artist.py +91 -0
- seolpyo_mplchart/_chart/slider/c_draw.py +54 -0
- seolpyo_mplchart/_chart/slider/d_segment.py +166 -0
- seolpyo_mplchart/_chart/slider/e_axis.py +70 -0
- seolpyo_mplchart/_chart/slider/f_background.py +37 -0
- seolpyo_mplchart/_chart/slider/g_event.py +353 -0
- seolpyo_mplchart/_chart/slider/h_data.py +102 -0
- seolpyo_mplchart/_chart/slider/test.py +71 -0
- seolpyo_mplchart/_config/candle.py +1 -0
- seolpyo_mplchart/_config/figure.py +3 -4
- seolpyo_mplchart/_config/ma.py +2 -0
- seolpyo_mplchart/_config/slider/config.py +2 -2
- seolpyo_mplchart/_config/slider/figure.py +3 -4
- seolpyo_mplchart/_config/slider/nav.py +3 -2
- seolpyo_mplchart/_config/volume.py +1 -0
- seolpyo_mplchart/_utils/__init__.py +10 -0
- seolpyo_mplchart/_utils/nums.py +67 -0
- seolpyo_mplchart/_utils/theme/__init__.py +15 -0
- seolpyo_mplchart/_utils/theme/dark.py +57 -0
- seolpyo_mplchart/_utils/theme/light.py +56 -0
- seolpyo_mplchart/_utils/utils.py +28 -0
- seolpyo_mplchart/_utils/xl/__init__.py +15 -0
- seolpyo_mplchart/_utils/xl/csv.py +46 -0
- seolpyo_mplchart/_utils/xl/xlsx.py +49 -0
- seolpyo_mplchart/sample/apple.txt +6058 -0
- seolpyo_mplchart/sample/samsung.txt +5938 -0
- seolpyo_mplchart/test.py +5 -5
- {seolpyo_mplchart-2.0.0.3.dist-info → seolpyo_mplchart-2.1.0.dist-info}/METADATA +21 -13
- seolpyo_mplchart-2.1.0.dist-info/RECORD +89 -0
- seolpyo_mplchart-2.0.0.3.dist-info/RECORD +0 -50
- {seolpyo_mplchart-2.0.0.3.dist-info → seolpyo_mplchart-2.1.0.dist-info}/WHEEL +0 -0
- {seolpyo_mplchart-2.0.0.3.dist-info → seolpyo_mplchart-2.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from matplotlib.axes import Axes
|
|
2
|
+
from matplotlib.collections import LineCollection
|
|
3
|
+
from matplotlib.lines import Line2D
|
|
4
|
+
from matplotlib.text import Text
|
|
5
|
+
|
|
6
|
+
from ..._config import ConfigData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WatermarkMixin:
|
|
10
|
+
CONFIG: ConfigData
|
|
11
|
+
|
|
12
|
+
watermark = 'seolpyo mplchart'
|
|
13
|
+
|
|
14
|
+
ax_price: Axes
|
|
15
|
+
|
|
16
|
+
def _add_watermark(self):
|
|
17
|
+
self.artist_watermark = Text(
|
|
18
|
+
x=0.5, y=0.5, text='',
|
|
19
|
+
animated=True,
|
|
20
|
+
horizontalalignment='center', verticalalignment='center',
|
|
21
|
+
transform=self.ax_price.transAxes
|
|
22
|
+
)
|
|
23
|
+
self.ax_price.add_artist(self.artist_watermark)
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
def _set_watermark(self):
|
|
27
|
+
self.artist_watermark.set(animated=True, horizontalalignment='center', verticalalignment='center',)
|
|
28
|
+
self.artist_watermark.set_text(self.watermark)
|
|
29
|
+
self.artist_watermark.set_fontsize(self.CONFIG.FIGURE.WATERMARK.fontsize)
|
|
30
|
+
self.artist_watermark.set_color(self.CONFIG.FIGURE.WATERMARK.color)
|
|
31
|
+
self.artist_watermark.set_alpha(self.CONFIG.FIGURE.WATERMARK.alpha)
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CollectionMixin:
|
|
36
|
+
ax_price: Axes
|
|
37
|
+
ax_volume: Axes
|
|
38
|
+
ax_legend: Axes
|
|
39
|
+
|
|
40
|
+
def _add_collections(self):
|
|
41
|
+
self.collection_candle = LineCollection([], animated=True, linewidths=0.8)
|
|
42
|
+
self.ax_price.add_collection(self.collection_candle)
|
|
43
|
+
|
|
44
|
+
self.collection_ma = LineCollection([], animated=True, linewidths=1)
|
|
45
|
+
self.ax_price.add_collection(self.collection_ma)
|
|
46
|
+
|
|
47
|
+
self.collection_volume = LineCollection([], animated=True, linewidths=1)
|
|
48
|
+
self.ax_volume.add_collection(self.collection_volume)
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
def set_collection_candle(self, segment, facecolors, edgecolors):
|
|
52
|
+
self.collection_candle.set_segments(segment)
|
|
53
|
+
self.collection_candle.set_facecolor(facecolors)
|
|
54
|
+
self.collection_candle.set_edgecolor(edgecolors)
|
|
55
|
+
self.collection_candle.set_transform(self.ax_price.transData)
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
def set_collection_volume(self, segment, facecolors, edgecolors):
|
|
59
|
+
self.collection_volume.set_segments(segment)
|
|
60
|
+
self.collection_volume.set_facecolor(facecolors)
|
|
61
|
+
self.collection_volume.set_edgecolor(edgecolors)
|
|
62
|
+
self.collection_volume.set_transform(self.ax_volume.transData)
|
|
63
|
+
self.collection_volume.set_antialiased(False)
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class MaMixin:
|
|
68
|
+
CONFIG: ConfigData
|
|
69
|
+
_visible_ma = set()
|
|
70
|
+
|
|
71
|
+
ax_legend: Axes
|
|
72
|
+
ax_price: Axes
|
|
73
|
+
collection_ma: LineCollection
|
|
74
|
+
_set_figure_ratios: callable
|
|
75
|
+
|
|
76
|
+
def set_collection_ma(self, segment, edgecolors):
|
|
77
|
+
self.collection_ma.set_segments(segment)
|
|
78
|
+
self.collection_ma.set_facecolor([])
|
|
79
|
+
self.collection_ma.set_edgecolor(edgecolors)
|
|
80
|
+
# print(self.collection_ma.get_linewidth())
|
|
81
|
+
self.collection_ma.set_linewidth(self.CONFIG.MA.linewidth)
|
|
82
|
+
self.collection_ma.set_transform(self.ax_price.transData)
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
def _set_legends(self):
|
|
86
|
+
legends = self.ax_legend.get_legend()
|
|
87
|
+
if legends:
|
|
88
|
+
legends.remove()
|
|
89
|
+
|
|
90
|
+
self._visible_ma.clear()
|
|
91
|
+
|
|
92
|
+
label_list, handle_list, edgecolor_list = ([], [], [])
|
|
93
|
+
# Legend Ax에 표시하는 선 segment
|
|
94
|
+
arr = [0, 1]
|
|
95
|
+
for n, ma in enumerate(self.CONFIG.MA.ma_list):
|
|
96
|
+
self._visible_ma.add(ma)
|
|
97
|
+
label_list.append(self.CONFIG.MA.format.format(ma))
|
|
98
|
+
try:
|
|
99
|
+
color = self.CONFIG.MA.color_list[n]
|
|
100
|
+
except:
|
|
101
|
+
color = self.CONFIG.MA.color_default
|
|
102
|
+
edgecolor_list.append(color)
|
|
103
|
+
handle_list.append(Line2D(arr, ydata=arr, color=color, linewidth=5, label=ma))
|
|
104
|
+
|
|
105
|
+
self.set_collection_ma([], edgecolors=edgecolor_list)
|
|
106
|
+
|
|
107
|
+
# 가격이동평균선 legend 생성
|
|
108
|
+
if handle_list:
|
|
109
|
+
legends = self.ax_legend.legend(
|
|
110
|
+
handle_list, label_list, loc='lower left', ncol=self.CONFIG.MA.ncol,
|
|
111
|
+
borderpad=0.55,
|
|
112
|
+
facecolor=self.CONFIG.AX.facecolor, edgecolor=self.CONFIG.AX.TICK.edgecolor,
|
|
113
|
+
labelcolor=self.CONFIG.AX.TICK.fontcolor,
|
|
114
|
+
)
|
|
115
|
+
for handle in legends.legend_handles:
|
|
116
|
+
# legend 클릭시 pick event가 발생할 수 있도록 설정
|
|
117
|
+
handle.set_picker(5)
|
|
118
|
+
|
|
119
|
+
# legend ax 크기 조정
|
|
120
|
+
self._set_figure_ratios()
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ArtistMixin(WatermarkMixin, CollectionMixin, MaMixin):
|
|
125
|
+
artist_watermark: Text
|
|
126
|
+
collection_candle: LineCollection
|
|
127
|
+
collection_volume: LineCollection
|
|
128
|
+
collection_ma: LineCollection
|
|
129
|
+
|
|
130
|
+
_visible_ma: set[int] = set()
|
|
131
|
+
|
|
132
|
+
def add_artists(self):
|
|
133
|
+
self._add_watermark()
|
|
134
|
+
self._set_watermark()
|
|
135
|
+
self._add_collections()
|
|
136
|
+
self._set_legends()
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
def set_artists(self):
|
|
140
|
+
self._set_watermark()
|
|
141
|
+
self._set_legends()
|
|
142
|
+
return
|
|
143
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from matplotlib.axes import Axes
|
|
2
|
+
from matplotlib.collections import LineCollection
|
|
3
|
+
from matplotlib.text import Text
|
|
4
|
+
|
|
5
|
+
from .a_canvas import Figure
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Base:
|
|
9
|
+
candle_on_ma = True
|
|
10
|
+
|
|
11
|
+
watermark: str
|
|
12
|
+
|
|
13
|
+
figure: Figure
|
|
14
|
+
ax_legend: Axes
|
|
15
|
+
ax_price: Axes
|
|
16
|
+
ax_volume: Axes
|
|
17
|
+
|
|
18
|
+
artist_watermark: Text
|
|
19
|
+
collection_candle: LineCollection
|
|
20
|
+
collection_volume: LineCollection
|
|
21
|
+
collection_ma: LineCollection
|
|
22
|
+
|
|
23
|
+
def draw_chart(self):
|
|
24
|
+
self._draw_ax_price()
|
|
25
|
+
self._draw_ax_volume()
|
|
26
|
+
self._draw_chart_price()
|
|
27
|
+
self._draw_chart_volume()
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
def _draw_chart_volume(self):
|
|
31
|
+
renderer = self.figure.canvas.renderer
|
|
32
|
+
|
|
33
|
+
self.collection_volume.draw(renderer)
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
def _draw_chart_price(self):
|
|
37
|
+
renderer = self.figure.canvas.renderer
|
|
38
|
+
# print(f'{renderer=}')
|
|
39
|
+
|
|
40
|
+
# print(self.collection_candle.get_segments())
|
|
41
|
+
if self.candle_on_ma:
|
|
42
|
+
self.collection_ma.draw(renderer)
|
|
43
|
+
self.collection_candle.draw(renderer)
|
|
44
|
+
else:
|
|
45
|
+
self.collection_candle.draw(renderer)
|
|
46
|
+
self.collection_ma.draw(renderer)
|
|
47
|
+
|
|
48
|
+
if self.watermark:
|
|
49
|
+
if self.watermark != self.artist_watermark.get_text():
|
|
50
|
+
self.artist_watermark.set_text(self.watermark)
|
|
51
|
+
self.artist_watermark.draw(renderer)
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
def draw_artists(self):
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
def draw_background(self):
|
|
58
|
+
self._draw_ax(self.ax_price)
|
|
59
|
+
self._draw_ax(self.ax_volume)
|
|
60
|
+
|
|
61
|
+
legend = self.ax_legend.get_legend()
|
|
62
|
+
if legend:
|
|
63
|
+
legend.draw(self.figure.canvas.renderer)
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
def _draw_ax(self, ax: Axes):
|
|
67
|
+
renderer = self.figure.canvas.renderer
|
|
68
|
+
|
|
69
|
+
# ax 배경
|
|
70
|
+
ax.patch.draw(renderer)
|
|
71
|
+
|
|
72
|
+
# ax 외곽선
|
|
73
|
+
for spine in ax.spines.values():
|
|
74
|
+
spine.draw(renderer)
|
|
75
|
+
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
def _draw_ax_volume(self):
|
|
79
|
+
renderer = self.figure.canvas.renderer
|
|
80
|
+
|
|
81
|
+
# grid, tick, ticklabel
|
|
82
|
+
ax = self.ax_volume
|
|
83
|
+
for axis in (ax.xaxis, ax.yaxis):
|
|
84
|
+
axis.draw(renderer)
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
def _draw_ax_price(self):
|
|
88
|
+
renderer = self.figure.canvas.renderer
|
|
89
|
+
# print(f'{renderer=}')
|
|
90
|
+
|
|
91
|
+
# grid, tick, ticklabel
|
|
92
|
+
ax = self.ax_price
|
|
93
|
+
for axis in (ax.xaxis, ax.yaxis):
|
|
94
|
+
axis.draw(renderer)
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class DrawMixin(Base):
|
|
99
|
+
candle_on_ma = True
|
|
100
|
+
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from ..._config import ConfigData
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DataMixin:
|
|
8
|
+
df: pd.DataFrame
|
|
9
|
+
CONFIG: ConfigData
|
|
10
|
+
|
|
11
|
+
def add_volume_color_column(self):
|
|
12
|
+
columns = ['facecolor_volume', 'edgecolor_volume']
|
|
13
|
+
face_rise = self.CONFIG.VOLUME.FACECOLOR.rise
|
|
14
|
+
face_fall = self.CONFIG.VOLUME.FACECOLOR.fall
|
|
15
|
+
edge_rise = self.CONFIG.VOLUME.EDGECOLOR.rise
|
|
16
|
+
edge_fall = self.CONFIG.VOLUME.EDGECOLOR.fall
|
|
17
|
+
face_doji = self.CONFIG.VOLUME.FACECOLOR.doji
|
|
18
|
+
edge_doji = self.CONFIG.VOLUME.EDGECOLOR.doji
|
|
19
|
+
|
|
20
|
+
# 주가 상승
|
|
21
|
+
self.df.loc[:, columns] = (face_rise, edge_rise)
|
|
22
|
+
if face_rise != face_fall or edge_rise != edge_fall:
|
|
23
|
+
# 주가 하락
|
|
24
|
+
condition = self.df['close'] < self.df['pre_close']
|
|
25
|
+
self.df.loc[condition, columns] = (face_fall, edge_fall)
|
|
26
|
+
if face_rise != face_doji or edge_rise != edge_doji:
|
|
27
|
+
# 보합
|
|
28
|
+
condition = self.df['close'] == self.df['pre_close']
|
|
29
|
+
self.df.loc[condition, columns] = (edge_doji, edge_doji)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
def add_candle_color_column(self):
|
|
33
|
+
columns = ['facecolor', 'edgecolor']
|
|
34
|
+
face_bull_rise = self.CONFIG.CANDLE.FACECOLOR.bull_rise
|
|
35
|
+
face_bull_fall = self.CONFIG.CANDLE.FACECOLOR.bull_fall
|
|
36
|
+
face_bear_rise = self.CONFIG.CANDLE.FACECOLOR.bear_rise
|
|
37
|
+
face_bear_fall = self.CONFIG.CANDLE.FACECOLOR.bear_fall
|
|
38
|
+
edge_bull_rise = self.CONFIG.CANDLE.EDGECOLOR.bull_rise
|
|
39
|
+
edge_bull_fall = self.CONFIG.CANDLE.EDGECOLOR.bull_fall
|
|
40
|
+
edge_bear_rise = self.CONFIG.CANDLE.EDGECOLOR.bear_rise
|
|
41
|
+
edge_bear_fall = self.CONFIG.CANDLE.EDGECOLOR.bear_fall
|
|
42
|
+
doji = self.CONFIG.CANDLE.EDGECOLOR.doji
|
|
43
|
+
|
|
44
|
+
# 상승양봉
|
|
45
|
+
self.df.loc[:, columns] = (face_bull_rise, edge_bull_rise)
|
|
46
|
+
if face_bull_rise != face_bear_fall or edge_bull_rise != edge_bear_fall:
|
|
47
|
+
# 하락음봉
|
|
48
|
+
self.df.loc[self.df['close'] < self.df['open'], columns] = (face_bear_fall, edge_bear_fall)
|
|
49
|
+
if face_bull_rise != doji or face_bear_fall != doji or edge_bull_rise != doji or edge_bear_fall != doji:
|
|
50
|
+
# 보합
|
|
51
|
+
self.df.loc[self.df['close'] == self.df['open'], columns] = (doji, doji)
|
|
52
|
+
|
|
53
|
+
if face_bull_rise != face_bull_fall or edge_bull_rise != edge_bull_fall:
|
|
54
|
+
# 하락양봉(비우기)
|
|
55
|
+
self.df.loc[(self.df['facecolor'] == face_bull_rise) & (self.df['close'] <= self.df['pre_close']), columns] = (face_bull_fall, edge_bull_fall)
|
|
56
|
+
if face_bear_fall != face_bear_rise or edge_bear_fall != edge_bear_rise:
|
|
57
|
+
# 상승음봉(비우기)
|
|
58
|
+
self.df.loc[(self.df['facecolor'] == face_bear_fall) & (self.df['pre_close'] <= self.df['close']), columns] = (face_bear_rise, edge_bear_rise)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class VolumeSegmentMixin(DataMixin):
|
|
63
|
+
key_volume: str
|
|
64
|
+
|
|
65
|
+
segment_volume: np.ndarray
|
|
66
|
+
segment_volume_wick: np.ndarray
|
|
67
|
+
facecolor_volume: np.ndarray
|
|
68
|
+
edgecolor_volume: np.ndarray
|
|
69
|
+
|
|
70
|
+
def set_volume_color_segments(self):
|
|
71
|
+
self.add_volume_color_column()
|
|
72
|
+
|
|
73
|
+
self.facecolor_volume = self.df['facecolor_volume'].values
|
|
74
|
+
self.edgecolor_volume = self.df['edgecolor_volume'].values
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
def set_volume_segments(self):
|
|
78
|
+
# 거래량 바 세그먼트
|
|
79
|
+
segment_volume_wick = self.df[[
|
|
80
|
+
'left_volume', 'zero',
|
|
81
|
+
'left_volume', 'volume',
|
|
82
|
+
'right_volume', 'volume',
|
|
83
|
+
'right_volume', 'zero',
|
|
84
|
+
]].values
|
|
85
|
+
|
|
86
|
+
self.segment_volume = segment_volume_wick.reshape(segment_volume_wick.shape[0], 4, 2)
|
|
87
|
+
|
|
88
|
+
# 거래량 심지 세그먼트
|
|
89
|
+
segment_volume_wick = self.df[[
|
|
90
|
+
'x', 'zero',
|
|
91
|
+
'x', 'volume',
|
|
92
|
+
]].values
|
|
93
|
+
self.segment_volume_wick = segment_volume_wick.reshape(segment_volume_wick.shape[0], 2, 2)
|
|
94
|
+
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class MethodMixin:
|
|
99
|
+
def get_candle_segment(self, *, is_up, x, left, right, top, bottom, high, low):
|
|
100
|
+
"""
|
|
101
|
+
get candle segment
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
is_up (bool): (True if open < close else False)
|
|
105
|
+
x (float): center of candle
|
|
106
|
+
left (float): left of candle
|
|
107
|
+
right (float): right of candle
|
|
108
|
+
top (float): top of candle(close if `is_up` else open)
|
|
109
|
+
bottom (float): bottom of candle(open if `is_up` else close)
|
|
110
|
+
high (float): top of candle wick
|
|
111
|
+
low (float): bottom of candle wick
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
tuple[tuple[float, float]]: candle segment
|
|
115
|
+
"""
|
|
116
|
+
return (
|
|
117
|
+
(x, top),
|
|
118
|
+
(left, top), (left, bottom),
|
|
119
|
+
(x, bottom), (x, low), (x, bottom),
|
|
120
|
+
(right, bottom), (right, top),
|
|
121
|
+
(x, top), (x, high)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def get_bar_segment(self, *, is_up, x, left, right, top, bottom, high, low):
|
|
125
|
+
if is_up:
|
|
126
|
+
return (
|
|
127
|
+
(x, top),
|
|
128
|
+
(x, high),
|
|
129
|
+
(x, top),
|
|
130
|
+
(right, top),
|
|
131
|
+
(x, top),
|
|
132
|
+
(x, low),
|
|
133
|
+
(x, bottom),
|
|
134
|
+
(left, bottom),
|
|
135
|
+
(x, bottom),
|
|
136
|
+
)
|
|
137
|
+
return (
|
|
138
|
+
(x, top),
|
|
139
|
+
(x, high),
|
|
140
|
+
(x, top),
|
|
141
|
+
(left, top),
|
|
142
|
+
(x, top),
|
|
143
|
+
(x, low),
|
|
144
|
+
(x, bottom),
|
|
145
|
+
(right, bottom),
|
|
146
|
+
(x, bottom),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class CandleSegmentMixin(MethodMixin, DataMixin):
|
|
151
|
+
segment_candle: np.ndarray
|
|
152
|
+
segment_candle_wick: np.ndarray
|
|
153
|
+
segment_priceline: np.ndarray
|
|
154
|
+
facecolor_candle: np.ndarray
|
|
155
|
+
edgecolor_candle: np.ndarray
|
|
156
|
+
|
|
157
|
+
def set_candle_color_segments(self):
|
|
158
|
+
self.add_candle_color_column()
|
|
159
|
+
|
|
160
|
+
self.facecolor_candle = self.df['facecolor'].values
|
|
161
|
+
self.edgecolor_candle = self.df['edgecolor'].values
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
def set_candle_segments(self):
|
|
165
|
+
# 캔들 세그먼트
|
|
166
|
+
segment_candle = []
|
|
167
|
+
for x, left, right, top, bottom, is_up, high, low in zip(
|
|
168
|
+
self.df['x'].to_numpy().tolist(),
|
|
169
|
+
self.df['left_candle'].to_numpy().tolist(), self.df['right_candle'].to_numpy().tolist(),
|
|
170
|
+
self.df['top_candle'].to_numpy().tolist(), self.df['bottom_candle'].to_numpy().tolist(),
|
|
171
|
+
self.df['is_up'].to_numpy().tolist(),
|
|
172
|
+
self.df['high'].to_numpy().tolist(), self.df['low'].to_numpy().tolist(),
|
|
173
|
+
):
|
|
174
|
+
segment_candle.append(
|
|
175
|
+
self.get_candle_segment(
|
|
176
|
+
is_up=is_up,
|
|
177
|
+
x=x, left=left, right=right,
|
|
178
|
+
top=top, bottom=bottom,
|
|
179
|
+
high=high, low=low,
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
self.segment_candle = np.array(segment_candle)
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
def _set_candle_segments(self):
|
|
187
|
+
# 심지 세그먼트
|
|
188
|
+
segment_wick = self.df[[
|
|
189
|
+
'x', 'high',
|
|
190
|
+
'x', 'low',
|
|
191
|
+
]].values
|
|
192
|
+
self.segment_candle_wick = segment_wick.reshape(segment_wick.shape[0], 2, 2)
|
|
193
|
+
# 종가 세그먼트
|
|
194
|
+
segment_priceline = segment_wick = self.df[['x', 'close']].values
|
|
195
|
+
self.segment_priceline = segment_priceline.reshape(1, *segment_wick.shape)
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class MaSegmentMixin(DataMixin):
|
|
200
|
+
_visible_ma: set
|
|
201
|
+
|
|
202
|
+
segment_ma: np.ndarray
|
|
203
|
+
edgecolor_ma: np.ndarray
|
|
204
|
+
|
|
205
|
+
def set_ma_segments(self):
|
|
206
|
+
# 주가 차트 가격이동평균선
|
|
207
|
+
key_ma = []
|
|
208
|
+
for i in reversed(self.CONFIG.MA.ma_list):
|
|
209
|
+
key_ma.append('x')
|
|
210
|
+
key_ma.append(f'ma{i}')
|
|
211
|
+
if key_ma:
|
|
212
|
+
segment_ma = self.df[key_ma].values
|
|
213
|
+
self.segment_ma = segment_ma.reshape(
|
|
214
|
+
segment_ma.shape[0], len(self.CONFIG.MA.ma_list), 2
|
|
215
|
+
).swapaxes(0, 1)
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
def _set_ma_color_segments(self):
|
|
219
|
+
# 이평선 색상 가져오기
|
|
220
|
+
edgecolors = []
|
|
221
|
+
for n, _ in enumerate(self.CONFIG.MA.ma_list):
|
|
222
|
+
try:
|
|
223
|
+
c = self.CONFIG.MA.color_list[n]
|
|
224
|
+
except:
|
|
225
|
+
c = self.CONFIG.MA.color_default
|
|
226
|
+
edgecolors.append(c)
|
|
227
|
+
|
|
228
|
+
self.edgecolor_ma = list(reversed(edgecolors))
|
|
229
|
+
|
|
230
|
+
return
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class SegmentMixin(CandleSegmentMixin, VolumeSegmentMixin, MaSegmentMixin):
|
|
234
|
+
segment_volume: np.ndarray
|
|
235
|
+
segment_volume_wick: np.ndarray
|
|
236
|
+
facecolor_volume: np.ndarray
|
|
237
|
+
edgecolor_volume: np.ndarray
|
|
238
|
+
|
|
239
|
+
segment_candle: np.ndarray
|
|
240
|
+
segment_candle_wick: np.ndarray
|
|
241
|
+
segment_priceline: np.ndarray
|
|
242
|
+
facecolor_candle: np.ndarray
|
|
243
|
+
edgecolor_candle: np.ndarray
|
|
244
|
+
|
|
245
|
+
segment_ma: np.ndarray
|
|
246
|
+
edgecolor_ma: np.ndarray
|
|
247
|
+
|
|
248
|
+
def set_segments(self):
|
|
249
|
+
self.set_candle_segments()
|
|
250
|
+
self._set_candle_segments()
|
|
251
|
+
self.set_volume_segments()
|
|
252
|
+
self.set_ma_segments()
|
|
253
|
+
|
|
254
|
+
self.set_color_segments()
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
def set_color_segments(self):
|
|
258
|
+
self.set_candle_color_segments()
|
|
259
|
+
self.set_volume_color_segments()
|
|
260
|
+
self._set_ma_color_segments()
|
|
261
|
+
return
|
|
262
|
+
|