seolpyo-mplchart 1.4.1__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 +53 -333
- seolpyo_mplchart/_chart/__init__.py +145 -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/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/_chart/test.py +121 -0
- seolpyo_mplchart/_config/__init__.py +3 -0
- seolpyo_mplchart/_config/ax.py +28 -0
- seolpyo_mplchart/_config/candle.py +31 -0
- seolpyo_mplchart/_config/config.py +21 -0
- seolpyo_mplchart/_config/cursor.py +49 -0
- seolpyo_mplchart/_config/figure.py +40 -0
- seolpyo_mplchart/_config/format.py +51 -0
- seolpyo_mplchart/_config/ma.py +17 -0
- seolpyo_mplchart/_config/slider/__init__.py +2 -0
- seolpyo_mplchart/_config/slider/config.py +24 -0
- seolpyo_mplchart/_config/slider/figure.py +19 -0
- seolpyo_mplchart/_config/slider/nav.py +10 -0
- seolpyo_mplchart/_config/unit.py +19 -0
- seolpyo_mplchart/_config/utils.py +67 -0
- seolpyo_mplchart/_config/volume.py +27 -0
- seolpyo_mplchart/_cursor.py +27 -25
- seolpyo_mplchart/_draw.py +7 -18
- seolpyo_mplchart/_slider.py +26 -20
- 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 +172 -56
- seolpyo_mplchart/xl_to_dict.py +47 -0
- seolpyo_mplchart-2.1.0.dist-info/METADATA +718 -0
- seolpyo_mplchart-2.1.0.dist-info/RECORD +89 -0
- {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.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.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from matplotlib.axes import Axes
|
|
2
|
+
from matplotlib.text import Text
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from ..._config import ConfigData
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Base:
|
|
9
|
+
CONFIG: ConfigData
|
|
10
|
+
df: pd.DataFrame
|
|
11
|
+
|
|
12
|
+
key_date = 'date'
|
|
13
|
+
key_open, key_high, key_low, key_close = ('open', 'high', 'low', 'close')
|
|
14
|
+
key_volume = 'volume'
|
|
15
|
+
|
|
16
|
+
ax_slider: Axes
|
|
17
|
+
|
|
18
|
+
index_list: list[int] = []
|
|
19
|
+
|
|
20
|
+
artist_text_slider: Text
|
|
21
|
+
|
|
22
|
+
axis: callable
|
|
23
|
+
get_default_xlim: callable
|
|
24
|
+
set_variables: callable
|
|
25
|
+
|
|
26
|
+
_set_slider_collection: callable
|
|
27
|
+
vxmin: int
|
|
28
|
+
vxmax: int
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SliderMixin(Base):
|
|
32
|
+
min_distance = 5
|
|
33
|
+
|
|
34
|
+
def get_default_xlim(self):
|
|
35
|
+
xmax = self.index_list[-1] + 1
|
|
36
|
+
xmin = xmax - 120
|
|
37
|
+
if xmin < 0:
|
|
38
|
+
xmin = 0
|
|
39
|
+
return (xmin, xmax)
|
|
40
|
+
|
|
41
|
+
def set_variables(self):
|
|
42
|
+
super().set_variables()
|
|
43
|
+
|
|
44
|
+
self._set_slider()
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
def _set_slider(self):
|
|
48
|
+
# print('_set_slider')
|
|
49
|
+
self._set_slider_xtick()
|
|
50
|
+
|
|
51
|
+
xmax = self.index_list[-1]
|
|
52
|
+
# 슬라이더 xlim
|
|
53
|
+
xdistance = round(xmax / 30)
|
|
54
|
+
self.slider_xmin, self.slider_xmax = (-xdistance, xmax+xdistance)
|
|
55
|
+
self.ax_slider.set_xlim(self.slider_xmin, self.slider_xmax)
|
|
56
|
+
|
|
57
|
+
# 네비게이터 경계선 두께
|
|
58
|
+
self._nav_width = round((self.slider_xmax - self.slider_xmin) / 250, 2)
|
|
59
|
+
|
|
60
|
+
# 슬라이더 ylim
|
|
61
|
+
ymin, ymax = (self.df['low'].min(), self.df['high'].max())
|
|
62
|
+
ysub = ymax - ymin
|
|
63
|
+
ydistance = round(ysub / 5, self.CONFIG.UNIT.digit+2)
|
|
64
|
+
self.slider_ymin, self.slider_ymax = (ymin-ydistance, ymax+ydistance)
|
|
65
|
+
self.ax_slider.set_ylim(self.slider_ymin, self.slider_ymax)
|
|
66
|
+
|
|
67
|
+
self._set_slider_collection()
|
|
68
|
+
self._set_slider_text_position()
|
|
69
|
+
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
def _set_slider_text_position(self):
|
|
73
|
+
# 슬라이더 텍스트 y
|
|
74
|
+
self.artist_text_slider.set_y(self.df['high'].max())
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
def _set_slider_xtick(self):
|
|
78
|
+
indices = [0, self.index_list[-1]]
|
|
79
|
+
# print(f'{indices=}')
|
|
80
|
+
|
|
81
|
+
date_list = [self.df.iloc[idx]['date'] for idx in indices]
|
|
82
|
+
# print(f'{date_list=}')
|
|
83
|
+
# xtick 설정, major tick과 겹쳐서 무시되는 것 방지
|
|
84
|
+
self.ax_slider.set_xticks([idx+0.01 for idx in indices], labels=date_list, minor=True)
|
|
85
|
+
|
|
86
|
+
labels = self.ax_slider.get_xticklabels(minor=True)
|
|
87
|
+
# print(f'{labels=}')
|
|
88
|
+
for label, align in zip(labels, ['center', 'center']):
|
|
89
|
+
# 라벨 텍스트 정렬
|
|
90
|
+
label.set_horizontalalignment(align)
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class DataMixin(SliderMixin):
|
|
95
|
+
min_distance = 5
|
|
96
|
+
_nav_width: float
|
|
97
|
+
|
|
98
|
+
slider_xmin: int
|
|
99
|
+
slider_xmax: int
|
|
100
|
+
slider_ymin: float
|
|
101
|
+
slider_ymax: float
|
|
102
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
from seolpyo_mplchart._utils.theme import set_theme
|
|
15
|
+
from seolpyo_mplchart._config import SLIDERCONFIG
|
|
16
|
+
from seolpyo_mplchart._chart.slider import Chart
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
path_file = path_pkg / 'sample' / 'samsung.txt'
|
|
20
|
+
with open(path_file, 'r', encoding='utf-8') as txt:
|
|
21
|
+
data = json.load(txt)
|
|
22
|
+
df = pd.DataFrame(data[:800])
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class C(Chart):
|
|
26
|
+
limit_candle = 100
|
|
27
|
+
limit_wick = 300
|
|
28
|
+
limit_volume = 10
|
|
29
|
+
limit_ma = 200
|
|
30
|
+
t = 'light'
|
|
31
|
+
# watermark = ''
|
|
32
|
+
def __init__(self):
|
|
33
|
+
super().__init__()
|
|
34
|
+
# super().__init__(config=set_theme(SLIDERCONFIG, theme='dark'))
|
|
35
|
+
self.figure.canvas.mpl_connect('button_press_event', lambda x: self.theme(x))
|
|
36
|
+
|
|
37
|
+
def theme(self, e):
|
|
38
|
+
btn = getattr(e, 'button')
|
|
39
|
+
# print(f'{str(btn)=}')
|
|
40
|
+
if str(btn) == '3':
|
|
41
|
+
# print('refresh')
|
|
42
|
+
if self.t == 'light':
|
|
43
|
+
self.slider_top = True
|
|
44
|
+
self.t = 'dark'
|
|
45
|
+
# self.CONFIG.MA.linewidth = 1
|
|
46
|
+
self.get_candle_segment = lambda **x: Chart.get_candle_segment(self, **x)
|
|
47
|
+
self.CONFIG.CANDLE.linewidth = 0.8
|
|
48
|
+
else:
|
|
49
|
+
self.slider_top = False
|
|
50
|
+
self.t = 'light'
|
|
51
|
+
# self.CONFIG.MA.linewidth = 3
|
|
52
|
+
self.get_candle_segment = self.get_bar_segment
|
|
53
|
+
self.CONFIG.CANDLE.linewidth = 1.3
|
|
54
|
+
# print(f'{self.t=}')
|
|
55
|
+
self.CONFIG = set_theme(self.CONFIG, theme=self.t)
|
|
56
|
+
self.refresh()
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def run():
|
|
61
|
+
chart = C()
|
|
62
|
+
chart.set_data(df)
|
|
63
|
+
plt.show()
|
|
64
|
+
plt.close()
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == '__main__':
|
|
69
|
+
run()
|
|
70
|
+
|
|
71
|
+
|
|
@@ -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,31 @@
|
|
|
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.linewidth = 0.8
|
|
26
|
+
self.line_color: str|tuple[float, float, float, float] = 'k'
|
|
27
|
+
self.FACECOLOR = CANDLEFACECOLOR
|
|
28
|
+
self.EDGECOLOR = CANDLEEDGECOLOR
|
|
29
|
+
|
|
30
|
+
CANDLE = CandleData()
|
|
31
|
+
|
|
@@ -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,40 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class RatioData:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self.price = 5
|
|
6
|
+
self.volume = 5
|
|
7
|
+
|
|
8
|
+
RATIO = RatioData()
|
|
9
|
+
|
|
10
|
+
class WatermarkData:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.alpha = 0.2
|
|
13
|
+
self.color = 'k'
|
|
14
|
+
self.fontsize = 20
|
|
15
|
+
|
|
16
|
+
WATERMARK = WatermarkData()
|
|
17
|
+
|
|
18
|
+
class AdjustData:
|
|
19
|
+
def __init__(self):
|
|
20
|
+
# 여백
|
|
21
|
+
self.top = 0.98
|
|
22
|
+
self.bottom = 0.05
|
|
23
|
+
self.left = 0.01
|
|
24
|
+
self.right = 0.93
|
|
25
|
+
# 플롯간 간격
|
|
26
|
+
self.wspace = 0
|
|
27
|
+
self.hspace = 0
|
|
28
|
+
|
|
29
|
+
ADJUST = AdjustData()
|
|
30
|
+
|
|
31
|
+
class FigureData:
|
|
32
|
+
def __init__(self):
|
|
33
|
+
self.facecolor: str|tuple[float, float, float, float] = '#fafafa'
|
|
34
|
+
self.figsize = (14, 7)
|
|
35
|
+
self.RATIO = RATIO
|
|
36
|
+
self.ADJUST = ADJUST
|
|
37
|
+
self.WATERMARK = WATERMARK
|
|
38
|
+
|
|
39
|
+
FIGURE = FigureData()
|
|
40
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
format_candleinfo_ko = """\
|
|
4
|
+
{dt}
|
|
5
|
+
|
|
6
|
+
종가: {close}
|
|
7
|
+
등락률: {rate}
|
|
8
|
+
대비: {compare}
|
|
9
|
+
시가: {open}({rate_open})
|
|
10
|
+
고가: {high}({rate_high})
|
|
11
|
+
저가: {low}({rate_low})
|
|
12
|
+
거래량: {volume}({rate_volume})\
|
|
13
|
+
"""
|
|
14
|
+
format_volumeinfo_ko = """\
|
|
15
|
+
{dt}
|
|
16
|
+
|
|
17
|
+
거래량: {volume}
|
|
18
|
+
거래량증가율: {rate_volume}
|
|
19
|
+
대비: {compare}\
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
class FormatData:
|
|
23
|
+
def __init__(self):
|
|
24
|
+
self.candle = format_candleinfo_ko
|
|
25
|
+
self.volume = format_volumeinfo_ko
|
|
26
|
+
|
|
27
|
+
FORMAT = FormatData()
|
|
28
|
+
|
|
29
|
+
format_candleinfo_en = """\
|
|
30
|
+
{dt}
|
|
31
|
+
|
|
32
|
+
close: {close}
|
|
33
|
+
rate: {rate}
|
|
34
|
+
compare: {compare}
|
|
35
|
+
open: {open}({rate_open})
|
|
36
|
+
high: {high}({rate_high})
|
|
37
|
+
low: {low}({rate_low})
|
|
38
|
+
volume: {volume}({rate_volume})\
|
|
39
|
+
"""
|
|
40
|
+
format_volumeinfo_en = """\
|
|
41
|
+
{dt}
|
|
42
|
+
|
|
43
|
+
volume: {volume}
|
|
44
|
+
volume rate: {rate_volume}
|
|
45
|
+
compare: {compare}\
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
FORMAT_EN = FormatData()
|
|
49
|
+
FORMAT_EN.candle = format_candleinfo_en
|
|
50
|
+
FORMAT_EN.volume = format_volumeinfo_en
|
|
51
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class MaData:
|
|
4
|
+
"https://matplotlib.org/stable/gallery/color/named_colors.html"
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.color_default: str|tuple[float, float, float, float] = 'k'
|
|
7
|
+
self.format = '{}일선'
|
|
8
|
+
self.linewidth = 1
|
|
9
|
+
self.ncol = 10
|
|
10
|
+
self.color_list: list[str|tuple[float, float, float, float]] = ['#8B00FF', '#008000', '#A0522D', '#008B8B', '#FF0080']
|
|
11
|
+
self.ma_list = (5, 20, 60, 120, 240)
|
|
12
|
+
|
|
13
|
+
MA = MaData()
|
|
14
|
+
|
|
15
|
+
MA_EN = MaData()
|
|
16
|
+
MA_EN.format = 'ma{}'
|
|
17
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .. import config
|
|
2
|
+
from .figure import FIGURE, SliderFigureData
|
|
3
|
+
from .nav import NAV
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SliderData:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.NAV = NAV
|
|
9
|
+
|
|
10
|
+
SLIDER = SliderData()
|
|
11
|
+
|
|
12
|
+
class SliderConfigData(config.ConfigData):
|
|
13
|
+
FIGURE: SliderFigureData
|
|
14
|
+
SLIDER: SliderData
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
SLIDERCONFIG: SliderConfigData = config.DEFAULTCONFIG
|
|
18
|
+
SLIDERCONFIG.FIGURE = FIGURE
|
|
19
|
+
SLIDERCONFIG.SLIDER = SLIDER
|
|
20
|
+
|
|
21
|
+
SLIDERCONFIG_EN: SliderConfigData = config.DEFAULTCONFIG_EN
|
|
22
|
+
SLIDERCONFIG_EN.FIGURE = FIGURE
|
|
23
|
+
SLIDERCONFIG_EN.SLIDER = SLIDER
|
|
24
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .. import figure
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RatioData:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.price = 9
|
|
7
|
+
self.volume = 3
|
|
8
|
+
self.none = 2
|
|
9
|
+
self.slider = 1.5
|
|
10
|
+
|
|
11
|
+
RATIO = RatioData()
|
|
12
|
+
|
|
13
|
+
class SliderFigureData(figure.FigureData):
|
|
14
|
+
def __init__(self):
|
|
15
|
+
super().__init__()
|
|
16
|
+
self.RATIO: RatioData = RATIO
|
|
17
|
+
|
|
18
|
+
FIGURE = SliderFigureData()
|
|
19
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .utils import convert_unit, convert_unit_en
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class UnitData:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.price = '원'
|
|
7
|
+
self.volume = '주'
|
|
8
|
+
self.digit = 0
|
|
9
|
+
self.digit_volume = 0
|
|
10
|
+
self.func = convert_unit
|
|
11
|
+
|
|
12
|
+
UNIT = UnitData()
|
|
13
|
+
|
|
14
|
+
UNIT_EN = UnitData()
|
|
15
|
+
UNIT_EN.price = ' $'
|
|
16
|
+
UNIT_EN.volume = ' Vol'
|
|
17
|
+
UNIT_EN.digit = 2
|
|
18
|
+
UNIT_EN.func = convert_unit_en
|
|
19
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
def convert_num(num):
|
|
4
|
+
if isinstance(num, float) and num % 1:
|
|
5
|
+
return num
|
|
6
|
+
return int(num)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def float_to_str(num: float, *, digit=0, plus=False):
|
|
10
|
+
if 0 < digit:
|
|
11
|
+
num.__round__(digit)
|
|
12
|
+
text = f'{num:+,.{digit}f}' if plus else f'{num:,.{digit}f}'
|
|
13
|
+
else:
|
|
14
|
+
num = round(num, digit).__int__()
|
|
15
|
+
text = f'{num:+,}' if plus else f'{num:,}'
|
|
16
|
+
return text
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
unit_ko = {
|
|
20
|
+
'경': 10_000_000_000_000_000,
|
|
21
|
+
'조': 1_000_000_000_000,
|
|
22
|
+
'억': 100_000_000,
|
|
23
|
+
'만': 10_000,
|
|
24
|
+
}
|
|
25
|
+
def convert_unit(value, *, digit=0, word='원', unit_data: dict[str, int]=None):
|
|
26
|
+
if not unit_data:
|
|
27
|
+
unit_data = unit_ko
|
|
28
|
+
# print('ko')
|
|
29
|
+
# print(f'{value=:,}')
|
|
30
|
+
v = abs(value)
|
|
31
|
+
for unit, n in unit_data.items():
|
|
32
|
+
if n <= v:
|
|
33
|
+
# print(f'{n=:,}')
|
|
34
|
+
# print(f'{unit=}')
|
|
35
|
+
num = value / n
|
|
36
|
+
if word.startswith(' '):
|
|
37
|
+
return f'{float_to_str(num, digit=digit)}{unit}{word}'
|
|
38
|
+
return f'{float_to_str(num, digit=digit)}{unit} {word}'
|
|
39
|
+
|
|
40
|
+
if not value % 1:
|
|
41
|
+
value = int(value)
|
|
42
|
+
text = f'{float_to_str(value, digit=digit)}{word}'
|
|
43
|
+
# print(f'{text=}')
|
|
44
|
+
return text
|
|
45
|
+
|
|
46
|
+
unit_en = {
|
|
47
|
+
'Qd': 1_000_000_000_000_000,
|
|
48
|
+
'T': 1_000_000_000_000,
|
|
49
|
+
'B': 1_000_000_000,
|
|
50
|
+
'M': 1_000_000,
|
|
51
|
+
'K': 1_000,
|
|
52
|
+
}
|
|
53
|
+
def convert_unit_en(value, *, digit=0, word='$', unit_data: dict[str, int]=None):
|
|
54
|
+
if not unit_data:
|
|
55
|
+
unit_data = unit_en
|
|
56
|
+
# print('en')
|
|
57
|
+
# print(f'{value=:,}')
|
|
58
|
+
return convert_unit(value, digit=digit, word=word, unit_data=unit_data)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == '__main__':
|
|
62
|
+
a = 456.123
|
|
63
|
+
print(float_to_str(a))
|
|
64
|
+
print(float_to_str(a, 2))
|
|
65
|
+
print(float_to_str(a, 6))
|
|
66
|
+
|
|
67
|
+
|