1+ from datetime import datetime , timezone
2+ import pytz
3+
4+ import pandas as pd
5+ import numpy as np
6+ import matplotlib .pyplot as plt
7+ import pyqtgraph as pg
8+ import talib as ta
9+
10+ import finplot_lib as fplt
11+
12+ from data_pipeline .market_profile_reader import MarketProfileReader
13+
14+ '''
15+ Plotting of orderflow chart
16+ - Candlestick
17+ - Orderflow data by price level
18+ - Volume bar
19+ - Classic MACD
20+ - CVD
21+ - StochRSI
22+
23+ Pyqtgraph ref:
24+ https://pyqtgraph.readthedocs.io/en/latest/index.html
25+ https://doc.qt.io/qtforpython-5/contents.html
26+ '''
27+
28+ if __name__ == '__main__' :
29+ inst = 'btcusdt'
30+ # input in HKT
31+ start = datetime (2021 , 12 , 10 , 0 , 0 , 0 )
32+ end = datetime (2021 , 12 , 10 , 6 , 0 , 0 )
33+
34+ profile = MarketProfileReader ()
35+ profile .load_data_from_influx (inst = inst , start = start , end = end , env = 'local' )
36+
37+ # slice_dt = pytz.timezone('Asia/Hong_Kong').localize(datetime(2022,3,17,17,23,0)) # input in HKT
38+ slice_start = pytz .timezone ('Asia/Hong_Kong' ).localize (start ) # input in HKT
39+ slice_end = pytz .timezone ('Asia/Hong_Kong' ).localize (end ) # input in HKT
40+
41+ # mp_slice = profile[slice_dt]
42+ mp_slice = profile [slice_start :slice_end ]
43+
44+ ohlcv = pd .DataFrame (
45+ {
46+ 'o' : [mp .open for mp in mp_slice ],
47+ 'h' : [mp .high for mp in mp_slice ],
48+ 'l' : [mp .low for mp in mp_slice ],
49+ 'c' : [mp .close for mp in mp_slice ],
50+ 'v' : [mp .volume_qty for mp in mp_slice ],
51+ 'd' : [mp .delta_qty for mp in mp_slice ],
52+ },
53+ index = [mp .timepoint for mp in mp_slice ]
54+ )
55+
56+ ema_n = [20 , 50 , 200 ]
57+ ema_colors = ['#33DCCD50' , '#ADE03670' , '#F4D03F80' ]
58+ for n in ema_n :
59+ ohlcv [f'ema{ n } ' ] = ta .EMA (ohlcv ['c' ], timeperiod = n )
60+
61+ macd = [12 , 26 , 9 ]
62+ ohlcv ['macd' ], ohlcv ['macd_signal' ], ohlcv ['macd_diff' ] = ta .MACD (ohlcv ['c' ], fastperiod = macd [0 ], slowperiod = macd [1 ], signalperiod = macd [2 ])
63+
64+ stoch_rsi = [21 , 14 , 14 ]
65+ ohlcv ['fastk' ], ohlcv ['fastd' ] = ta .STOCHRSI (ohlcv ['c' ], timeperiod = stoch_rsi [0 ], fastk_period = stoch_rsi [1 ], fastd_period = stoch_rsi [2 ], fastd_matype = 0 )
66+
67+ print ('Drawing plot...' )
68+
69+ # plotting
70+ fplt .foreground = '#D6DBDF'
71+ fplt .background = '#151E26'
72+ fplt .legend_border_color = '#ffffff30' # make transparent
73+ fplt .legend_fill_color = '#ffffff10' # make transparent
74+ fplt .legend_text_color = fplt .foreground
75+ fplt .top_graph_scale = 2 # top graph and bottom graph has ratio of r:1
76+ fplt .winx , fplt .winy , fplt .winw , fplt .winh = 0 ,0 ,1600 ,1600
77+ ax , ax3 , ax2 , ax4 = fplt .create_plot (
78+ title = 'Chart' ,
79+ rows = 4 , # main candlestick = ax / MACD = ax3 / CVD = ax2 / StochRSI = ax4
80+ maximize = False ,
81+ init_zoom_periods = 18 ,
82+ row_stretch_factors = [3 , 1 , 1 , 1 ]
83+ )
84+
85+ # placeholder for tick info; updated with fplt.set_time_inspector(func)
86+ hover_label = fplt .add_legend ('' , ax = ax )
87+
88+ # set max zoom: for orderflow data, allow more zoom (default was 20)
89+ fplt .max_zoom_points = 20
90+
91+ # add candlestick
92+ # this is the version of candlestick without orderflow data
93+ candlestick_plot = fplt .candlestick_ochl (datasrc = ohlcv [['o' , 'c' , 'h' , 'l' ]], candle_width = 0.7 , ax = ax )
94+
95+ # add volume
96+ volume_plot = fplt .volume_ocv (ohlcv [['o' , 'c' , 'v' ]], candle_width = 0.7 , ax = ax .overlay ())
97+
98+ # plot EMAs
99+ for n , color in zip (ema_n , ema_colors ):
100+ fplt .plot (ohlcv [f'ema{ n } ' ], ax = ax .overlay (), legend = f'EMA { n } ' , color = color )
101+
102+ # plot stoch RSI
103+ stoch_rsi_plot = fplt .plot (ohlcv ['fastk' ], ax = ax4 , legend = f'StochRSI Fast k: { stoch_rsi [1 ]} Timeperiod: { stoch_rsi [0 ]} ' )
104+ fplt .plot (ohlcv ['fastd' ], ax = ax4 , legend = f'StochRSI Fast d: { stoch_rsi [2 ]} Timeperiod: { stoch_rsi [0 ]} ' )
105+
106+ thresholds = [20 , 80 ]
107+ for th in thresholds :
108+ rsi_threshold_line = pg .InfiniteLine (pos = th , angle = 0 , pen = fplt ._makepen (color = '#ffffff50' , style = '- - ' ))
109+ ax4 .addItem (rsi_threshold_line , ignoreBounds = True )
110+ fplt .add_band (* thresholds , color = '#2980B920' , ax = ax4 )
111+
112+ vb = stoch_rsi_plot .getViewBox ()
113+ vb .setBackgroundColor ('#00000000' )
114+
115+ # plot MACD
116+ macd_plot = fplt .volume_ocv (ohlcv [['o' , 'c' , 'macd_diff' ]], ax = ax3 , candle_width = 0.7 , colorfunc = fplt .strength_colorfilter )
117+ fplt .plot (ohlcv ['macd' ], ax = ax3 , legend = f'MACD ({ macd [0 ]} , { macd [1 ]} , { macd [2 ]} )' )
118+ fplt .plot (ohlcv ['macd_signal' ], ax = ax3 , legend = 'Signal' )
119+
120+ vb = macd_plot .getViewBox ()
121+ vb .setBackgroundColor ('#00000000' )
122+
123+ '''
124+ Ref: examples/snp500.py
125+ '''
126+ # plot cvd
127+ line_color = '#F4D03F'
128+ cvd_plot = fplt .plot (np .cumsum (ohlcv ['d' ]), ax = ax2 , legend = 'CVD' , color = line_color , fillLevel = 0 , brush = line_color + '10' )
129+ # and set background
130+ vb = cvd_plot .getViewBox ()
131+ vb .setBackgroundColor ('#00000000' )
132+
133+ '''
134+ Ref: examples/complicated.py
135+ '''
136+ # set bull body to same color as bull frame; otherwise it is default background color (transparent)
137+ bull = '#1ABC9C'
138+ bear = '#E74C3C'
139+ fplt .candle_bull_color = bull
140+ fplt .candle_bull_body_color = bull
141+ fplt .candle_bear_color = bear
142+ candlestick_plot .colors .update ({
143+ 'bull_body' : fplt .candle_bull_color
144+ })
145+
146+ transparency = '45'
147+ volume_plot .colors .update ({
148+ 'bull_frame' : fplt .candle_bull_color + transparency ,
149+ 'bull_body' : fplt .candle_bull_body_color + transparency ,
150+ 'bear_frame' : fplt .candle_bear_color + transparency ,
151+ 'bear_body' : fplt .candle_bear_color + transparency ,
152+ })
153+
154+ # set gridlines
155+ ax .showGrid (x = True , y = True , alpha = 0.2 )
156+ ax2 .showGrid (x = True , y = True , alpha = 0.2 )
157+ ax3 .showGrid (x = True , y = True , alpha = 0.2 )
158+ ax4 .showGrid (x = True , y = True , alpha = 0.2 )
159+
160+ # add YAxis item at the right
161+ # ax.axes['right'] = {'item': fplt.YAxisItem(vb=ax.vb, orientation='right')}
162+ # ax2.axes['right'] = {'item': fplt.YAxisItem(vb=ax2.vb, orientation='right')}
163+
164+ # add legend of ohlcv data
165+ '''
166+ Ref: examples/snp500.py
167+ '''
168+ def update_legend_text (x , y ):
169+ dt = datetime .fromtimestamp (x // 1000000000 )
170+ utcdt = dt .astimezone (pytz .utc )
171+ # dt = dt.replace(tzinfo=timezone.utc)
172+ row = ohlcv .loc [utcdt ]
173+ # format html with the candle and set legend
174+ fmt = '<span style="color:%s; margin: 16px;">%%s</span>' % (bull if (row ['o' ] < row ['c' ]).all () else bear )
175+ rawtxt = '<span style="font-size:14px">%%s %%s</span> O: %s H: %s L: %s C: %s Delta: %s' % (fmt , fmt , fmt , fmt , fmt )
176+ hover_label .setText (rawtxt % ('TOKEN' , 'INTERVAL' , row ['o' ], row ['h' ], row ['l' ], row ['c' ], row ['d' ]))
177+ fplt .set_time_inspector (update_legend_text , ax = ax , when = 'hover' )
178+
179+ # additional crosshair info
180+ def enrich_info (x , y , xtext , ytext ):
181+ o = ohlcv .iloc [x ]['o' ]
182+ h = ohlcv .iloc [x ]['h' ]
183+ l = ohlcv .iloc [x ]['l' ]
184+ c = ohlcv .iloc [x ]['c' ]
185+ add_xt = f'\t { xtext } '
186+ add_yt = f'\t Level: { ytext } \n \n \t Open: { o } \n \t High: { h } \n \t Low: { l } \n \t Close: { c } '
187+ return add_xt , add_yt
188+
189+ fplt .add_crosshair_info (enrich_info , ax = ax )
190+
191+ '''
192+ Ref: examples/complicated.py
193+ '''
194+ # set dark themes ====================
195+ pg .setConfigOptions (foreground = fplt .foreground , background = fplt .background )
196+
197+ # window background
198+ for win in fplt .windows :
199+ win .setBackground (fplt .background )
200+
201+ # axis, crosshair, candlesticks, volumes
202+ axs = [ax for win in fplt .windows for ax in win .axs ]
203+ vbs = set ([ax .vb for ax in axs ])
204+ axs += fplt .overlay_axs
205+ axis_pen = fplt ._makepen (color = fplt .foreground )
206+ for ax in axs :
207+ ax .axes ['left' ]['item' ].setPen (axis_pen )
208+ ax .axes ['left' ]['item' ].setTextPen (axis_pen )
209+ ax .axes ['bottom' ]['item' ].setPen (axis_pen )
210+ ax .axes ['bottom' ]['item' ].setTextPen (axis_pen )
211+ if ax .crosshair is not None :
212+ ax .crosshair .vline .pen .setColor (pg .mkColor (fplt .foreground ))
213+ ax .crosshair .hline .pen .setColor (pg .mkColor (fplt .foreground ))
214+ ax .crosshair .xtext .setColor (fplt .foreground )
215+ ax .crosshair .ytext .setColor (fplt .foreground )
216+ # ====================================
217+
218+ fplt .show ()
0 commit comments