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.
Files changed (83) hide show
  1. seolpyo_mplchart/__init__.py +53 -333
  2. seolpyo_mplchart/_chart/__init__.py +145 -0
  3. seolpyo_mplchart/_chart/_base.py +217 -0
  4. seolpyo_mplchart/_chart/_cursor/__init__.py +2 -0
  5. seolpyo_mplchart/_chart/_cursor/_artist.py +217 -0
  6. seolpyo_mplchart/_chart/_cursor/_cursor.py +165 -0
  7. seolpyo_mplchart/_chart/_cursor/_info.py +187 -0
  8. seolpyo_mplchart/_chart/_draw/__init__.py +2 -0
  9. seolpyo_mplchart/_chart/_draw/_artist.py +50 -0
  10. seolpyo_mplchart/_chart/_draw/_data.py +314 -0
  11. seolpyo_mplchart/_chart/_draw/_draw.py +103 -0
  12. seolpyo_mplchart/_chart/_draw/_lim.py +265 -0
  13. seolpyo_mplchart/_chart/_slider/__init__.py +1 -0
  14. seolpyo_mplchart/_chart/_slider/_base.py +268 -0
  15. seolpyo_mplchart/_chart/_slider/_data.py +105 -0
  16. seolpyo_mplchart/_chart/_slider/_mouse.py +176 -0
  17. seolpyo_mplchart/_chart/_slider/_nav.py +204 -0
  18. seolpyo_mplchart/_chart/base/__init__.py +111 -0
  19. seolpyo_mplchart/_chart/base/a_canvas.py +250 -0
  20. seolpyo_mplchart/_chart/base/b_artist.py +143 -0
  21. seolpyo_mplchart/_chart/base/c_draw.py +100 -0
  22. seolpyo_mplchart/_chart/base/d_segment.py +262 -0
  23. seolpyo_mplchart/_chart/base/e_axis.py +267 -0
  24. seolpyo_mplchart/_chart/base/f_background.py +62 -0
  25. seolpyo_mplchart/_chart/base/g_event.py +66 -0
  26. seolpyo_mplchart/_chart/base/h_data.py +138 -0
  27. seolpyo_mplchart/_chart/base/test.py +58 -0
  28. seolpyo_mplchart/_chart/cursor/__init__.py +125 -0
  29. seolpyo_mplchart/_chart/cursor/b_artist.py +130 -0
  30. seolpyo_mplchart/_chart/cursor/c_draw.py +96 -0
  31. seolpyo_mplchart/_chart/cursor/d_segment.py +359 -0
  32. seolpyo_mplchart/_chart/cursor/e_axis.py +65 -0
  33. seolpyo_mplchart/_chart/cursor/g_event.py +233 -0
  34. seolpyo_mplchart/_chart/cursor/h_data.py +61 -0
  35. seolpyo_mplchart/_chart/cursor/test.py +69 -0
  36. seolpyo_mplchart/_chart/slider/__init__.py +169 -0
  37. seolpyo_mplchart/_chart/slider/a_canvas.py +260 -0
  38. seolpyo_mplchart/_chart/slider/b_artist.py +91 -0
  39. seolpyo_mplchart/_chart/slider/c_draw.py +54 -0
  40. seolpyo_mplchart/_chart/slider/d_segment.py +166 -0
  41. seolpyo_mplchart/_chart/slider/e_axis.py +70 -0
  42. seolpyo_mplchart/_chart/slider/f_background.py +37 -0
  43. seolpyo_mplchart/_chart/slider/g_event.py +353 -0
  44. seolpyo_mplchart/_chart/slider/h_data.py +102 -0
  45. seolpyo_mplchart/_chart/slider/test.py +71 -0
  46. seolpyo_mplchart/_chart/test.py +121 -0
  47. seolpyo_mplchart/_config/__init__.py +3 -0
  48. seolpyo_mplchart/_config/ax.py +28 -0
  49. seolpyo_mplchart/_config/candle.py +31 -0
  50. seolpyo_mplchart/_config/config.py +21 -0
  51. seolpyo_mplchart/_config/cursor.py +49 -0
  52. seolpyo_mplchart/_config/figure.py +40 -0
  53. seolpyo_mplchart/_config/format.py +51 -0
  54. seolpyo_mplchart/_config/ma.py +17 -0
  55. seolpyo_mplchart/_config/slider/__init__.py +2 -0
  56. seolpyo_mplchart/_config/slider/config.py +24 -0
  57. seolpyo_mplchart/_config/slider/figure.py +19 -0
  58. seolpyo_mplchart/_config/slider/nav.py +10 -0
  59. seolpyo_mplchart/_config/unit.py +19 -0
  60. seolpyo_mplchart/_config/utils.py +67 -0
  61. seolpyo_mplchart/_config/volume.py +27 -0
  62. seolpyo_mplchart/_cursor.py +27 -25
  63. seolpyo_mplchart/_draw.py +7 -18
  64. seolpyo_mplchart/_slider.py +26 -20
  65. seolpyo_mplchart/_utils/__init__.py +10 -0
  66. seolpyo_mplchart/_utils/nums.py +67 -0
  67. seolpyo_mplchart/_utils/theme/__init__.py +15 -0
  68. seolpyo_mplchart/_utils/theme/dark.py +57 -0
  69. seolpyo_mplchart/_utils/theme/light.py +56 -0
  70. seolpyo_mplchart/_utils/utils.py +28 -0
  71. seolpyo_mplchart/_utils/xl/__init__.py +15 -0
  72. seolpyo_mplchart/_utils/xl/csv.py +46 -0
  73. seolpyo_mplchart/_utils/xl/xlsx.py +49 -0
  74. seolpyo_mplchart/sample/apple.txt +6058 -0
  75. seolpyo_mplchart/sample/samsung.txt +5938 -0
  76. seolpyo_mplchart/test.py +172 -56
  77. seolpyo_mplchart/xl_to_dict.py +47 -0
  78. seolpyo_mplchart-2.1.0.dist-info/METADATA +718 -0
  79. seolpyo_mplchart-2.1.0.dist-info/RECORD +89 -0
  80. {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.dist-info}/WHEEL +1 -1
  81. seolpyo_mplchart-1.4.1.dist-info/METADATA +0 -57
  82. seolpyo_mplchart-1.4.1.dist-info/RECORD +0 -17
  83. {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,138 @@
1
+ from matplotlib.axes import Axes
2
+ import numpy as np
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_price: Axes
17
+ index_list: list[int] = []
18
+
19
+ set_segments: callable
20
+ axis: callable
21
+
22
+ def get_default_xlim(self):
23
+ """
24
+ get_default_xlim.
25
+
26
+ space = int(self.index_list[-1] / 20)
27
+
28
+ Returns:
29
+ (int, int): (-space, self.index_list[-1]+space)
30
+ """
31
+ # print(f'{self.index_list[-1]=}')
32
+ space = int(self.index_list[-1] / 20)
33
+ return (-space, self.index_list[-1]+space)
34
+
35
+ def set_variables(self):
36
+ self.index_list.clear()
37
+ self.index_list = self.df.index.tolist()
38
+ self.xmin, self.xmax = (0, self.index_list[-1])
39
+
40
+ self.chart_price_ymax = round(self.df['high'].max() * 1.3, self.CONFIG.UNIT.digit+2)
41
+ if self.key_volume:
42
+ self.chart_volume_ymax = round(self.df['volume'].max() * 1.3, self.CONFIG.UNIT.digit_volume+2)
43
+ else:
44
+ self.chart_volume_ymax = 10
45
+
46
+ if not self.CONFIG.MA.ma_list:
47
+ self.CONFIG.MA.ma_list = []
48
+ else:
49
+ self.CONFIG.MA.ma_list = sorted(self.CONFIG.MA.ma_list)
50
+
51
+ return
52
+
53
+ def set_data(self, df: pd.DataFrame, *, change_xlim=True):
54
+ """
55
+ `if change_xlim`: change xlim with `self.get_default_xlim()` value
56
+
57
+ `if not change_xlim`: Keep the current xlim
58
+ """
59
+ self.df = self._convert_df(df)
60
+
61
+ self._add_columns()
62
+ # print(f'{self.df.columns=}')
63
+
64
+ self.set_variables()
65
+
66
+ self.set_segments()
67
+
68
+ if change_xlim:
69
+ xmin, xmax = self.get_default_xlim()
70
+ self.axis(xmin, xmax=xmax)
71
+ return
72
+
73
+ def _convert_df(self, df: pd.DataFrame):
74
+ keys = {
75
+ self.key_date: 'date',
76
+ self.key_open: 'open',
77
+ self.key_high: 'high',
78
+ self.key_low: 'low',
79
+ self.key_close: 'close',
80
+ self.key_volume: 'volume',
81
+ }
82
+ df.rename(columns=keys, inplace=True)
83
+
84
+ # df column 추출
85
+ if self.key_volume:
86
+ df = df[['date', 'open', 'high', 'low', 'close', 'volume']].copy()
87
+ else:
88
+ df = df[['date', 'open', 'high', 'low', 'close',]].copy()
89
+ df['volume'] = 0
90
+ df.loc[:, 'ymax_volume'] = df['volume'] * 1.2
91
+
92
+ # 오름차순 정렬
93
+ df = df.sort_values(['date'])
94
+ df = df.reset_index(drop=True)
95
+
96
+ return df
97
+
98
+ def _add_columns(self):
99
+ # 전일 종가 추가
100
+ self.df['pre_close'] = self.df['close'].shift(1).fillna(0)
101
+ # 거래정지인 경우 전일종가 적용
102
+ self.df.loc[self.df['close'] == 0, 'close'] = self.df['pre_close']
103
+ # 종가만 유효한 경우 종가로 통일
104
+ self.df.loc[(self.df['close'] != 0) & (self.df['open'] == 0), ['open', 'high', 'low']] = self.df['close']
105
+
106
+ # 가격이동평균선 계산
107
+ for ma in self.CONFIG.MA.ma_list:
108
+ self.df[f'ma{ma}'] = self.df['close'].rolling(ma).mean()
109
+
110
+ # 세그먼트 생성을 위한 column 추가
111
+ self.df['x'] = self.df.index + 0.5
112
+ self.df['left_candle'] = self.df['x'] - self.CONFIG.CANDLE.half_width
113
+ self.df['right_candle'] = self.df['x'] + self.CONFIG.CANDLE.half_width
114
+ self.df['left_volume'] = self.df['x'] - self.CONFIG.VOLUME.half_width
115
+ self.df['right_volume'] = self.df['x'] + self.CONFIG.VOLUME.half_width
116
+ self.df['zero'] = 0
117
+
118
+ self.df['is_up'] = np.where(self.df['open'] < self.df['close'], True, False)
119
+ self.df['top_candle'] = np.where(self.df['is_up'], self.df['close'], self.df['open'])
120
+ self.df['bottom_candle'] = np.where(self.df['is_up'], self.df['open'], self.df['close'])
121
+
122
+ return
123
+
124
+
125
+ class DataMixin(Base):
126
+ key_date = 'date'
127
+ key_open, key_high, key_low, key_close = ('open', 'high', 'low', 'close')
128
+ key_volume = 'volume'
129
+
130
+ index_list: list[int] = []
131
+
132
+ df: pd.DataFrame
133
+
134
+ chart_price_ymax: float
135
+ chart_volume_ymax: float
136
+ xmin: int
137
+ xmax: int
138
+
@@ -0,0 +1,58 @@
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._chart.base import Chart
16
+
17
+
18
+ path_file = path_pkg / 'sample' / 'samsung.txt'
19
+ with open(path_file, 'r', encoding='utf-8') as txt:
20
+ data = json.load(txt)
21
+ df = pd.DataFrame(data[:])
22
+
23
+
24
+ class C(Chart):
25
+ # limit_wick = 200
26
+ t = 'light'
27
+ # watermark = ''
28
+ def __init__(self):
29
+ super().__init__()
30
+ self.figure.canvas.mpl_connect('button_press_event', lambda x: self.theme(x))
31
+
32
+ def theme(self, e):
33
+ btn = getattr(e, 'button')
34
+ # print(f'{str(btn)=}')
35
+ if str(btn) == '3':
36
+ # print('refresh')
37
+ if self.t == 'light':
38
+ self.t = 'dark'
39
+ else:
40
+ self.t = 'light'
41
+ # print(f'{self.t=}')
42
+ self.CONFIG = set_theme(self.CONFIG, theme=self.t)
43
+ self.refresh()
44
+ return
45
+
46
+
47
+ def run():
48
+ chart = C()
49
+ chart.set_data(df)
50
+ plt.show()
51
+ plt.close()
52
+ return
53
+
54
+
55
+ if __name__ == '__main__':
56
+ run()
57
+
58
+
@@ -0,0 +1,125 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.collections import LineCollection
3
+ from matplotlib.text import Text
4
+ from matplotlib.axes import Axes
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+ from ..base import Chart as BaseChart, Figure, ConfigData
9
+
10
+ from .b_artist import ArtistMixin
11
+ from .c_draw import DrawMixin
12
+ from .d_segment import SegmentMixin
13
+ from .e_axis import AxisMixin
14
+ from .g_event import EventMixin
15
+ from .h_data import DataMixin
16
+
17
+
18
+ class CursorMixin(
19
+ ArtistMixin,
20
+ DrawMixin,
21
+ SegmentMixin,
22
+ AxisMixin,
23
+ EventMixin,
24
+ DataMixin
25
+ ):
26
+ pass
27
+
28
+
29
+ class Chart(CursorMixin, BaseChart):
30
+ limit_candle = 400
31
+ limit_wick = 2_000
32
+ candle_on_ma = True
33
+ fraction = False
34
+
35
+ key_date = 'date'
36
+ key_open, key_high, key_low, key_close = ('open', 'high', 'low', 'close')
37
+ key_volume = 'volume'
38
+
39
+ index_list: list[int] = []
40
+
41
+ df: pd.DataFrame
42
+
43
+ CONFIG: ConfigData
44
+
45
+ figure: Figure
46
+ ax_legend: Axes
47
+ ax_price: Axes
48
+ ax_volume: Axes
49
+
50
+ artist_watermark: Text
51
+ collection_candle: LineCollection
52
+ collection_volume: LineCollection
53
+ collection_ma: LineCollection
54
+
55
+ in_chart = False
56
+ in_chart_price = False
57
+ in_chart_volume = False
58
+
59
+ in_candle = False
60
+ in_volume = False
61
+
62
+ collection_price_crossline: LineCollection
63
+ collection_volume_crossline: LineCollection
64
+
65
+ artist_label_x: Text = None
66
+ artist_label_y: Text = None
67
+
68
+ collection_box_price: LineCollection
69
+ collection_box_volume: LineCollection
70
+
71
+ artist_info_candle: Text
72
+ artist_info_volume: Text
73
+
74
+ ###
75
+
76
+ segment_volume: np.ndarray
77
+ segment_volume_wick: np.ndarray
78
+ facecolor_volume: np.ndarray
79
+ edgecolor_volume: np.ndarray
80
+
81
+ segment_candle: np.ndarray
82
+ segment_candle_wick: np.ndarray
83
+ segment_priceline: np.ndarray
84
+ facecolor_candle: np.ndarray
85
+ edgecolor_candle: np.ndarray
86
+
87
+ segment_ma: np.ndarray
88
+ edgecolor_ma: np.ndarray
89
+
90
+ price_ymin: int
91
+ price_ymax: int
92
+ volume_ymax: int
93
+
94
+ chart_price_ymax: float
95
+ chart_volume_ymax: float
96
+
97
+ vxmin: int
98
+ vxmax: int
99
+
100
+ v0: int
101
+ v1: int
102
+ vmiddle: int
103
+
104
+ min_height_box_candle: float
105
+ min_height_box_volume: float
106
+
107
+ ###
108
+
109
+ _visible_ma: set[int] = set()
110
+ _edgecolor_ma = []
111
+
112
+ _background = None
113
+ _background_background = None
114
+ _creating_background = False
115
+
116
+ _length_text: int
117
+
118
+ _in_mouse_move = False
119
+
120
+ def refresh(self):
121
+ super().refresh()
122
+
123
+ self._set_length_text()
124
+ return
125
+
@@ -0,0 +1,130 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.collections import LineCollection
3
+ from matplotlib.text import Text
4
+
5
+ from ..._config import ConfigData
6
+ from ..base.a_canvas import Figure
7
+
8
+
9
+ class Base:
10
+ CONFIG: ConfigData
11
+
12
+ figure: Figure
13
+ ax_price: Axes
14
+ ax_volume: Axes
15
+
16
+ add_artists: callable
17
+ set_artists: callable
18
+
19
+
20
+ class CrossLineMixin(Base):
21
+ def _add_crosslines(self):
22
+ kwargs = {'segments': [], 'animated': True}
23
+
24
+ self.collection_price_crossline = LineCollection(**kwargs)
25
+ self.ax_price.add_artist(self.collection_price_crossline)
26
+
27
+ self.collection_volume_crossline = LineCollection(**kwargs)
28
+ self.ax_volume.add_artist(self.collection_volume_crossline)
29
+ return
30
+
31
+ def _set_crosslines(self):
32
+ kwargs = self.CONFIG.CURSOR.CROSSLINE.__dict__
33
+ kwargs.update({'segments': [], 'animated': True})
34
+
35
+ self.collection_price_crossline.set(**kwargs)
36
+ self.collection_volume_crossline.set(**kwargs)
37
+ return
38
+
39
+
40
+ class LabelMixin(Base):
41
+ def _add_artist_labels(self):
42
+ kwargs = {'text': '', 'animated': True,}
43
+
44
+ self.artist_label_x = Text(**kwargs)
45
+ self.figure.add_artist(self.artist_label_x)
46
+
47
+ self.artist_label_y = Text(**kwargs)
48
+ self.figure.add_artist(self.artist_label_y)
49
+ return
50
+
51
+ def _set_artist_labels(self):
52
+ kwargs = self.CONFIG.CURSOR.TEXT.to_dict()
53
+ kwargs.update({'text': ' ', 'animated': True, 'horizontalalignment': 'center', 'verticalalignment': 'center', 'clip_on':True})
54
+
55
+ self.artist_label_x.set(**kwargs)
56
+ self.artist_label_y.set(**kwargs)
57
+ return
58
+
59
+
60
+ class BoxMixin(Base):
61
+ def _add_box_collections(self):
62
+ kwargs = {'segments': [], 'animated': True,}
63
+
64
+ self.collection_box_price = LineCollection(**kwargs)
65
+ self.ax_price.add_artist(self.collection_box_price)
66
+ self.collection_box_volume = LineCollection(**kwargs)
67
+ self.ax_volume.add_artist(self.collection_box_volume)
68
+ return
69
+
70
+ def _set_box_collections(self):
71
+ kwargs = self.CONFIG.CURSOR.BOX.__dict__
72
+ kwargs.update({'segments': [], 'animated': True,})
73
+
74
+ self.collection_box_price.set(**kwargs)
75
+ self.collection_box_volume.set(**kwargs)
76
+ return
77
+
78
+
79
+ class InfoMixin(Base):
80
+ def _add_info_texts(self):
81
+ kwargs = {'text': '', 'animated': True, 'horizontalalignment': 'left', 'verticalalignment': 'top',}
82
+
83
+ self.artist_info_candle = Text(**kwargs)
84
+ self.ax_price.add_artist(self.artist_info_candle)
85
+ self.artist_info_volume = Text(**kwargs)
86
+ self.ax_volume.add_artist(self.artist_info_volume)
87
+ return
88
+
89
+ def _set_info_texts(self):
90
+ kwargs = self.CONFIG.CURSOR.TEXT.to_dict()
91
+ kwargs.update({'text': '', 'animated': True, 'horizontalalignment': 'left', 'verticalalignment': 'top',})
92
+
93
+ self.artist_info_candle.set(**kwargs)
94
+ self.artist_info_volume.set(**kwargs)
95
+ return
96
+
97
+
98
+ class ArtistMixin(CrossLineMixin, LabelMixin, BoxMixin, InfoMixin):
99
+ collection_price_crossline: LineCollection
100
+ collection_volume_crossline: LineCollection
101
+
102
+ artist_label_x: Text = None
103
+ artist_label_y: Text = None
104
+
105
+ collection_box_price: LineCollection
106
+ collection_box_volume: LineCollection
107
+
108
+ artist_info_candle: Text
109
+ artist_info_volume: Text
110
+
111
+ def add_artists(self):
112
+ super().add_artists()
113
+
114
+ self._add_crosslines()
115
+ self._add_artist_labels()
116
+ self._add_box_collections()
117
+ self._add_info_texts()
118
+
119
+ self.set_artists()
120
+ return
121
+
122
+ def set_artists(self):
123
+ super().set_artists()
124
+
125
+ self._set_crosslines()
126
+ self._set_artist_labels()
127
+ self._set_box_collections()
128
+ self._set_info_texts()
129
+ return
130
+
@@ -0,0 +1,96 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.collections import LineCollection
3
+ from matplotlib.text import Text
4
+ import pandas as pd
5
+
6
+ from ..._config import ConfigData
7
+ from ..._chart.base.a_canvas import Figure
8
+
9
+
10
+ class Base:
11
+ CONFIG: ConfigData
12
+
13
+ df: pd.DataFrame
14
+
15
+ watermark: str
16
+
17
+ figure: Figure
18
+ ax_legend: Axes
19
+ ax_price: Axes
20
+ ax_volume: Axes
21
+ artist_watermark: Text
22
+ collection_candle: LineCollection
23
+ collection_volume: LineCollection
24
+ collection_ma: LineCollection
25
+
26
+ collection_price_crossline: LineCollection
27
+ collection_volume_crossline: LineCollection
28
+
29
+ artist_label_x: Text
30
+ artist_label_y: Text
31
+
32
+ collection_box_price: LineCollection
33
+ collection_box_volume: LineCollection
34
+
35
+ artist_info_candle: Text
36
+ artist_info_volume: Text
37
+
38
+ in_chart_price: bool
39
+ in_chart_volume: bool
40
+
41
+
42
+ class CrosslineMixin(Base):
43
+ def _draw_crossline(self):
44
+ renderer = self.figure.canvas.renderer
45
+ for artist in [
46
+ self.collection_price_crossline,
47
+ self.collection_volume_crossline,
48
+ ]:
49
+ artist.draw(renderer)
50
+ return
51
+
52
+
53
+ class LabelMixin(Base):
54
+ def _draw_label_x(self):
55
+ artist = self.artist_label_x
56
+ renderer = self.figure.canvas.renderer
57
+
58
+ artist.draw(renderer)
59
+ # print(f'{artist.get_position()=}')
60
+ return
61
+
62
+ def _draw_label_y(self):
63
+ artist = self.artist_label_y
64
+ renderer = self.figure.canvas.renderer
65
+
66
+ artist.draw(renderer)
67
+ return
68
+
69
+
70
+ class BoxMixin(Base):
71
+ def _draw_box_candle(self):
72
+ renderer = self.figure.canvas.renderer
73
+ self.collection_box_price.draw(renderer)
74
+ return
75
+
76
+ def _draw_box_volume(self):
77
+ renderer = self.figure.canvas.renderer
78
+ self.collection_box_volume.draw(renderer)
79
+ return
80
+
81
+
82
+ class InfoMixin(Base):
83
+ def _draw_info_candle(self):
84
+ renderer = self.figure.canvas.renderer
85
+ self.artist_info_candle.draw(renderer)
86
+ return
87
+
88
+ def _draw_info_volume(self):
89
+ renderer = self.figure.canvas.renderer
90
+ self.artist_info_volume.draw(renderer)
91
+ return
92
+
93
+
94
+ class DrawMixin(CrosslineMixin, LabelMixin, BoxMixin, InfoMixin):
95
+ pass
96
+