├── .gitignore ├── README.md ├── backend.py ├── config └── volume_data_recorder_config.yaml ├── docs ├── demo_01.png ├── demo_02.png └── demo_03.png ├── examples ├── 01_simple_ohlcv.py ├── 02_candlestick_panels.py ├── 03_static_orderflow_plot.py └── data │ ├── orderflow_data.csv │ ├── sample_data.csv │ └── sample_data_2.csv ├── finplotter ├── __init__.py ├── finplot_library │ ├── __init__.py │ ├── live.py │ └── pdplot.py └── finplotter.py ├── market_profile.py ├── orderflow_plotter.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stack-orderflow 2 | 3 | > **Some words:** 4 | > This repo started off as a project for my own needs - there were little to no open source tools about orderflow charting and visualization given how specific it is. 5 | > I am losing motivation on expanding/optimizing this project because coding up desktop GUI using PyQt-related tools feels crazy, especially when comopared to the development experience in JS-ecosystem - I don't really love JavaScript but given how available the community is, it is a lot easier to code up things there. Plus, desktop GUIs feel really old. At the time of writing, I feel more excited to code up webapps/desktop apps with something else. Just not graphic dev with Python. Nevertheless, I will try to keep developing on this project from time to time 😗. 6 | 7 | Orderflow chart desktop GUI using [Finplot](https://github.com/highfestiva/finplot) and [PyQtGraph](https://github.com/pyqtgraph/pyqtgraph). 8 | 9 | **Enviornment:** 10 | 11 | - Tested with Python3.9+ 12 | - Updated to PyQt6 13 | 14 | ## Plotting 15 | 16 | ### Dependence on `@highfestiva/finplot` library 17 | 18 | The plotting is largely based on the [original Finplot library](https://github.com/highfestiva/finplot), with slight modification in order to allow for orderflow plotting. The whole package source code of his repo is included in this repo (`./finplotter/finplot_library`) because this repo modifies the original package in a hacky way. In the future, this project will be refactored in a way that the inclusion of Finplot package would be unnecessary. Nevertheless in the mean time, it is advised to look into the package itself for further customization. 19 | 20 | ## Examples 21 | 22 | - You may find these examples available in `./examples`. 23 | 24 | ### Example 01: Simple candlestick plot 25 | ![Example 1](docs/demo_01.png "Example 1") 26 | 27 | ### Example 02: Simple candlestick plot with technical indicator panels 28 | ![Example 2](docs/demo_02.png "Example 2") 29 | 30 | ### Example 03: Orderflow plot with static data 31 | ![Example 3](docs/demo_03.png "Example 3") 32 | 33 | ## To-do 34 | ### Example 04: Orderflow plot with real time data streaming 35 | -------------------------------------------------------------------------------- /backend.py: -------------------------------------------------------------------------------- 1 | 2 | from threading import Thread, Lock 3 | from datetime import datetime, timedelta 4 | import pytz 5 | 6 | import pandas as pd 7 | import numpy as np 8 | from unicorn_binance_websocket_api import BinanceWebSocketApiManager 9 | from unicorn_fy import UnicornFy 10 | 11 | 12 | from market_profile import MarketProfileSlice 13 | 14 | 15 | ''' 16 | Backend for updating GUI data. 17 | Stream and update aggTrades data from Binance given an instrument name. 18 | Uses websocket server to receive message for real time updating the GUI. 19 | ''' 20 | 21 | class RealTimeDataStream: 22 | 23 | def __init__(self, config): 24 | self._spot_websocket_api_manager = BinanceWebSocketApiManager(exchange="binance.com") 25 | self.data = [] 26 | 27 | # set config attribute 28 | self.inst = config['inst'] 29 | self.channel = config['channel'] 30 | self.token = config['token'] 31 | self.resample_period = config['resample_period'] 32 | # currently only support 1 min resample! 33 | assert self.resample_period == '1min', 'Reample period has to be `1min`' 34 | self.round_px_to_nearest = float(config['round_px_to_nearest']) 35 | 36 | # data storages 37 | self.ohlcv = pd.DataFrame() 38 | self.mp_slice = [] 39 | self._lock = Lock() 40 | 41 | 42 | def connect(self): 43 | self.thread_connect = Thread(target=self.run, args=(True,)) 44 | self.thread_connect.daemon = True 45 | self.thread_connect.start() 46 | 47 | 48 | def run(self): 49 | ''' 50 | See reference from `unicorn_binance_websocket_api.manager` 51 | https://github.com/LUCIT-Systems-and-Development/unicorn-binance-websocket-api/blob/master/unicorn_binance_websocket_api/manager.py 52 | ''' 53 | self._spot_websocket_api_manager.create_stream([self.channel], [self.inst]) 54 | 55 | # check if it is an incomplete candle (because not ran from 0th second) 56 | first_incomplete_candle = True if datetime.now().second != 0 else False 57 | 58 | current_minute = datetime.now().minute 59 | while True: 60 | # with self._lock: 61 | oldest_data_from_stream_buffer = self._spot_websocket_api_manager.pop_stream_data_from_stream_buffer() 62 | 63 | # callback 64 | if oldest_data_from_stream_buffer: 65 | data = UnicornFy.binance_com_websocket(oldest_data_from_stream_buffer) 66 | if 'result' not in data: # first line from websocket is a trivial dict = {'result': None, 'id': 1, 'unicorn_fied': ['binance.com', '0.11.0']} 67 | t = datetime.utcfromtimestamp(int(data['event_time']) // 1000).replace(tzinfo=pytz.utc) 68 | p = float(data['price']) 69 | if data['is_market_maker']: 70 | q = -float(data['quantity']) 71 | else: 72 | q = float(data['quantity']) 73 | 74 | # check if we it's time to call influx to write and clean up self.data 75 | if t.minute != current_minute: 76 | ohlc_df, volume_df = self._transform_feed_and_flush() 77 | candle_time = max(ohlc_df['T']) 78 | self._announce_minute_data(candle_time, ohlc_df, volume_df, no_update=first_incomplete_candle) 79 | current_minute = t.minute 80 | first_incomplete_candle = False 81 | 82 | self.data.append([t, p, q]) 83 | # print(f'{t} {p=} {q=}, {len(self.data)=}') 84 | 85 | 86 | def _announce_minute_data(self, dt: datetime, ohlcv_df: pd.DataFrame, volume_df: pd.DataFrame, no_update: bool = False): 87 | with self._lock: 88 | if not no_update: 89 | print(dt) 90 | ohlcv_df['T'] = pd.to_datetime(ohlcv_df['T']) 91 | ohlcv_df = ohlcv_df.set_index('T') 92 | ohlcv_df = ohlcv_df['o h l c v pot_ask pot_bid pot'.split()] 93 | 94 | self.ohlcv = pd.concat([self.ohlcv, ohlcv_df]) 95 | ohlcv_data = ohlcv_df.loc[dt].to_dict() 96 | orderflow_row = volume_df['p q d b a'.split()].values 97 | self.mp_slice.append(MarketProfileSlice(self.inst, dt, ohlcv_data, orderflow_row)) 98 | 99 | # always only store recent n timepoints in datastream 100 | self._flush_old_data(keep=10_000) 101 | 102 | 103 | def _flush_old_data(self, keep: int): 104 | if len(self.ohlcv) > keep: 105 | self.ohlcv = self.ohlcv.iloc[-keep:] 106 | self.mp_slice = self.mp_slice[-keep:] 107 | 108 | 109 | def _load_recent_candle_from_db(self, load_recent=30): 110 | ''' 111 | functionality to load from influxdb 112 | load recent n minutes of candles 113 | ''' 114 | profile = MarketProfileReader() 115 | end = datetime.now() 116 | start = end - timedelta(minutes=load_recent) # in local time 117 | profile.load_data_from_influx(inst=self.inst, start=start, end=end, env=self.env) 118 | slice_start = pytz.timezone('Asia/Hong_Kong').localize(start) # input in local time 119 | slice_end = pytz.timezone('Asia/Hong_Kong').localize(end) # input in local time 120 | mp_slice = profile[slice_start:slice_end] 121 | # override ohlcv df and mp_slice list, which are suppsoed to be empty before conencting to ws 122 | self.ohlcv = pd.DataFrame({ 123 | 'o': [mp.open for mp in mp_slice], 124 | 'h': [mp.high for mp in mp_slice], 125 | 'l': [mp.low for mp in mp_slice], 126 | 'c': [mp.close for mp in mp_slice], 127 | 'v': [mp.volume_qty for mp in mp_slice], 128 | 'pot': [mp.pot for mp in mp_slice], 129 | 'pot_ask': [mp.pot_ask for mp in mp_slice], 130 | 'pot_bid': [mp.pot_bid for mp in mp_slice], 131 | }, index=[mp.timepoint for mp in mp_slice]) 132 | self.mp_slice = profile[slice_start:slice_end] 133 | print(f'Loaded recent {len(self.ohlcv)} timepoint data from InfluxDB.') 134 | 135 | 136 | def _transform_feed_and_flush(self, verbose=True): 137 | # with self._lock: 138 | if self.data: 139 | # when the current minute finishes, do the following: 140 | 141 | # to conform into influx format 142 | df = pd.DataFrame(self.data, columns=['T', 'p', 'q']) 143 | 144 | # calculate tape speed 145 | pot_ask = len(df[df['q'] >= 0]) 146 | pot_bid = len(df[df['q'] < 0]) 147 | pot = pot_ask + pot_bid 148 | 149 | # HH:MM:SS means the data between HH:MM:00 to HH:MM:59; aka. the datetime is the open time 150 | ohlc_df = df.copy() 151 | ohlc_df['q'] = ohlc_df['q'].abs() 152 | ohlc_df = ohlc_df.resample(self.resample_period, on='T', origin='epoch', label='left').agg({'p': ['first', 'max', 'min', 'last'], 'q': 'sum'}).sort_index() 153 | ohlc_df.columns = ohlc_df.columns.get_level_values(1) 154 | ohlc_df = ohlc_df.rename(columns={'first': 'o', 'max': 'h', 'min': 'l', 'last': 'c', 'sum': 'v'}) 155 | ohlc_df['v'] = np.round(ohlc_df['v'], 8) 156 | ohlc_df['s'] = self.inst 157 | 158 | # PoT is Pace of Tape - trade intensity 159 | ohlc_df['pot_ask'] = pot_ask 160 | ohlc_df['pot_bid'] = pot_bid 161 | ohlc_df['pot'] = pot 162 | ohlc_df = ohlc_df.reset_index(drop=False) 163 | 164 | df['p'] = self.round_to(df['p'], nearest=self.round_px_to_nearest) 165 | delta_df = df.groupby(['p']).resample(self.resample_period, on='T', origin='epoch', label='left').agg({'q': 'sum'}).reset_index().sort_values(['T','p'])#.pivot(columns=['T'], values='q', index='p') 166 | volume_df = df.copy() 167 | volume_df['q'] = volume_df['q'].abs() 168 | volume_df = volume_df.groupby(['p']).resample(self.resample_period, on='T', origin='epoch', label='left').agg({'q': 'sum'}).reset_index().sort_values(['T','p'])#.pivot(columns=['T'], values='q', index='p') 169 | volume_df = volume_df[volume_df['q'] != 0.0] 170 | 171 | ''' 172 | q: quantity(volume): ask + bid 173 | d: delta: ask - bid 174 | b: (q - d) / 2 = bid 175 | a: (q + d) / 2 = ask 176 | ''' 177 | volume_df['p'] = np.round(volume_df['p'], 8) 178 | volume_df['q'] = np.round(volume_df['q'], 8) 179 | volume_df['d'] = np.round(delta_df['q'], 8) 180 | volume_df['a'] = np.round((volume_df['q'] + volume_df['d']) / 2, 8) 181 | volume_df['b'] = np.round((volume_df['q'] - volume_df['d']) / 2, 8) 182 | volume_df['s'] = self.inst 183 | 184 | # if not no_write: 185 | # self._write_to_db(volume_df=volume_df, ohlc_df=ohlc_df, verbose=verbose) 186 | # if verbose: 187 | # print(f'{datetime.utcnow()} - Finished writing, flushed data queue') 188 | 189 | if verbose: 190 | print(f'{datetime.utcnow()} - Flushed data queue') 191 | 192 | # reset data array 193 | self.data = [] 194 | return ohlc_df, volume_df 195 | else: 196 | return 197 | 198 | 199 | @staticmethod 200 | def round_to(pxs: float, nearest: int): 201 | return nearest * np.round(pxs / nearest) 202 | -------------------------------------------------------------------------------- /config/volume_data_recorder_config.yaml: -------------------------------------------------------------------------------- 1 | Config: 2 | - inst: btcusdt 3 | channel: aggTrade 4 | token: BTC 5 | resample_period: 1min 6 | round_px_to_nearest: 10 7 | # - inst: ethusdt 8 | # channel: aggTrade 9 | # token: ETH 10 | # resample_period: 1min 11 | # round_px_to_nearest: 2 12 | # - inst: nearusdt 13 | # channel: aggTrade 14 | # token: NEAR 15 | # resample_period: 1min 16 | # round_px_to_nearest: 0.01 -------------------------------------------------------------------------------- /docs/demo_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tysonwu/stack-orderflow/37fc4811950bce5fd40295a4c25ee443538dc4fc/docs/demo_01.png -------------------------------------------------------------------------------- /docs/demo_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tysonwu/stack-orderflow/37fc4811950bce5fd40295a4c25ee443538dc4fc/docs/demo_02.png -------------------------------------------------------------------------------- /docs/demo_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tysonwu/stack-orderflow/37fc4811950bce5fd40295a4c25ee443538dc4fc/docs/demo_03.png -------------------------------------------------------------------------------- /examples/01_simple_ohlcv.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import talib as ta 3 | 4 | from finplotter.finplotter import FinPlotter 5 | 6 | ''' 7 | Simple candlestick plot with a basic EMA line 8 | ''' 9 | 10 | if __name__ == '__main__': 11 | 12 | inst = 'btcusdt'.upper() 13 | interval = '1m' 14 | 15 | metadata = dict(inst=inst, interval=interval) 16 | 17 | df = pd.read_csv('examples/data/sample_data.csv', index_col=0) 18 | df.index = pd.to_datetime(df.index) 19 | df['ema20'] = ta.EMA(df['c'], timeperiod=10) 20 | 21 | plotter = FinPlotter(metadata=metadata, figsize=(1800, 1000)) 22 | plotter.plot_candlestick(data=df[['o', 'h', 'l', 'c', 'v']], with_volume=True) # has to be this name and this order 23 | plotter.add_line(data=df['ema20'], color='#F4D03F', width=3, legend='EMA') 24 | plotter.show() -------------------------------------------------------------------------------- /examples/02_candlestick_panels.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import pytz 3 | 4 | import pandas as pd 5 | import numpy as np 6 | import pyqtgraph as pg 7 | import talib as ta 8 | 9 | import finplotter.finplot_library as fplt 10 | 11 | ''' 12 | Plotting of candlestick chart with panels 13 | - Candlestick 14 | - Orderflow data by price level 15 | - Volume bar 16 | - Classic MACD 17 | - CVD 18 | - StochRSI 19 | ''' 20 | 21 | if __name__ == '__main__': 22 | inst = 'btcusdt' 23 | 24 | ohlcv = pd.read_csv('examples/data/sample_data.csv', index_col=0) 25 | ohlcv.index = pd.to_datetime(ohlcv.index) 26 | 27 | ema_n = [20, 50, 200] 28 | ema_colors = ['#33DCCD50', '#ADE03670', '#F4D03F80'] 29 | for n in ema_n: 30 | ohlcv[f'ema{n}'] = ta.EMA(ohlcv['c'], timeperiod=n) 31 | 32 | macd = [12, 26, 9] 33 | ohlcv['macd'], ohlcv['macd_signal'], ohlcv['macd_diff'] = ta.MACD(ohlcv['c'], fastperiod=macd[0], slowperiod=macd[1], signalperiod=macd[2]) 34 | 35 | stoch_rsi = [21, 14, 14] 36 | 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) 37 | 38 | print('Drawing plot...') 39 | 40 | # plotting 41 | fplt.foreground = '#D6DBDF' 42 | fplt.background = '#151E26' 43 | fplt.legend_border_color = '#ffffff30' # make transparent 44 | fplt.legend_fill_color = '#ffffff10' # make transparent 45 | fplt.legend_text_color = fplt.foreground 46 | fplt.top_graph_scale = 2 # top graph and bottom graph has ratio of r:1 47 | fplt.winx, fplt.winy, fplt.winw, fplt.winh = 0,0,1600,1600 48 | ax, ax3, ax2, ax4 = fplt.create_plot( 49 | title='Chart', 50 | rows=4, # main candlestick = ax / MACD = ax3 / CVD = ax2 / StochRSI = ax4 51 | maximize=False, 52 | init_zoom_periods=18, 53 | row_stretch_factors=[3, 1, 1, 1] 54 | ) 55 | 56 | # placeholder for tick info; updated with fplt.set_time_inspector(func) 57 | hover_label = fplt.add_legend('', ax=ax) 58 | 59 | # set max zoom: for orderflow data, allow more zoom (default was 20) 60 | fplt.max_zoom_points = 20 61 | 62 | # add candlestick 63 | # this is the version of candlestick without orderflow data 64 | candlestick_plot = fplt.candlestick_ochl(datasrc=ohlcv[['o', 'c', 'h', 'l']], candle_width=0.7, ax=ax) 65 | 66 | # add volume 67 | volume_plot = fplt.volume_ocv(ohlcv[['o', 'c', 'v']], candle_width=0.7, ax=ax.overlay()) 68 | 69 | # plot EMAs 70 | for n, color in zip(ema_n, ema_colors): 71 | fplt.plot(ohlcv[f'ema{n}'], ax=ax.overlay(), legend=f'EMA {n}', color=color) 72 | 73 | # plot stoch RSI 74 | stoch_rsi_plot = fplt.plot(ohlcv['fastk'], ax=ax4, legend=f'StochRSI Fast k: {stoch_rsi[1]} Timeperiod: {stoch_rsi[0]}') 75 | fplt.plot(ohlcv['fastd'], ax=ax4, legend=f'StochRSI Fast d: {stoch_rsi[2]} Timeperiod: {stoch_rsi[0]}') 76 | 77 | thresholds = [20, 80] 78 | for th in thresholds: 79 | rsi_threshold_line = pg.InfiniteLine(pos=th, angle=0, pen=fplt._makepen(color='#ffffff50', style='- - ')) 80 | ax4.addItem(rsi_threshold_line, ignoreBounds=True) 81 | fplt.add_band(*thresholds, color='#2980B920', ax=ax4) 82 | 83 | vb = stoch_rsi_plot.getViewBox() 84 | vb.setBackgroundColor('#00000000') 85 | 86 | # plot MACD 87 | macd_plot = fplt.volume_ocv(ohlcv[['o', 'c', 'macd_diff']], ax=ax3, candle_width=0.7, colorfunc=fplt.strength_colorfilter) 88 | fplt.plot(ohlcv['macd'], ax=ax3, legend=f'MACD ({macd[0]}, {macd[1]}, {macd[2]})') 89 | fplt.plot(ohlcv['macd_signal'], ax=ax3, legend='Signal') 90 | 91 | vb = macd_plot.getViewBox() 92 | vb.setBackgroundColor('#00000000') 93 | 94 | ''' 95 | Ref: examples/snp500.py 96 | ''' 97 | # plot cvd 98 | line_color = '#F4D03F' 99 | cvd_plot = fplt.plot(np.cumsum(ohlcv['d']), ax=ax2, legend='CVD', color=line_color, fillLevel=0, brush=line_color+'10') 100 | # and set background 101 | vb = cvd_plot.getViewBox() 102 | vb.setBackgroundColor('#00000000') 103 | 104 | ''' 105 | Ref: examples/complicated.py 106 | ''' 107 | # set bull body to same color as bull frame; otherwise it is default background color (transparent) 108 | bull = '#1ABC9C' 109 | bear = '#E74C3C' 110 | fplt.candle_bull_color = bull 111 | fplt.candle_bull_body_color = bull 112 | fplt.candle_bear_color = bear 113 | candlestick_plot.colors.update({ 114 | 'bull_body': fplt.candle_bull_color 115 | }) 116 | 117 | transparency = '45' 118 | volume_plot.colors.update({ 119 | 'bull_frame': fplt.candle_bull_color + transparency, 120 | 'bull_body': fplt.candle_bull_body_color + transparency, 121 | 'bear_frame': fplt.candle_bear_color + transparency, 122 | 'bear_body': fplt.candle_bear_color + transparency, 123 | }) 124 | 125 | # set gridlines 126 | ax.showGrid(x=True, y=True, alpha=0.2) 127 | ax2.showGrid(x=True, y=True, alpha=0.2) 128 | ax3.showGrid(x=True, y=True, alpha=0.2) 129 | ax4.showGrid(x=True, y=True, alpha=0.2) 130 | 131 | # add legend of ohlcv data 132 | ''' 133 | Ref: examples/snp500.py 134 | ''' 135 | def update_legend_text(x, y): 136 | dt = datetime.fromtimestamp(x // 1000000000) 137 | utcdt = dt.astimezone(pytz.utc) 138 | # dt = dt.replace(tzinfo=timezone.utc) 139 | row = ohlcv.loc[utcdt] 140 | # format html with the candle and set legend 141 | fmt = '%%s' % (bull if (row['o'] < row['c']).all() else bear) 142 | rawtxt = '%%s %%s   O: %s H: %s L: %s C: %s Delta: %s' % (fmt, fmt, fmt, fmt, fmt) 143 | hover_label.setText(rawtxt % ('TOKEN', 'INTERVAL', row['o'], row['h'], row['l'], row['c'], row['d'])) 144 | fplt.set_time_inspector(update_legend_text, ax=ax, when='hover') 145 | 146 | # additional crosshair info 147 | def enrich_info(x, y, xtext, ytext): 148 | o = ohlcv.iloc[x]['o'] 149 | h = ohlcv.iloc[x]['h'] 150 | l = ohlcv.iloc[x]['l'] 151 | c = ohlcv.iloc[x]['c'] 152 | add_xt = f'\t{xtext}' 153 | add_yt = f'\tLevel: {ytext}\n\n\tOpen: {o}\n\tHigh: {h}\n\tLow: {l}\n\tClose: {c}' 154 | return add_xt, add_yt 155 | 156 | fplt.add_crosshair_info(enrich_info, ax=ax) 157 | 158 | ''' 159 | Ref: examples/complicated.py 160 | ''' 161 | # set dark themes ==================== 162 | pg.setConfigOptions(foreground=fplt.foreground, background=fplt.background) 163 | 164 | # window background 165 | for win in fplt.windows: 166 | win.setBackground(fplt.background) 167 | 168 | # axis, crosshair, candlesticks, volumes 169 | axs = [ax for win in fplt.windows for ax in win.axs] 170 | vbs = set([ax.vb for ax in axs]) 171 | axs += fplt.overlay_axs 172 | axis_pen = fplt._makepen(color=fplt.foreground) 173 | for ax in axs: 174 | ax.axes['left']['item'].setPen(axis_pen) 175 | ax.axes['left']['item'].setTextPen(axis_pen) 176 | ax.axes['bottom']['item'].setPen(axis_pen) 177 | ax.axes['bottom']['item'].setTextPen(axis_pen) 178 | if ax.crosshair is not None: 179 | ax.crosshair.vline.pen.setColor(pg.mkColor(fplt.foreground)) 180 | ax.crosshair.hline.pen.setColor(pg.mkColor(fplt.foreground)) 181 | ax.crosshair.xtext.setColor(fplt.foreground) 182 | ax.crosshair.ytext.setColor(fplt.foreground) 183 | # ==================================== 184 | 185 | fplt.show() -------------------------------------------------------------------------------- /examples/03_static_orderflow_plot.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from market_profile import MarketProfileSlice 4 | from orderflow_plotter import OrderflowPlotter 5 | 6 | ''' 7 | Plotting of orderflow chart 8 | - Candlestick 9 | - Orderflow data by price level 10 | - Volume bar 11 | - Classic MACD 12 | - CVD 13 | - StochRSI 14 | ''' 15 | 16 | if __name__ == '__main__': 17 | 18 | inst = 'btcusdt' 19 | increment = 10 20 | token = inst.upper() 21 | interval = '1m' 22 | 23 | ohlcv = pd.read_csv('examples/data/sample_data_2.csv', index_col=0) 24 | ohlcv.index = pd.to_datetime(ohlcv.index) 25 | 26 | profile = pd.read_csv('examples/data/orderflow_data.csv') 27 | profile['t'] = pd.to_datetime(profile['t']) 28 | 29 | mp_slices=[] 30 | dts = list(ohlcv.index) 31 | for dt in dts: 32 | ohlcv_data = ohlcv.loc[dt].to_dict() 33 | orderflow_data = profile[profile['t'] == dt][['p', 'q', 'd', 'b', 'a']].to_numpy() 34 | mp_slices.append(MarketProfileSlice(inst, dt, ohlcv_data, orderflow_data)) 35 | 36 | plotter = OrderflowPlotter(token, interval, increment, ohlcv, mp_slices) 37 | plotter.orderflow_plot() 38 | plotter.show() 39 | -------------------------------------------------------------------------------- /examples/data/sample_data.csv: -------------------------------------------------------------------------------- 1 | ,o,h,l,c,v,d 2 | 2021-12-09 16:00:00+00:00,48462.08,48471.09,48400.25,48421.05,33.19742,-9.84886 3 | 2021-12-09 16:01:00+00:00,48421.06,48477.67,48400.0,48451.97,30.88797,3.75877 4 | 2021-12-09 16:02:00+00:00,48451.96,48499.52,48430.95,48450.0,34.58077,-2.56591 5 | 2021-12-09 16:03:00+00:00,48450.0,48458.6,48359.17,48369.67,36.80515,-13.13527 6 | 2021-12-09 16:04:00+00:00,48369.67,48387.2,48300.0,48316.97,20.64135,-5.38579 7 | 2021-12-09 16:05:00+00:00,48316.97,48383.62,48316.96,48321.76,19.85662,-1.10692 8 | 2021-12-09 16:06:00+00:00,48321.77,48429.44,48312.0,48409.06,30.80138,1.58166 9 | 2021-12-09 16:07:00+00:00,48409.06,48462.15,48404.38,48425.65,17.92321,-2.31785 10 | 2021-12-09 16:08:00+00:00,48425.64,48434.0,48385.9,48392.0,26.27059,-7.40073 11 | 2021-12-09 16:09:00+00:00,48390.99,48414.0,48373.11,48406.96,15.38413,3.18337 12 | 2021-12-09 16:10:00+00:00,48404.17,48450.0,48398.26,48437.24,12.46371,-1.90931 13 | 2021-12-09 16:11:00+00:00,48437.25,48449.51,48386.03,48398.25,23.45167,-5.69007 14 | 2021-12-09 16:12:00+00:00,48398.25,48398.28,48347.6,48350.0,13.2289,-2.28722 15 | 2021-12-09 16:13:00+00:00,48350.0,48365.0,48251.0,48282.4,35.8989,-9.33984 16 | 2021-12-09 16:14:00+00:00,48282.4,48321.0,48256.01,48271.68,20.20307,-2.21009 17 | 2021-12-09 16:15:00+00:00,48271.68,48326.36,48242.76,48326.36,22.6587,3.04832 18 | 2021-12-09 16:16:00+00:00,48326.36,48335.14,48272.81,48277.99,16.56836,0.458 19 | 2021-12-09 16:17:00+00:00,48278.0,48360.34,48150.61,48338.51,121.49389,-34.50623 20 | 2021-12-09 16:18:00+00:00,48338.52,48415.0,48326.93,48394.48,41.79241,8.92813 21 | 2021-12-09 16:19:00+00:00,48394.48,48428.8,48380.16,48402.83,16.18288,-1.27576 22 | 2021-12-09 16:20:00+00:00,48402.82,48421.43,48350.02,48354.86,19.63573,-1.46383 23 | 2021-12-09 16:21:00+00:00,48354.86,48388.11,48334.0,48385.69,20.84247,8.86547 24 | 2021-12-09 16:22:00+00:00,48385.69,48421.17,48377.39,48403.12,22.67236,13.85224 25 | 2021-12-09 16:23:00+00:00,48403.11,48425.0,48370.28,48420.63,36.84737,-0.65581 26 | 2021-12-09 16:24:00+00:00,48420.64,48476.77,48412.69,48463.04,19.7659,6.6856 27 | 2021-12-09 16:25:00+00:00,48463.04,48482.02,48425.64,48427.06,16.57488,1.21148 28 | 2021-12-09 16:26:00+00:00,48427.06,48443.87,48400.49,48415.99,10.70349,-0.27683 29 | 2021-12-09 16:27:00+00:00,48415.98,48500.0,48415.98,48492.82,19.29575,0.87743 30 | 2021-12-09 16:28:00+00:00,48492.82,48599.28,48481.11,48569.48,59.31386,12.5544 31 | 2021-12-09 16:29:00+00:00,48569.49,48575.29,48528.01,48550.0,24.11346,4.30542 32 | 2021-12-09 16:30:00+00:00,48544.45,48570.36,48500.0,48560.52,20.55332,-4.4265 33 | 2021-12-09 16:31:00+00:00,48560.52,48668.82,48560.51,48613.0,65.25778,-4.74242 34 | 2021-12-09 16:32:00+00:00,48612.99,48657.07,48603.01,48647.0,17.88621,-4.28919 35 | 2021-12-09 16:33:00+00:00,48645.99,48647.33,48604.07,48629.99,22.74937,-1.62149 36 | 2021-12-09 16:34:00+00:00,48630.0,48648.72,48600.04,48618.75,26.66594,-3.6086 37 | 2021-12-09 16:35:00+00:00,48618.75,48638.11,48581.24,48592.1,21.74366,-3.66616 38 | 2021-12-09 16:36:00+00:00,48592.1,48649.0,48573.01,48647.73,15.04572,-0.2236 39 | 2021-12-09 16:37:00+00:00,48647.74,48647.74,48604.96,48634.03,14.38838,0.01154 40 | 2021-12-09 16:38:00+00:00,48634.03,48635.97,48608.5,48617.59,8.88425,-2.68405 41 | 2021-12-09 16:39:00+00:00,48617.58,48729.92,48617.58,48729.91,20.26567,8.32501 42 | 2021-12-09 16:40:00+00:00,48729.92,48838.29,48700.01,48713.78,129.73702,43.90572 43 | 2021-12-09 16:41:00+00:00,48711.23,48795.74,48711.22,48740.01,30.41867,-0.48853 44 | 2021-12-09 16:42:00+00:00,48740.01,48743.71,48687.6,48700.36,26.43937,-13.09767 45 | 2021-12-09 16:43:00+00:00,48700.36,48710.28,48665.49,48665.49,15.98573,-2.95331 46 | 2021-12-09 16:44:00+00:00,48665.5,48704.16,48665.49,48670.01,24.59263,6.00103 47 | 2021-12-09 16:45:00+00:00,48670.01,48683.54,48580.0,48592.6,22.37004,8.03932 48 | 2021-12-09 16:46:00+00:00,48592.6,48680.54,48592.6,48653.36,36.48524,16.90386 49 | 2021-12-09 16:47:00+00:00,48653.37,48695.77,48650.0,48670.09,16.14957,2.70753 50 | 2021-12-09 16:48:00+00:00,48670.1,48698.9,48613.44,48689.27,102.37469,-37.08471 51 | 2021-12-09 16:49:00+00:00,48689.28,48702.09,48654.97,48656.87,18.97107,-1.92111 52 | 2021-12-09 16:50:00+00:00,48654.97,48699.99,48650.75,48681.73,13.01653,2.44113 53 | 2021-12-09 16:51:00+00:00,48681.72,48681.73,48643.86,48660.01,18.01154,-4.26514 54 | 2021-12-09 16:52:00+00:00,48660.01,48699.98,48660.0,48693.73,28.94981,4.68765 55 | 2021-12-09 16:53:00+00:00,48693.73,48696.13,48613.44,48636.84,63.12403,-34.52657 56 | 2021-12-09 16:54:00+00:00,48636.84,48680.78,48629.99,48672.13,26.98044,7.42792 57 | 2021-12-09 16:55:00+00:00,48672.13,48706.64,48659.35,48695.75,16.35741,3.74265 58 | 2021-12-09 16:56:00+00:00,48695.75,48699.02,48650.0,48659.82,18.7983,-15.5317 59 | 2021-12-09 16:57:00+00:00,48663.71,48678.94,48612.38,48617.8,23.21013,4.29171 60 | 2021-12-09 16:58:00+00:00,48617.8,48638.63,48529.41,48562.67,23.36645,-11.99473 61 | 2021-12-09 16:59:00+00:00,48562.68,48571.1,48504.97,48504.98,11.13805,-3.08877 62 | 2021-12-09 17:00:00+00:00,48504.97,48539.98,48461.42,48511.09,52.13817,-15.83589 63 | 2021-12-09 17:01:00+00:00,48511.09,48572.98,48511.08,48559.57,21.4514,4.558 64 | 2021-12-09 17:02:00+00:00,48559.58,48683.43,48538.63,48657.95,66.31384,32.48004 65 | 2021-12-09 17:03:00+00:00,48657.03,48705.15,48612.34,48623.31,37.97775,-7.00153 66 | 2021-12-09 17:04:00+00:00,48623.31,48641.29,48615.82,48623.75,9.5125,-1.64806 67 | 2021-12-09 17:05:00+00:00,48623.74,48623.75,48573.69,48580.38,9.45786,-3.89566 68 | 2021-12-09 17:06:00+00:00,48580.39,48600.55,48560.0,48600.54,19.27907,0.38283 69 | 2021-12-09 17:07:00+00:00,48600.54,48628.62,48600.54,48624.59,8.81892,-0.23422 70 | 2021-12-09 17:08:00+00:00,48624.6,48641.14,48610.0,48610.0,12.9581,-0.81442 71 | 2021-12-09 17:09:00+00:00,48610.0,48629.22,48610.0,48614.53,11.09988,-0.18574 72 | 2021-12-09 17:10:00+00:00,48610.86,48620.49,48571.38,48603.01,23.50961,-8.16163 73 | 2021-12-09 17:11:00+00:00,48603.01,48633.85,48598.16,48629.28,19.43852,-9.35022 74 | 2021-12-09 17:12:00+00:00,48629.28,48659.52,48607.26,48607.26,11.09724,-2.77176 75 | 2021-12-09 17:13:00+00:00,48607.26,48636.22,48592.01,48605.12,25.92299,0.77285 76 | 2021-12-09 17:14:00+00:00,48605.12,48653.99,48605.12,48640.43,19.12718,1.17666 77 | 2021-12-09 17:15:00+00:00,48640.44,48645.75,48600.02,48612.28,10.31785,0.70763 78 | 2021-12-09 17:16:00+00:00,48612.27,48666.66,48610.0,48666.65,10.72669,2.95363 79 | 2021-12-09 17:17:00+00:00,48666.66,48698.9,48663.96,48698.89,15.01342,4.40838 80 | 2021-12-09 17:18:00+00:00,48698.9,48698.9,48600.72,48611.31,18.48362,-5.9255 81 | 2021-12-09 17:19:00+00:00,48611.31,48626.16,48606.86,48617.79,4.14046,0.80226 82 | 2021-12-09 17:20:00+00:00,48617.78,48617.79,48584.38,48600.77,8.4233,-2.53402 83 | 2021-12-09 17:21:00+00:00,48600.78,48600.78,48553.36,48578.21,15.29689,-5.40659 84 | 2021-12-09 17:22:00+00:00,48578.21,48626.03,48570.39,48594.63,15.1639,2.7131 85 | 2021-12-09 17:23:00+00:00,48594.64,48600.01,48571.01,48593.08,17.27131,-2.97463 86 | 2021-12-09 17:24:00+00:00,48593.08,48650.0,48585.99,48638.46,18.03648,1.29634 87 | 2021-12-09 17:25:00+00:00,48636.57,48691.92,48621.0,48676.8,41.18835,19.31593 88 | 2021-12-09 17:26:00+00:00,48676.8,48678.97,48581.01,48585.02,14.07153,-4.86561 89 | 2021-12-09 17:27:00+00:00,48585.02,48593.04,48540.0,48551.31,14.98182,-6.22588 90 | 2021-12-09 17:28:00+00:00,48550.73,48563.14,48540.3,48562.75,9.69964,-1.24244 91 | 2021-12-09 17:29:00+00:00,48559.55,48580.01,48559.54,48560.0,9.85318,-2.78444 92 | 2021-12-09 17:30:00+00:00,48560.0,48560.01,48461.42,48488.89,24.36224,-7.88904 93 | 2021-12-09 17:31:00+00:00,48492.38,48492.38,48473.67,48488.86,11.46918,-0.96282 94 | 2021-12-09 17:32:00+00:00,48488.87,48525.61,48486.49,48520.03,17.84398,1.8659 95 | 2021-12-09 17:33:00+00:00,48520.03,48568.05,48508.62,48567.29,22.84265,-6.91843 96 | 2021-12-09 17:34:00+00:00,48567.29,48568.05,48515.11,48533.69,9.77684,-3.38378 97 | 2021-12-09 17:35:00+00:00,48533.69,48574.99,48520.19,48525.43,22.73426,-5.4008 98 | 2021-12-09 17:36:00+00:00,48529.7,48554.87,48519.05,48539.49,16.01484,0.55892 99 | 2021-12-09 17:37:00+00:00,48539.49,48539.5,48491.12,48491.12,12.52299,-2.60951 100 | 2021-12-09 17:38:00+00:00,48491.13,48495.17,48426.25,48453.29,38.92443,14.60357 101 | 2021-12-09 17:39:00+00:00,48451.97,48463.25,48411.24,48448.78,30.35585,-9.84101 102 | 2021-12-09 17:40:00+00:00,48448.77,48479.34,48435.57,48459.78,28.02667,7.35897 103 | 2021-12-09 17:41:00+00:00,48459.77,48544.54,48458.19,48502.91,36.48446,24.04394 104 | 2021-12-09 17:42:00+00:00,48502.91,48542.51,48476.53,48505.84,17.81188,-0.94784 105 | 2021-12-09 17:43:00+00:00,48505.84,48507.03,48440.16,48445.93,18.02415,-5.17623 106 | 2021-12-09 17:44:00+00:00,48445.93,48453.31,48400.0,48403.51,10.69841,0.31323 107 | 2021-12-09 17:45:00+00:00,48403.48,48465.97,48403.47,48428.69,26.53248,-1.94172 108 | 2021-12-09 17:46:00+00:00,48428.69,48431.62,48413.61,48426.78,5.42267,0.47485 109 | 2021-12-09 17:47:00+00:00,48426.78,48471.05,48424.06,48427.39,10.96142,3.1045 110 | 2021-12-09 17:48:00+00:00,48427.39,48449.3,48427.09,48438.95,10.53923,-0.63773 111 | 2021-12-09 17:49:00+00:00,48438.95,48462.01,48437.62,48455.96,12.00467,3.06559 112 | 2021-12-09 17:50:00+00:00,48455.97,48463.75,48416.51,48417.57,13.75201,1.95869 113 | 2021-12-09 17:51:00+00:00,48417.57,48507.74,48404.16,48491.82,33.16287,8.33037 114 | 2021-12-09 17:52:00+00:00,48491.83,48520.07,48426.76,48438.2,22.19856,-3.09466 115 | 2021-12-09 17:53:00+00:00,48438.21,48460.04,48427.7,48432.6,15.23131,-0.26225 116 | 2021-12-09 17:54:00+00:00,48432.6,48443.64,48360.0,48360.0,24.51334,-2.02672 117 | 2021-12-09 17:55:00+00:00,48360.0,48383.58,48350.07,48381.72,15.31994,-0.24872 118 | 2021-12-09 17:56:00+00:00,48381.72,48381.72,48300.9,48306.01,19.25495,-6.20619 119 | 2021-12-09 17:57:00+00:00,48306.01,48335.08,48272.57,48293.18,26.27427,2.39881 120 | 2021-12-09 17:58:00+00:00,48293.19,48306.0,48125.0,48127.99,44.72591,-16.28447 121 | 2021-12-09 17:59:00+00:00,48127.99,48205.56,48127.99,48171.65,50.30446,2.69566 122 | 2021-12-09 18:00:00+00:00,48171.66,48265.31,48171.65,48265.31,39.33272,2.32488 123 | 2021-12-09 18:01:00+00:00,48261.6,48292.58,48230.0,48255.99,38.34556,0.76678 124 | 2021-12-09 18:02:00+00:00,48255.99,48300.0,48233.92,48293.52,20.94682,3.16356 125 | 2021-12-09 18:03:00+00:00,48293.39,48363.42,48277.0,48289.97,53.76145,16.65925 126 | 2021-12-09 18:04:00+00:00,48289.97,48289.98,48142.08,48220.57,36.78634,-16.3849 127 | 2021-12-09 18:05:00+00:00,48220.58,48264.42,48162.65,48192.99,15.03328,-3.73744 128 | 2021-12-09 18:06:00+00:00,48192.98,48239.77,48181.94,48203.21,19.90815,-2.84815 129 | 2021-12-09 18:07:00+00:00,48203.21,48300.0,48203.21,48279.85,17.02044,4.29444 130 | 2021-12-09 18:08:00+00:00,48279.84,48300.0,48269.8,48269.8,14.27011,-0.66871 131 | 2021-12-09 18:09:00+00:00,48269.8,48333.28,48269.8,48322.5,11.64713,3.80669 132 | 2021-12-09 18:10:00+00:00,48322.5,48350.0,48276.3,48300.23,11.72683,-0.50421 133 | 2021-12-09 18:11:00+00:00,48300.23,48311.23,48270.0,48274.31,10.31554,-2.25612 134 | 2021-12-09 18:12:00+00:00,48274.32,48283.05,48236.68,48259.18,10.04726,1.6882 135 | 2021-12-09 18:13:00+00:00,48259.18,48259.21,48200.0,48200.07,11.94605,-4.36099 136 | 2021-12-09 18:14:00+00:00,48200.06,48267.19,48200.0,48260.0,14.43437,1.03229 137 | 2021-12-09 18:15:00+00:00,48259.99,48315.5,48205.09,48247.88,35.83551,-16.45145 138 | 2021-12-09 18:16:00+00:00,48247.88,48350.0,48240.88,48344.39,23.39579,6.42513 139 | 2021-12-09 18:17:00+00:00,48344.38,48350.0,48302.04,48308.95,23.02273,6.95703 140 | 2021-12-09 18:18:00+00:00,48308.96,48348.87,48277.53,48292.3,25.53056,4.4442 141 | 2021-12-09 18:19:00+00:00,48289.17,48321.4,48262.22,48274.6,8.9135,-3.36538 142 | 2021-12-09 18:20:00+00:00,48274.6,48307.0,48254.05,48276.79,10.47728,1.16168 143 | 2021-12-09 18:21:00+00:00,48276.78,48276.79,48160.0,48171.0,24.73562,-9.77944 144 | 2021-12-09 18:22:00+00:00,48170.99,48171.0,48118.05,48150.0,31.25459,-2.44109 145 | 2021-12-09 18:23:00+00:00,48150.0,48157.0,48000.0,48121.66,159.62083,-69.73561 146 | 2021-12-09 18:24:00+00:00,48121.67,48193.99,48055.41,48138.3,68.24915,-1.28295 147 | 2021-12-09 18:25:00+00:00,48138.29,48180.71,48050.59,48066.13,72.45619,-5.15127 148 | 2021-12-09 18:26:00+00:00,48066.13,48072.24,47885.0,47964.25,233.24593,-62.08829 149 | 2021-12-09 18:27:00+00:00,47964.25,47985.96,47547.93,47806.38,360.72372,-126.78854 150 | 2021-12-09 18:28:00+00:00,47813.81,47855.52,47678.0,47817.0,98.8646,10.12552 151 | 2021-12-09 18:29:00+00:00,47819.8,47985.3,47791.0,47884.73,195.81371,-16.39093 152 | 2021-12-09 18:30:00+00:00,47884.72,47885.9,47796.84,47838.34,66.00139,0.85725 153 | 2021-12-09 18:31:00+00:00,47838.36,47863.02,47750.01,47755.01,29.56819,-2.02637 154 | 2021-12-09 18:32:00+00:00,47755.01,47859.02,47755.0,47810.04,37.83084,15.31352 155 | 2021-12-09 18:33:00+00:00,47810.04,47826.99,47694.32,47694.32,53.21158,4.31446 156 | 2021-12-09 18:34:00+00:00,47694.32,47767.11,47556.35,47667.3,141.87722,-41.95822 157 | 2021-12-09 18:35:00+00:00,47667.3,47683.45,47537.26,47565.69,85.86911,-33.89627 158 | 2021-12-09 18:36:00+00:00,47570.85,47645.25,47565.69,47634.75,55.75349,9.93573 159 | 2021-12-09 18:37:00+00:00,47634.75,47748.96,47610.47,47689.33,30.97011,2.94537 160 | 2021-12-09 18:38:00+00:00,47689.33,47757.9,47689.33,47734.28,40.10801,15.89435 161 | 2021-12-09 18:39:00+00:00,47734.29,47843.07,47726.01,47771.78,70.14363,18.11995 162 | 2021-12-09 18:40:00+00:00,47771.78,47778.01,47668.05,47748.55,50.88067,-27.03565 163 | 2021-12-09 18:41:00+00:00,47748.55,47748.56,47680.0,47685.66,15.7938,-0.81858 164 | 2021-12-09 18:42:00+00:00,47685.65,47733.47,47650.55,47668.6,23.09799,-2.26545 165 | 2021-12-09 18:43:00+00:00,47668.6,47745.27,47657.22,47714.12,39.68717,12.14783 166 | 2021-12-09 18:44:00+00:00,47714.13,47743.61,47683.0,47683.01,32.80538,3.2859 167 | 2021-12-09 18:45:00+00:00,47683.0,47773.87,47660.38,47743.84,37.98497,10.03003 168 | 2021-12-09 18:46:00+00:00,47743.84,47743.84,47672.62,47686.35,21.27299,0.77063 169 | 2021-12-09 18:47:00+00:00,47686.35,47705.42,47570.49,47614.95,49.8062,-7.65308 170 | 2021-12-09 18:48:00+00:00,47614.95,47614.96,47520.0,47566.72,65.29844,-13.80788 171 | 2021-12-09 18:49:00+00:00,47566.73,47649.99,47546.92,47601.38,48.71767,2.27983 172 | 2021-12-09 18:50:00+00:00,47601.38,47608.99,47510.0,47516.78,60.74578,-0.03176 173 | 2021-12-09 18:51:00+00:00,47516.78,47555.0,47510.0,47532.64,48.1163,5.97424 174 | 2021-12-09 18:52:00+00:00,47532.65,47553.75,47500.0,47542.98,64.63464,-4.68574 175 | 2021-12-09 18:53:00+00:00,47542.98,47632.2,47530.01,47606.41,36.34047,13.22999 176 | 2021-12-09 18:54:00+00:00,47606.42,47686.85,47606.41,47631.04,54.49522,13.8676 177 | 2021-12-09 18:55:00+00:00,47631.04,47684.84,47609.39,47665.02,20.17286,1.98922 178 | 2021-12-09 18:56:00+00:00,47665.01,47814.61,47665.01,47745.25,75.77455,6.02507 179 | 2021-12-09 18:57:00+00:00,47745.25,47839.0,47701.21,47839.0,36.90415,-2.66997 180 | 2021-12-09 18:58:00+00:00,47838.99,47850.0,47770.45,47791.54,57.01794,-11.2607 181 | 2021-12-09 18:59:00+00:00,47793.94,47793.94,47714.98,47753.24,28.75472,-1.133 182 | 2021-12-09 19:00:00+00:00,47751.87,47823.92,47715.01,47800.0,35.31876,6.94756 183 | 2021-12-09 19:01:00+00:00,47800.0,47888.28,47799.99,47850.0,46.78706,22.12248 184 | 2021-12-09 19:02:00+00:00,47850.01,47858.63,47810.0,47817.53,22.00075,-2.63513 185 | 2021-12-09 19:03:00+00:00,47817.52,47817.53,47780.0,47791.41,17.35652,4.67042 186 | 2021-12-09 19:04:00+00:00,47791.4,47808.73,47744.04,47774.62,30.56867,9.86211 187 | 2021-12-09 19:05:00+00:00,47774.62,47800.04,47763.11,47768.22,8.33402,-0.11096 188 | 2021-12-09 19:06:00+00:00,47763.11,47863.36,47760.0,47847.19,27.56449,9.41747 189 | 2021-12-09 19:07:00+00:00,47847.19,47886.0,47835.56,47870.26,20.95855,9.25515 190 | 2021-12-09 19:08:00+00:00,47870.27,47986.62,47850.11,47915.91,41.11089,15.58805 191 | 2021-12-09 19:09:00+00:00,47924.25,48018.63,47784.22,47847.52,130.32771,-10.91207 192 | 2021-12-09 19:10:00+00:00,47847.52,47847.53,47653.14,47653.14,39.88889,-10.81909 193 | 2021-12-09 19:11:00+00:00,47653.14,47705.7,47600.68,47657.2,50.0431,-4.54276 194 | 2021-12-09 19:12:00+00:00,47657.21,47710.55,47617.37,47693.22,40.13704,8.90716 195 | 2021-12-09 19:13:00+00:00,47693.22,47700.21,47623.37,47640.28,25.65562,-0.32938 196 | 2021-12-09 19:14:00+00:00,47640.29,47654.75,47614.0,47648.23,15.05372,0.7643 197 | 2021-12-09 19:15:00+00:00,47648.22,47649.98,47552.0,47636.79,46.39269,-17.96539 198 | 2021-12-09 19:16:00+00:00,47636.8,47690.16,47617.86,47665.44,18.22909,2.86877 199 | 2021-12-09 19:17:00+00:00,47665.44,47742.49,47657.8,47716.93,35.21191,18.11761 200 | 2021-12-09 19:18:00+00:00,47721.62,47800.0,47716.93,47791.11,29.216,5.4097 201 | 2021-12-09 19:19:00+00:00,47791.11,47791.11,47712.68,47743.27,22.61945,3.04261 202 | 2021-12-09 19:20:00+00:00,47743.26,47794.5,47735.75,47772.19,14.90552,1.15772 203 | 2021-12-09 19:21:00+00:00,47772.19,47778.74,47710.4,47714.76,14.28422,-5.24612 204 | 2021-12-09 19:22:00+00:00,47714.75,47761.93,47678.97,47749.33,13.09988,-1.80872 205 | 2021-12-09 19:23:00+00:00,47747.63,47848.9,47718.42,47848.9,24.8379,2.84158 206 | 2021-12-09 19:24:00+00:00,47848.9,47862.01,47785.89,47830.8,27.87156,-8.32224 207 | 2021-12-09 19:25:00+00:00,47830.8,47840.53,47776.0,47825.43,14.31423,-2.00661 208 | 2021-12-09 19:26:00+00:00,47825.43,47825.43,47767.53,47776.19,14.60979,-1.71183 209 | 2021-12-09 19:27:00+00:00,47776.2,47815.11,47767.03,47767.04,19.64111,6.71507 210 | 2021-12-09 19:28:00+00:00,47767.03,47800.0,47765.5,47789.76,11.61942,-3.86452 211 | 2021-12-09 19:29:00+00:00,47789.79,47792.18,47764.01,47783.52,13.6047,0.9915 212 | 2021-12-09 19:30:00+00:00,47783.53,47839.49,47783.53,47829.77,22.17437,10.96437 213 | 2021-12-09 19:31:00+00:00,47829.83,47850.0,47760.0,47760.0,19.54458,-6.15866 214 | 2021-12-09 19:32:00+00:00,47760.0,47785.79,47721.72,47744.79,19.80299,-4.23737 215 | 2021-12-09 19:33:00+00:00,47744.79,47802.56,47725.64,47790.79,13.24541,-0.36001 216 | 2021-12-09 19:34:00+00:00,47790.81,47873.08,47779.0,47828.95,29.12617,8.77155 217 | 2021-12-09 19:35:00+00:00,47832.71,47843.36,47800.01,47811.15,16.61361,-3.47957 218 | 2021-12-09 19:36:00+00:00,47811.16,47850.06,47810.82,47830.0,13.34088,1.99642 219 | 2021-12-09 19:37:00+00:00,47830.0,47930.0,47827.02,47918.01,30.75514,4.8183 220 | 2021-12-09 19:38:00+00:00,47918.0,47924.01,47866.76,47883.74,13.76087,-0.58501 221 | 2021-12-09 19:39:00+00:00,47883.73,47919.99,47880.02,47900.0,23.37856,1.55004 222 | 2021-12-09 19:40:00+00:00,47900.0,47937.79,47899.99,47924.16,19.53532,-1.34156 223 | 2021-12-09 19:41:00+00:00,47924.16,47963.15,47914.92,47918.28,51.2301,0.7845 224 | 2021-12-09 19:42:00+00:00,47918.28,47918.28,47805.94,47812.99,35.80958,-16.28008 225 | 2021-12-09 19:43:00+00:00,47813.0,47818.39,47756.0,47768.16,17.55094,-3.82984 226 | 2021-12-09 19:44:00+00:00,47765.0,47858.74,47724.86,47771.51,65.8107,-15.17412 227 | 2021-12-09 19:45:00+00:00,47774.88,47796.87,47720.83,47734.38,29.40851,6.04227 228 | 2021-12-09 19:46:00+00:00,47734.38,47788.5,47707.38,47754.63,33.63215,-0.08571 229 | 2021-12-09 19:47:00+00:00,47754.64,47785.35,47721.72,47737.29,22.81776,-0.00164 230 | 2021-12-09 19:48:00+00:00,47737.28,47768.52,47730.22,47741.69,17.26888,2.07638 231 | 2021-12-09 19:49:00+00:00,47741.7,47832.3,47731.58,47832.12,21.46646,5.34704 232 | 2021-12-09 19:50:00+00:00,47832.12,47861.42,47805.37,47825.35,30.15849,-9.66759 233 | 2021-12-09 19:51:00+00:00,47825.34,47864.41,47822.25,47839.91,16.78082,6.29474 234 | 2021-12-09 19:52:00+00:00,47839.91,47866.21,47761.09,47791.86,32.88954,-20.85004 235 | 2021-12-09 19:53:00+00:00,47791.85,47794.99,47750.79,47774.88,13.56606,0.99678 236 | 2021-12-09 19:54:00+00:00,47774.87,47774.88,47730.0,47739.92,16.26292,-4.34772 237 | 2021-12-09 19:55:00+00:00,47739.92,47750.0,47700.0,47700.0,7.83787,-1.49827 238 | 2021-12-09 19:56:00+00:00,47700.01,47704.92,47650.0,47664.08,30.64117,-2.90547 239 | 2021-12-09 19:57:00+00:00,47664.08,47707.13,47628.11,47700.0,29.99506,-2.68228 240 | 2021-12-09 19:58:00+00:00,47700.01,47723.9,47684.7,47684.71,13.30957,-3.31165 241 | 2021-12-09 19:59:00+00:00,47684.71,47687.43,47610.0,47613.53,19.37786,-6.91258 242 | 2021-12-09 20:00:00+00:00,47615.89,47639.99,47556.54,47622.66,32.48367,12.13053 243 | 2021-12-09 20:01:00+00:00,47622.67,47711.0,47615.53,47701.42,25.2616,3.84212 244 | 2021-12-09 20:02:00+00:00,47701.42,47702.0,47658.49,47662.57,16.97593,-1.25171 245 | 2021-12-09 20:03:00+00:00,47662.56,47699.21,47574.14,47600.0,27.45793,0.70913 246 | 2021-12-09 20:04:00+00:00,47600.0,47618.93,47560.24,47560.24,26.17596,4.17592 247 | 2021-12-09 20:05:00+00:00,47560.24,47593.44,47525.34,47546.41,54.96504,0.04564 248 | 2021-12-09 20:06:00+00:00,47550.05,47610.39,47520.22,47549.62,23.15354,-2.22782 249 | 2021-12-09 20:07:00+00:00,47549.61,47557.78,47500.22,47500.32,24.56703,-0.08251 250 | 2021-12-09 20:08:00+00:00,47500.32,47561.32,47464.0,47531.68,148.56746,-24.38612 251 | 2021-12-09 20:09:00+00:00,47531.68,47531.69,47422.98,47474.84,46.3633,-11.8114 252 | 2021-12-09 20:10:00+00:00,47474.83,47540.35,47466.13,47523.78,39.09088,3.02298 253 | 2021-12-09 20:11:00+00:00,47523.79,47527.95,47422.98,47424.23,31.63602,0.38898 254 | 2021-12-09 20:12:00+00:00,47424.24,47428.18,47373.34,47383.05,86.47801,-16.12625 255 | 2021-12-09 20:13:00+00:00,47383.05,47455.21,47359.61,47369.22,38.52295,1.79965 256 | 2021-12-09 20:14:00+00:00,47368.63,47426.97,47366.47,47398.99,23.3264,-0.91776 257 | 2021-12-09 20:15:00+00:00,47396.77,47515.6,47387.56,47484.0,33.34442,11.26568 258 | 2021-12-09 20:16:00+00:00,47484.02,47640.0,47463.01,47610.0,63.41772,23.08598 259 | 2021-12-09 20:17:00+00:00,47610.01,47639.09,47540.92,47587.78,31.68534,-0.3794 260 | 2021-12-09 20:18:00+00:00,47587.77,47631.37,47581.77,47593.45,21.96321,2.71495 261 | 2021-12-09 20:19:00+00:00,47593.45,47684.07,47516.78,47668.48,30.1008,-0.27166 262 | 2021-12-09 20:20:00+00:00,47668.48,47719.76,47600.0,47608.32,28.41382,5.53704 263 | 2021-12-09 20:21:00+00:00,47608.32,47636.14,47557.31,47561.9,14.6481,-2.2551 264 | 2021-12-09 20:22:00+00:00,47561.9,47564.28,47510.23,47537.36,27.89854,3.91904 265 | 2021-12-09 20:23:00+00:00,47537.35,47626.05,47517.58,47603.73,15.20852,2.29162 266 | 2021-12-09 20:24:00+00:00,47603.73,47618.94,47547.03,47561.55,13.0622,2.76312 267 | 2021-12-09 20:25:00+00:00,47561.54,47561.55,47415.57,47485.45,30.66192,-2.952 268 | 2021-12-09 20:26:00+00:00,47485.44,47513.0,47428.01,47496.79,15.11078,-0.49848 269 | 2021-12-09 20:27:00+00:00,47496.8,47600.0,47488.74,47598.51,15.38874,6.19226 270 | 2021-12-09 20:28:00+00:00,47599.99,47600.0,47545.32,47563.62,18.85786,5.50288 271 | 2021-12-09 20:29:00+00:00,47564.41,47575.44,47513.43,47520.81,11.71755,-2.17813 272 | 2021-12-09 20:30:00+00:00,47520.8,47612.95,47475.43,47596.02,16.64406,2.87824 273 | 2021-12-09 20:31:00+00:00,47595.99,47600.0,47539.59,47550.0,12.97183,-1.88857 274 | 2021-12-09 20:32:00+00:00,47550.01,47550.01,47500.0,47500.01,13.12036,-6.83308 275 | 2021-12-09 20:33:00+00:00,47500.0,47511.86,47420.61,47429.41,21.81849,7.19045 276 | 2021-12-09 20:34:00+00:00,47433.89,47433.89,47390.0,47407.01,23.96631,3.22765 277 | 2021-12-09 20:35:00+00:00,47407.01,47444.72,47390.01,47407.28,27.4922,-2.98188 278 | 2021-12-09 20:36:00+00:00,47407.27,47478.09,47333.0,47349.98,56.81844,-19.33174 279 | 2021-12-09 20:37:00+00:00,47349.98,47398.64,47338.12,47384.68,18.65384,-0.86068 280 | 2021-12-09 20:38:00+00:00,47384.68,47471.14,47369.01,47431.52,28.99028,12.49532 281 | 2021-12-09 20:39:00+00:00,47431.53,47475.58,47411.21,47466.91,26.54801,11.71371 282 | 2021-12-09 20:40:00+00:00,47468.1,47471.92,47378.84,47378.85,11.74243,-0.00441 283 | 2021-12-09 20:41:00+00:00,47378.85,47416.24,47374.12,47386.23,13.3052,-4.26294 284 | 2021-12-09 20:42:00+00:00,47384.53,47394.1,47359.01,47370.0,11.69128,-0.44616 285 | 2021-12-09 20:43:00+00:00,47370.0,47394.0,47320.0,47375.45,24.46085,4.26637 286 | 2021-12-09 20:44:00+00:00,47374.55,47413.54,47349.6,47375.43,26.76865,9.16793 287 | 2021-12-09 20:45:00+00:00,47375.43,47395.73,47327.03,47330.99,28.49977,1.05827 288 | 2021-12-09 20:46:00+00:00,47330.99,47411.18,47326.31,47400.27,33.36038,-2.24364 289 | 2021-12-09 20:47:00+00:00,47400.28,47434.98,47342.48,47426.16,26.48458,6.95598 290 | 2021-12-09 20:48:00+00:00,47426.16,47577.35,47420.77,47472.43,56.4821,1.56114 291 | 2021-12-09 20:49:00+00:00,47468.03,47480.47,47393.22,47406.91,24.33013,3.71673 292 | 2021-12-09 20:50:00+00:00,47406.92,47425.45,47361.93,47388.39,22.62333,-1.93759 293 | 2021-12-09 20:51:00+00:00,47388.39,47474.31,47388.39,47439.26,22.96322,6.38158 294 | 2021-12-09 20:52:00+00:00,47439.26,47511.09,47439.25,47495.15,46.88827,-16.85689 295 | 2021-12-09 20:53:00+00:00,47495.14,47501.39,47441.44,47462.05,16.45068,-3.92308 296 | 2021-12-09 20:54:00+00:00,47462.05,47529.14,47444.19,47479.15,33.29875,8.20763 297 | 2021-12-09 20:55:00+00:00,47479.15,47507.47,47442.0,47450.28,31.25648,-6.70906 298 | 2021-12-09 20:56:00+00:00,47450.28,47560.0,47413.01,47503.41,34.96715,3.11369 299 | 2021-12-09 20:57:00+00:00,47503.4,47577.42,47503.4,47550.82,32.12153,9.37253 300 | 2021-12-09 20:58:00+00:00,47550.82,47678.84,47550.0,47659.0,62.19299,26.99421 301 | 2021-12-09 20:59:00+00:00,47659.01,47724.39,47636.66,47683.61,51.47722,16.1452 302 | 2021-12-09 21:00:00+00:00,47683.61,47766.42,47642.87,47739.04,35.37194,4.69036 303 | 2021-12-09 21:01:00+00:00,47739.04,47785.33,47705.42,47728.06,40.00747,12.54975 304 | 2021-12-09 21:02:00+00:00,47728.06,47740.02,47667.0,47672.17,27.71807,-8.08919 305 | 2021-12-09 21:03:00+00:00,47672.17,47672.18,47573.23,47580.97,46.11501,-7.65363 306 | 2021-12-09 21:04:00+00:00,47577.22,47604.93,47563.45,47594.35,14.56052,3.0419 307 | 2021-12-09 21:05:00+00:00,47594.35,47624.54,47568.65,47601.64,18.86074,0.66144 308 | 2021-12-09 21:06:00+00:00,47601.65,47628.73,47574.3,47605.98,22.21953,1.37605 309 | 2021-12-09 21:07:00+00:00,47605.99,47677.55,47602.23,47666.0,17.8799,5.26852 310 | 2021-12-09 21:08:00+00:00,47666.0,47753.11,47659.71,47750.01,21.7333,6.85346 311 | 2021-12-09 21:09:00+00:00,47750.0,47779.43,47707.99,47708.0,27.39377,-0.92499 312 | 2021-12-09 21:10:00+00:00,47708.0,47771.51,47707.99,47730.43,38.66754,-3.2892 313 | 2021-12-09 21:11:00+00:00,47730.42,47739.05,47693.64,47715.77,22.53717,2.10173 314 | 2021-12-09 21:12:00+00:00,47715.77,47744.14,47704.42,47744.1,20.60496,10.34814 315 | 2021-12-09 21:13:00+00:00,47744.09,47787.43,47730.46,47781.6,11.01188,1.07464 316 | 2021-12-09 21:14:00+00:00,47778.43,47790.27,47750.0,47761.98,20.16038,0.51434 317 | 2021-12-09 21:15:00+00:00,47761.98,47786.77,47742.25,47786.76,11.5763,4.65942 318 | 2021-12-09 21:16:00+00:00,47786.76,47787.49,47707.31,47722.11,49.14269,-1.59593 319 | 2021-12-09 21:17:00+00:00,47722.11,47742.48,47707.33,47742.48,19.72314,5.6831 320 | 2021-12-09 21:18:00+00:00,47742.47,47889.76,47742.47,47877.49,45.41375,18.62787 321 | 2021-12-09 21:19:00+00:00,47880.12,47936.06,47850.8,47866.59,37.02553,-2.13961 322 | 2021-12-09 21:20:00+00:00,47866.64,47915.0,47830.02,47845.67,40.28473,-10.19813 323 | 2021-12-09 21:21:00+00:00,47845.68,47872.34,47791.24,47857.5,22.02304,-2.08842 324 | 2021-12-09 21:22:00+00:00,47857.5,47857.51,47829.49,47846.58,13.41622,3.52582 325 | 2021-12-09 21:23:00+00:00,47846.58,47916.44,47750.34,47804.58,86.81257,-41.08549 326 | 2021-12-09 21:24:00+00:00,47804.59,47821.37,47768.77,47791.47,39.37471,19.63261 327 | 2021-12-09 21:25:00+00:00,47790.0,47798.69,47750.09,47753.06,9.70395,-2.16847 328 | 2021-12-09 21:26:00+00:00,47753.08,47823.27,47743.59,47804.87,28.69929,2.41587 329 | 2021-12-09 21:27:00+00:00,47801.53,47947.19,47801.53,47924.73,29.55061,14.09801 330 | 2021-12-09 21:28:00+00:00,47924.73,47968.86,47913.33,47917.26,30.93005,3.76639 331 | 2021-12-09 21:29:00+00:00,47917.27,47917.27,47851.7,47881.96,11.08948,-0.73298 332 | 2021-12-09 21:30:00+00:00,47881.96,47897.26,47836.42,47883.82,10.01128,-0.49386 333 | 2021-12-09 21:31:00+00:00,47883.81,47883.82,47841.61,47847.89,11.94444,-4.51354 334 | 2021-12-09 21:32:00+00:00,47847.89,47866.25,47791.42,47828.23,26.71098,-6.93136 335 | 2021-12-09 21:33:00+00:00,47828.24,47838.99,47819.83,47826.93,11.8311,0.6654 336 | 2021-12-09 21:34:00+00:00,47824.35,47831.96,47803.34,47831.95,9.0322,0.18292 337 | 2021-12-09 21:35:00+00:00,47831.95,47856.34,47734.95,47745.4,44.49419,5.53467 338 | 2021-12-09 21:36:00+00:00,47745.4,47783.93,47745.4,47777.82,36.50914,10.56024 339 | 2021-12-09 21:37:00+00:00,47777.82,47817.89,47777.81,47809.75,31.19494,24.01796 340 | 2021-12-09 21:38:00+00:00,47809.76,47855.17,47800.0,47816.89,12.798,-2.1047 341 | 2021-12-09 21:39:00+00:00,47816.88,47925.05,47816.88,47914.8,31.26477,14.21731 342 | 2021-12-09 21:40:00+00:00,47914.81,47948.57,47895.09,47910.98,39.69053,-7.87027 343 | 2021-12-09 21:41:00+00:00,47910.98,47910.99,47864.33,47868.44,13.39162,-2.33686 344 | 2021-12-09 21:42:00+00:00,47868.44,48040.0,47861.75,47982.54,52.3262,-0.0994 345 | 2021-12-09 21:43:00+00:00,47982.54,48032.09,47951.72,48016.11,17.38277,5.00515 346 | 2021-12-09 21:44:00+00:00,48016.11,48111.0,48016.1,48110.99,46.65428,11.00204 347 | 2021-12-09 21:45:00+00:00,48111.0,48141.18,48053.34,48092.43,58.01201,6.82461 348 | 2021-12-09 21:46:00+00:00,48092.43,48139.7,48062.49,48101.85,56.64367,-12.99937 349 | 2021-12-09 21:47:00+00:00,48101.85,48101.86,48012.99,48022.4,22.17756,-10.66244 350 | 2021-12-09 21:48:00+00:00,48027.21,48029.99,47966.81,47994.29,36.71756,5.15286 351 | 2021-12-09 21:49:00+00:00,47994.29,48067.51,47982.49,48053.47,26.82734,14.54048 352 | 2021-12-09 21:50:00+00:00,48047.01,48084.82,48018.38,48038.6,19.35252,-2.0074 353 | 2021-12-09 21:51:00+00:00,48038.61,48100.0,48036.03,48100.0,18.53592,2.73984 354 | 2021-12-09 21:52:00+00:00,48100.0,48110.93,48035.0,48045.36,21.73614,-2.16454 355 | 2021-12-09 21:53:00+00:00,48045.37,48110.86,47986.55,48020.12,60.12551,31.48043 356 | 2021-12-09 21:54:00+00:00,48020.13,48076.97,48013.92,48054.14,13.72089,-0.91793 357 | 2021-12-09 21:55:00+00:00,48054.13,48054.14,48003.11,48012.24,14.14883,0.42463 358 | 2021-12-09 21:56:00+00:00,48008.23,48026.95,48000.7,48008.44,8.11234,-2.38478 359 | 2021-12-09 21:57:00+00:00,48008.44,48053.83,47986.36,48018.45,18.30073,2.98889 360 | 2021-12-09 21:58:00+00:00,48018.44,48018.5,47915.76,47969.21,42.21731,-14.96107 361 | 2021-12-09 21:59:00+00:00,47966.86,48011.99,47962.86,47980.29,26.82471,1.76781 362 | -------------------------------------------------------------------------------- /examples/data/sample_data_2.csv: -------------------------------------------------------------------------------- 1 | ,o,h,l,c,v,pot,pot_ask,pot_bid 2 | 2021-12-09 16:00:00+00:00,48462.08,48471.09,48400.25,48421.05,33.19742,755.0,353.0,402.0 3 | 2021-12-09 16:01:00+00:00,48421.06,48477.67,48400.0,48451.97,30.88797,941.0,469.0,472.0 4 | 2021-12-09 16:02:00+00:00,48451.96,48499.52,48430.95,48450.0,34.58077,946.0,436.0,510.0 5 | 2021-12-09 16:03:00+00:00,48450.0,48458.6,48359.17,48369.67,36.80515,1003.0,467.0,536.0 6 | 2021-12-09 16:04:00+00:00,48369.67,48387.2,48300.0,48316.97,20.64135,825.0,350.0,475.0 7 | 2021-12-09 16:05:00+00:00,48316.97,48383.62,48316.96,48321.76,19.85662,651.0,355.0,296.0 8 | 2021-12-09 16:06:00+00:00,48321.77,48429.44,48312.0,48409.06,30.80138,793.0,427.0,366.0 9 | 2021-12-09 16:07:00+00:00,48409.06,48462.15,48404.38,48425.65,17.92321,704.0,372.0,332.0 10 | 2021-12-09 16:08:00+00:00,48425.64,48434.0,48385.9,48392.0,26.27059,701.0,316.0,385.0 11 | 2021-12-09 16:09:00+00:00,48390.99,48414.0,48373.11,48406.96,15.38413,613.0,325.0,288.0 12 | 2021-12-09 16:10:00+00:00,48404.17,48450.0,48398.26,48437.24,12.46371,680.0,330.0,350.0 13 | 2021-12-09 16:11:00+00:00,48437.25,48449.51,48386.03,48398.25,23.45167,640.0,247.0,393.0 14 | 2021-12-09 16:12:00+00:00,48398.25,48398.28,48347.6,48350.0,13.2289,521.0,258.0,263.0 15 | 2021-12-09 16:13:00+00:00,48350.0,48365.0,48251.0,48282.4,35.8989,951.0,376.0,575.0 16 | 2021-12-09 16:14:00+00:00,48282.4,48321.0,48256.01,48271.68,20.20307,606.0,301.0,305.0 17 | 2021-12-09 16:15:00+00:00,48271.68,48326.36,48242.76,48326.36,22.6587,680.0,331.0,349.0 18 | 2021-12-09 16:16:00+00:00,48326.36,48335.14,48272.81,48277.99,16.56836,606.0,305.0,301.0 19 | 2021-12-09 16:17:00+00:00,48278.0,48360.34,48150.61,48338.51,121.49389,1866.0,809.0,1057.0 20 | 2021-12-09 16:18:00+00:00,48338.52,48415.0,48326.93,48394.48,41.79241,689.0,339.0,350.0 21 | 2021-12-09 16:19:00+00:00,48394.48,48428.8,48380.16,48402.83,16.18288,642.0,327.0,315.0 22 | 2021-12-09 16:20:00+00:00,48402.82,48421.43,48350.02,48354.86,19.63573,639.0,309.0,330.0 23 | 2021-12-09 16:21:00+00:00,48354.86,48388.11,48334.0,48385.69,20.84247,604.0,304.0,300.0 24 | 2021-12-09 16:22:00+00:00,48385.69,48421.17,48377.39,48403.12,22.67236,527.0,289.0,238.0 25 | 2021-12-09 16:23:00+00:00,48403.11,48425.0,48370.28,48420.63,36.84737,665.0,321.0,344.0 26 | 2021-12-09 16:24:00+00:00,48420.64,48476.77,48412.69,48463.04,19.7659,595.0,312.0,283.0 27 | 2021-12-09 16:25:00+00:00,48463.04,48482.02,48425.64,48427.06,16.57488,559.0,272.0,287.0 28 | 2021-12-09 16:26:00+00:00,48427.06,48443.87,48400.49,48415.99,10.70349,535.0,236.0,299.0 29 | 2021-12-09 16:27:00+00:00,48415.98,48500.0,48415.98,48492.82,19.29575,628.0,331.0,297.0 30 | 2021-12-09 16:28:00+00:00,48492.82,48599.28,48481.11,48569.48,59.31386,1074.0,602.0,472.0 31 | 2021-12-09 16:29:00+00:00,48569.49,48575.29,48528.01,48550.0,24.11346,692.0,333.0,359.0 32 | 2021-12-09 16:30:00+00:00,48544.45,48570.36,48500.0,48560.52,20.55332,692.0,333.0,359.0 33 | 2021-12-09 16:31:00+00:00,48560.52,48668.82,48560.51,48613.0,65.25778,1302.0,698.0,604.0 34 | 2021-12-09 16:32:00+00:00,48612.99,48657.07,48603.01,48647.0,17.88621,605.0,275.0,330.0 35 | 2021-12-09 16:33:00+00:00,48645.99,48647.33,48604.07,48629.99,22.74937,736.0,324.0,412.0 36 | 2021-12-09 16:34:00+00:00,48630.0,48648.72,48600.04,48618.75,26.66594,675.0,337.0,338.0 37 | 2021-12-09 16:35:00+00:00,48618.75,48638.11,48581.24,48592.1,21.74366,569.0,297.0,272.0 38 | 2021-12-09 16:36:00+00:00,48592.1,48649.0,48573.01,48647.73,15.04572,596.0,299.0,297.0 39 | 2021-12-09 16:37:00+00:00,48647.74,48647.74,48604.96,48634.03,14.38838,612.0,307.0,305.0 40 | 2021-12-09 16:38:00+00:00,48634.03,48635.97,48608.5,48617.59,8.88425,463.0,236.0,227.0 41 | 2021-12-09 16:39:00+00:00,48617.58,48729.92,48617.58,48729.91,20.26567,791.0,514.0,277.0 42 | 2021-12-09 16:40:00+00:00,48729.92,48838.29,48700.01,48713.78,129.73702,2121.0,1048.0,1073.0 43 | 2021-12-09 16:41:00+00:00,48711.23,48795.74,48711.22,48740.01,30.41867,788.0,353.0,435.0 44 | 2021-12-09 16:42:00+00:00,48740.01,48743.71,48687.6,48700.36,26.43937,676.0,274.0,402.0 45 | 2021-12-09 16:43:00+00:00,48700.36,48710.28,48665.49,48665.49,15.98573,542.0,278.0,264.0 46 | 2021-12-09 16:44:00+00:00,48665.5,48704.16,48665.49,48670.01,24.59263,581.0,308.0,273.0 47 | 2021-12-09 16:45:00+00:00,48670.01,48683.54,48580.0,48592.6,22.37004,679.0,315.0,364.0 48 | 2021-12-09 16:46:00+00:00,48592.6,48680.54,48592.6,48653.36,36.48524,687.0,432.0,255.0 49 | 2021-12-09 16:47:00+00:00,48653.37,48695.77,48650.0,48670.09,16.14957,419.0,187.0,232.0 50 | 2021-12-09 16:48:00+00:00,48670.1,48698.9,48613.44,48689.27,102.37469,952.0,410.0,542.0 51 | 2021-12-09 16:49:00+00:00,48689.28,48702.09,48654.97,48656.87,18.97107,511.0,264.0,247.0 52 | 2021-12-09 16:50:00+00:00,48654.97,48699.99,48650.75,48681.73,13.01653,485.0,236.0,249.0 53 | 2021-12-09 16:51:00+00:00,48681.72,48681.73,48643.86,48660.01,18.01154,707.0,296.0,411.0 54 | 2021-12-09 16:52:00+00:00,48660.01,48699.98,48660.0,48693.73,28.94981,705.0,374.0,331.0 55 | 2021-12-09 16:53:00+00:00,48693.73,48696.13,48613.44,48636.84,63.12403,779.0,327.0,452.0 56 | 2021-12-09 16:54:00+00:00,48636.84,48680.78,48629.99,48672.13,26.98044,629.0,337.0,292.0 57 | 2021-12-09 16:55:00+00:00,48672.13,48706.64,48659.35,48695.75,16.35741,597.0,306.0,291.0 58 | 2021-12-09 16:56:00+00:00,48695.75,48699.02,48650.0,48659.82,18.7983,424.0,127.0,297.0 59 | 2021-12-09 16:57:00+00:00,48663.71,48678.94,48612.38,48617.8,23.21013,585.0,324.0,261.0 60 | 2021-12-09 16:58:00+00:00,48617.8,48638.63,48529.41,48562.67,23.36645,659.0,257.0,402.0 61 | 2021-12-09 16:59:00+00:00,48562.68,48571.1,48504.97,48504.98,11.13805,409.0,196.0,213.0 62 | 2021-12-09 17:00:00+00:00,48504.97,48539.98,48461.42,48511.09,52.13817,987.0,397.0,590.0 63 | 2021-12-09 17:01:00+00:00,48511.09,48572.98,48511.08,48559.57,21.4514,780.0,487.0,293.0 64 | 2021-12-09 17:02:00+00:00,48559.58,48683.43,48538.63,48657.95,66.31384,1256.0,758.0,498.0 65 | 2021-12-09 17:03:00+00:00,48657.03,48705.15,48612.34,48623.31,37.97775,822.0,366.0,456.0 66 | 2021-12-09 17:04:00+00:00,48623.31,48641.29,48615.82,48623.75,9.5125,457.0,196.0,261.0 67 | 2021-12-09 17:05:00+00:00,48623.74,48623.75,48573.69,48580.38,9.45786,455.0,203.0,252.0 68 | 2021-12-09 17:06:00+00:00,48580.39,48600.55,48560.0,48600.54,19.27907,461.0,228.0,233.0 69 | 2021-12-09 17:07:00+00:00,48600.54,48628.62,48600.54,48624.59,8.81892,402.0,197.0,205.0 70 | 2021-12-09 17:08:00+00:00,48624.6,48641.14,48610.0,48610.0,12.9581,379.0,162.0,217.0 71 | 2021-12-09 17:09:00+00:00,48610.0,48629.22,48610.0,48614.53,11.09988,398.0,184.0,214.0 72 | 2021-12-09 17:10:00+00:00,48610.86,48620.49,48571.38,48603.01,23.50961,532.0,222.0,310.0 73 | 2021-12-09 17:11:00+00:00,48603.01,48633.85,48598.16,48629.28,19.43852,499.0,186.0,313.0 74 | 2021-12-09 17:12:00+00:00,48629.28,48659.52,48607.26,48607.26,11.09724,396.0,184.0,212.0 75 | 2021-12-09 17:13:00+00:00,48607.26,48636.22,48592.01,48605.12,25.92299,566.0,287.0,279.0 76 | 2021-12-09 17:14:00+00:00,48605.12,48653.99,48605.12,48640.43,19.12718,543.0,253.0,290.0 77 | 2021-12-09 17:15:00+00:00,48640.44,48645.75,48600.02,48612.28,10.31785,494.0,222.0,272.0 78 | 2021-12-09 17:16:00+00:00,48612.27,48666.66,48610.0,48666.65,10.72669,376.0,179.0,197.0 79 | 2021-12-09 17:17:00+00:00,48666.66,48698.9,48663.96,48698.89,15.01342,431.0,233.0,198.0 80 | 2021-12-09 17:18:00+00:00,48698.9,48698.9,48600.72,48611.31,18.48362,539.0,262.0,277.0 81 | 2021-12-09 17:19:00+00:00,48611.31,48626.16,48606.86,48617.79,4.14046,295.0,141.0,154.0 82 | 2021-12-09 17:20:00+00:00,48617.78,48617.79,48584.38,48600.77,8.4233,351.0,158.0,193.0 83 | 2021-12-09 17:21:00+00:00,48600.78,48600.78,48553.36,48578.21,15.29689,439.0,166.0,273.0 84 | 2021-12-09 17:22:00+00:00,48578.21,48626.03,48570.39,48594.63,15.1639,570.0,319.0,251.0 85 | 2021-12-09 17:23:00+00:00,48594.64,48600.01,48571.01,48593.08,17.27131,500.0,234.0,266.0 86 | 2021-12-09 17:24:00+00:00,48593.08,48650.0,48585.99,48638.46,18.03648,522.0,282.0,240.0 87 | 2021-12-09 17:25:00+00:00,48636.57,48691.92,48621.0,48676.8,41.18835,822.0,480.0,342.0 88 | 2021-12-09 17:26:00+00:00,48676.8,48678.97,48581.01,48585.02,14.07153,449.0,199.0,250.0 89 | 2021-12-09 17:27:00+00:00,48585.02,48593.04,48540.0,48551.31,14.98182,490.0,182.0,308.0 90 | 2021-12-09 17:28:00+00:00,48550.73,48563.14,48540.3,48562.75,9.69964,448.0,200.0,248.0 91 | 2021-12-09 17:29:00+00:00,48559.55,48580.01,48559.54,48560.0,9.85318,362.0,157.0,205.0 92 | 2021-12-09 17:30:00+00:00,48560.0,48560.01,48461.42,48488.89,24.36224,760.0,304.0,456.0 93 | 2021-12-09 17:31:00+00:00,48492.38,48492.38,48473.67,48488.86,11.46918,474.0,252.0,222.0 94 | 2021-12-09 17:32:00+00:00,48488.87,48525.61,48486.49,48520.03,17.84398,556.0,315.0,241.0 95 | 2021-12-09 17:33:00+00:00,48520.03,48568.05,48508.62,48567.29,22.84265,606.0,344.0,262.0 96 | 2021-12-09 17:34:00+00:00,48567.29,48568.05,48515.11,48533.69,9.77684,329.0,153.0,176.0 97 | 2021-12-09 17:35:00+00:00,48533.69,48574.99,48520.19,48525.43,22.73426,536.0,219.0,317.0 98 | 2021-12-09 17:36:00+00:00,48529.7,48554.87,48519.05,48539.49,16.01484,437.0,256.0,181.0 99 | 2021-12-09 17:37:00+00:00,48539.49,48539.5,48491.12,48491.12,12.52299,399.0,179.0,220.0 100 | 2021-12-09 17:38:00+00:00,48491.13,48495.17,48426.25,48453.29,38.92443,792.0,430.0,362.0 101 | 2021-12-09 17:39:00+00:00,48451.97,48463.25,48411.24,48448.78,30.35585,678.0,254.0,424.0 102 | 2021-12-09 17:40:00+00:00,48448.77,48479.34,48435.57,48459.78,28.02667,564.0,300.0,264.0 103 | 2021-12-09 17:41:00+00:00,48459.77,48544.54,48458.19,48502.91,36.48446,649.0,342.0,307.0 104 | 2021-12-09 17:42:00+00:00,48502.91,48542.51,48476.53,48505.84,17.81188,620.0,295.0,325.0 105 | 2021-12-09 17:43:00+00:00,48505.84,48507.03,48440.16,48445.93,18.02415,547.0,242.0,305.0 106 | 2021-12-09 17:44:00+00:00,48445.93,48453.31,48400.0,48403.51,10.69841,575.0,244.0,331.0 107 | 2021-12-09 17:45:00+00:00,48403.48,48465.97,48403.47,48428.69,26.53248,555.0,296.0,259.0 108 | 2021-12-09 17:46:00+00:00,48428.69,48431.62,48413.61,48426.78,5.42267,368.0,214.0,154.0 109 | 2021-12-09 17:47:00+00:00,48426.78,48471.05,48424.06,48427.39,10.96142,457.0,266.0,191.0 110 | 2021-12-09 17:48:00+00:00,48427.39,48449.3,48427.09,48438.95,10.53923,433.0,202.0,231.0 111 | 2021-12-09 17:49:00+00:00,48438.95,48462.01,48437.62,48455.96,12.00467,489.0,225.0,264.0 112 | 2021-12-09 17:50:00+00:00,48455.97,48463.75,48416.51,48417.57,13.75201,579.0,254.0,325.0 113 | 2021-12-09 17:51:00+00:00,48417.57,48507.74,48404.16,48491.82,33.16287,883.0,451.0,432.0 114 | 2021-12-09 17:52:00+00:00,48491.83,48520.07,48426.76,48438.2,22.19856,802.0,391.0,411.0 115 | 2021-12-09 17:53:00+00:00,48438.21,48460.04,48427.7,48432.6,15.23131,559.0,336.0,223.0 116 | 2021-12-09 17:54:00+00:00,48432.6,48443.64,48360.0,48360.0,24.51334,819.0,369.0,450.0 117 | 2021-12-09 17:55:00+00:00,48360.0,48383.58,48350.07,48381.72,15.31994,656.0,313.0,343.0 118 | 2021-12-09 17:56:00+00:00,48381.72,48381.72,48300.9,48306.01,19.25495,808.0,348.0,460.0 119 | 2021-12-09 17:57:00+00:00,48306.01,48335.08,48272.57,48293.18,26.27427,1116.0,494.0,622.0 120 | 2021-12-09 17:58:00+00:00,48293.19,48306.0,48125.0,48127.99,44.72591,1402.0,543.0,859.0 121 | 2021-12-09 17:59:00+00:00,48127.99,48205.56,48127.99,48171.65,50.30446,1605.0,775.0,830.0 122 | 2021-12-09 18:00:00+00:00,48171.66,48265.31,48171.65,48265.31,39.33272,1075.0,601.0,474.0 123 | 2021-12-09 18:01:00+00:00,48261.6,48292.58,48230.0,48255.99,38.34556,827.0,468.0,359.0 124 | 2021-12-09 18:02:00+00:00,48255.99,48300.0,48233.92,48293.52,20.94682,732.0,435.0,297.0 125 | 2021-12-09 18:03:00+00:00,48293.39,48363.42,48277.0,48289.97,53.76145,843.0,488.0,355.0 126 | 2021-12-09 18:04:00+00:00,48289.97,48289.98,48142.08,48220.57,36.78634,789.0,304.0,485.0 127 | 2021-12-09 18:05:00+00:00,48220.58,48264.42,48162.65,48192.99,15.03328,527.0,232.0,295.0 128 | 2021-12-09 18:06:00+00:00,48192.98,48239.77,48181.94,48203.21,19.90815,539.0,221.0,318.0 129 | 2021-12-09 18:07:00+00:00,48203.21,48300.0,48203.21,48279.85,17.02044,579.0,277.0,302.0 130 | 2021-12-09 18:08:00+00:00,48279.84,48300.0,48269.8,48269.8,14.27011,447.0,176.0,271.0 131 | 2021-12-09 18:09:00+00:00,48269.8,48333.28,48269.8,48322.5,11.64713,519.0,263.0,256.0 132 | 2021-12-09 18:10:00+00:00,48322.5,48350.0,48276.3,48300.23,11.72683,443.0,191.0,252.0 133 | 2021-12-09 18:11:00+00:00,48300.23,48311.23,48270.0,48274.31,10.31554,479.0,209.0,270.0 134 | 2021-12-09 18:12:00+00:00,48274.32,48283.05,48236.68,48259.18,10.04726,432.0,178.0,254.0 135 | 2021-12-09 18:13:00+00:00,48259.18,48259.21,48200.0,48200.07,11.94605,431.0,175.0,256.0 136 | 2021-12-09 18:14:00+00:00,48200.06,48267.19,48200.0,48260.0,14.43437,506.0,242.0,264.0 137 | 2021-12-09 18:15:00+00:00,48259.99,48315.5,48205.09,48247.88,35.83551,790.0,374.0,416.0 138 | 2021-12-09 18:16:00+00:00,48247.88,48350.0,48240.88,48344.39,23.39579,690.0,343.0,347.0 139 | 2021-12-09 18:17:00+00:00,48344.38,48350.0,48302.04,48308.95,23.02273,441.0,212.0,229.0 140 | 2021-12-09 18:18:00+00:00,48308.96,48348.87,48277.53,48292.3,25.53056,618.0,271.0,347.0 141 | 2021-12-09 18:19:00+00:00,48289.17,48321.4,48262.22,48274.6,8.9135,453.0,173.0,280.0 142 | 2021-12-09 18:20:00+00:00,48274.6,48307.0,48254.05,48276.79,10.47728,467.0,219.0,248.0 143 | 2021-12-09 18:21:00+00:00,48276.78,48276.79,48160.0,48171.0,24.73562,771.0,267.0,504.0 144 | 2021-12-09 18:22:00+00:00,48170.99,48171.0,48118.05,48150.0,31.25459,1009.0,432.0,577.0 145 | 2021-12-09 18:23:00+00:00,48150.0,48157.0,48000.0,48121.66,159.62083,2936.0,1116.0,1820.0 146 | 2021-12-09 18:24:00+00:00,48121.67,48193.99,48055.41,48138.3,68.24915,1401.0,710.0,691.0 147 | 2021-12-09 18:25:00+00:00,48138.29,48180.71,48050.59,48066.13,72.45619,1242.0,601.0,641.0 148 | 2021-12-09 18:26:00+00:00,48066.13,48072.24,47885.0,47964.25,233.24593,3729.0,1546.0,2183.0 149 | 2021-12-09 18:27:00+00:00,47964.25,47985.96,47547.93,47806.38,360.72372,6658.0,3098.0,3560.0 150 | 2021-12-09 18:28:00+00:00,47813.81,47855.52,47678.0,47817.0,98.8646,2625.0,1602.0,1023.0 151 | 2021-12-09 18:29:00+00:00,47819.8,47985.3,47791.0,47884.73,195.81371,2181.0,1107.0,1074.0 152 | 2021-12-09 18:30:00+00:00,47884.72,47885.9,47796.84,47838.34,66.00139,1134.0,479.0,655.0 153 | 2021-12-09 18:31:00+00:00,47838.36,47863.02,47750.01,47755.01,29.56819,943.0,490.0,453.0 154 | 2021-12-09 18:32:00+00:00,47755.01,47859.02,47755.0,47810.04,37.83084,975.0,571.0,404.0 155 | 2021-12-09 18:33:00+00:00,47810.04,47826.99,47694.32,47694.32,53.21158,1246.0,580.0,666.0 156 | 2021-12-09 18:34:00+00:00,47694.32,47767.11,47556.35,47667.3,141.87722,2323.0,1079.0,1244.0 157 | 2021-12-09 18:35:00+00:00,47667.3,47683.45,47537.26,47565.69,85.86911,1689.0,689.0,1000.0 158 | 2021-12-09 18:36:00+00:00,47570.85,47645.25,47565.69,47634.75,55.75349,1220.0,701.0,519.0 159 | 2021-12-09 18:37:00+00:00,47634.75,47748.96,47610.47,47689.33,30.97011,1156.0,625.0,531.0 160 | 2021-12-09 18:38:00+00:00,47689.33,47757.9,47689.33,47734.28,40.10801,946.0,555.0,391.0 161 | 2021-12-09 18:39:00+00:00,47734.29,47843.07,47726.01,47771.78,70.14363,1364.0,821.0,543.0 162 | 2021-12-09 18:40:00+00:00,47771.78,47778.01,47668.05,47748.55,50.88067,860.0,429.0,431.0 163 | 2021-12-09 18:41:00+00:00,47748.55,47748.56,47680.0,47685.66,15.7938,582.0,312.0,270.0 164 | 2021-12-09 18:42:00+00:00,47685.65,47733.47,47650.55,47668.6,23.09799,664.0,336.0,328.0 165 | 2021-12-09 18:43:00+00:00,47668.6,47745.27,47657.22,47714.12,39.68717,793.0,393.0,400.0 166 | 2021-12-09 18:44:00+00:00,47714.13,47743.61,47683.0,47683.01,32.80538,914.0,501.0,413.0 167 | 2021-12-09 18:45:00+00:00,47683.0,47773.87,47660.38,47743.84,37.98497,985.0,538.0,447.0 168 | 2021-12-09 18:46:00+00:00,47743.84,47743.84,47672.62,47686.35,21.27299,697.0,317.0,380.0 169 | 2021-12-09 18:47:00+00:00,47686.35,47705.42,47570.49,47614.95,49.8062,1232.0,548.0,684.0 170 | 2021-12-09 18:48:00+00:00,47614.95,47614.96,47520.0,47566.72,65.29844,1661.0,729.0,932.0 171 | 2021-12-09 18:49:00+00:00,47566.73,47649.99,47546.92,47601.38,48.71767,1384.0,706.0,678.0 172 | 2021-12-09 18:50:00+00:00,47601.38,47608.99,47510.0,47516.78,60.74578,1463.0,717.0,746.0 173 | 2021-12-09 18:51:00+00:00,47516.78,47555.0,47510.0,47532.64,48.1163,1471.0,840.0,631.0 174 | 2021-12-09 18:52:00+00:00,47532.65,47553.75,47500.0,47542.98,64.63464,1525.0,652.0,873.0 175 | 2021-12-09 18:53:00+00:00,47542.98,47632.2,47530.01,47606.41,36.34047,1019.0,551.0,468.0 176 | 2021-12-09 18:54:00+00:00,47606.42,47686.85,47606.41,47631.04,54.49522,1234.0,685.0,549.0 177 | 2021-12-09 18:55:00+00:00,47631.04,47684.84,47609.39,47665.02,20.17286,726.0,427.0,299.0 178 | 2021-12-09 18:56:00+00:00,47665.01,47814.61,47665.01,47745.25,75.77455,1817.0,1081.0,736.0 179 | 2021-12-09 18:57:00+00:00,47745.25,47839.0,47701.21,47839.0,36.90415,944.0,488.0,456.0 180 | 2021-12-09 18:58:00+00:00,47838.99,47850.0,47770.45,47791.54,57.01794,891.0,424.0,467.0 181 | 2021-12-09 18:59:00+00:00,47793.94,47793.94,47714.98,47753.24,28.75472,680.0,352.0,328.0 182 | 2021-12-09 19:00:00+00:00,47751.87,47823.92,47715.01,47800.0,35.31876,838.0,425.0,413.0 183 | 2021-12-09 19:01:00+00:00,47800.0,47888.28,47799.99,47850.0,46.78706,856.0,517.0,339.0 184 | 2021-12-09 19:02:00+00:00,47850.01,47858.63,47810.0,47817.53,22.00075,648.0,354.0,294.0 185 | 2021-12-09 19:03:00+00:00,47817.52,47817.53,47780.0,47791.41,17.35652,530.0,276.0,254.0 186 | 2021-12-09 19:04:00+00:00,47791.4,47808.73,47744.04,47774.62,30.56867,720.0,365.0,355.0 187 | 2021-12-09 19:05:00+00:00,47774.62,47800.04,47763.11,47768.22,8.33402,495.0,216.0,279.0 188 | 2021-12-09 19:06:00+00:00,47763.11,47863.36,47760.0,47847.19,27.56449,655.0,402.0,253.0 189 | 2021-12-09 19:07:00+00:00,47847.19,47886.0,47835.56,47870.26,20.95855,628.0,363.0,265.0 190 | 2021-12-09 19:08:00+00:00,47870.27,47986.62,47850.11,47915.91,41.11089,1037.0,660.0,377.0 191 | 2021-12-09 19:09:00+00:00,47924.25,48018.63,47784.22,47847.52,130.32771,2269.0,1037.0,1232.0 192 | 2021-12-09 19:10:00+00:00,47847.52,47847.53,47653.14,47653.14,39.88889,1109.0,488.0,621.0 193 | 2021-12-09 19:11:00+00:00,47653.14,47705.7,47600.68,47657.2,50.0431,985.0,503.0,482.0 194 | 2021-12-09 19:12:00+00:00,47657.21,47710.55,47617.37,47693.22,40.13704,778.0,429.0,349.0 195 | 2021-12-09 19:13:00+00:00,47693.22,47700.21,47623.37,47640.28,25.65562,763.0,338.0,425.0 196 | 2021-12-09 19:14:00+00:00,47640.29,47654.75,47614.0,47648.23,15.05372,495.0,253.0,242.0 197 | 2021-12-09 19:15:00+00:00,47648.22,47649.98,47552.0,47636.79,46.39269,889.0,404.0,485.0 198 | 2021-12-09 19:16:00+00:00,47636.8,47690.16,47617.86,47665.44,18.22909,677.0,382.0,295.0 199 | 2021-12-09 19:17:00+00:00,47665.44,47742.49,47657.8,47716.93,35.21191,783.0,501.0,282.0 200 | 2021-12-09 19:18:00+00:00,47721.62,47800.0,47716.93,47791.11,29.216,713.0,405.0,308.0 201 | 2021-12-09 19:19:00+00:00,47791.11,47791.11,47712.68,47743.27,22.61945,561.0,283.0,278.0 202 | 2021-12-09 19:20:00+00:00,47743.26,47794.5,47735.75,47772.19,14.90552,559.0,306.0,253.0 203 | 2021-12-09 19:21:00+00:00,47772.19,47778.74,47710.4,47714.76,14.28422,603.0,259.0,344.0 204 | 2021-12-09 19:22:00+00:00,47714.75,47761.93,47678.97,47749.33,13.09988,528.0,281.0,247.0 205 | 2021-12-09 19:23:00+00:00,47747.63,47848.9,47718.42,47848.9,24.8379,717.0,405.0,312.0 206 | 2021-12-09 19:24:00+00:00,47848.9,47862.01,47785.89,47830.8,27.87156,667.0,317.0,350.0 207 | 2021-12-09 19:25:00+00:00,47830.8,47840.53,47776.0,47825.43,14.31423,530.0,262.0,268.0 208 | 2021-12-09 19:26:00+00:00,47825.43,47825.43,47767.53,47776.19,14.60979,431.0,179.0,252.0 209 | 2021-12-09 19:27:00+00:00,47776.2,47815.11,47767.03,47767.04,19.64111,494.0,298.0,196.0 210 | 2021-12-09 19:28:00+00:00,47767.03,47800.0,47765.5,47789.76,11.61942,439.0,208.0,231.0 211 | 2021-12-09 19:29:00+00:00,47789.79,47792.18,47764.01,47783.52,13.6047,468.0,246.0,222.0 212 | 2021-12-09 19:30:00+00:00,47783.53,47839.49,47783.53,47829.77,22.17437,554.0,292.0,262.0 213 | 2021-12-09 19:31:00+00:00,47829.83,47850.0,47760.0,47760.0,19.54458,549.0,252.0,297.0 214 | 2021-12-09 19:32:00+00:00,47760.0,47785.79,47721.72,47744.79,19.80299,533.0,245.0,288.0 215 | 2021-12-09 19:33:00+00:00,47744.79,47802.56,47725.64,47790.79,13.24541,442.0,247.0,195.0 216 | 2021-12-09 19:34:00+00:00,47790.81,47873.08,47779.0,47828.95,29.12617,709.0,422.0,287.0 217 | 2021-12-09 19:35:00+00:00,47832.71,47843.36,47800.01,47811.15,16.61361,496.0,218.0,278.0 218 | 2021-12-09 19:36:00+00:00,47811.16,47850.06,47810.82,47830.0,13.34088,499.0,298.0,201.0 219 | 2021-12-09 19:37:00+00:00,47830.0,47930.0,47827.02,47918.01,30.75514,884.0,479.0,405.0 220 | 2021-12-09 19:38:00+00:00,47918.0,47924.01,47866.76,47883.74,13.76087,579.0,340.0,239.0 221 | 2021-12-09 19:39:00+00:00,47883.73,47919.99,47880.02,47900.0,23.37856,560.0,313.0,247.0 222 | 2021-12-09 19:40:00+00:00,47900.0,47937.79,47899.99,47924.16,19.53532,510.0,236.0,274.0 223 | 2021-12-09 19:41:00+00:00,47924.16,47963.15,47914.92,47918.28,51.2301,746.0,351.0,395.0 224 | 2021-12-09 19:42:00+00:00,47918.28,47918.28,47805.94,47812.99,35.80958,805.0,286.0,519.0 225 | 2021-12-09 19:43:00+00:00,47813.0,47818.39,47756.0,47768.16,17.55094,674.0,280.0,394.0 226 | 2021-12-09 19:44:00+00:00,47765.0,47858.74,47724.86,47771.51,65.8107,1026.0,501.0,525.0 227 | 2021-12-09 19:45:00+00:00,47774.88,47796.87,47720.83,47734.38,29.40851,669.0,371.0,298.0 228 | 2021-12-09 19:46:00+00:00,47734.38,47788.5,47707.38,47754.63,33.63215,718.0,305.0,413.0 229 | 2021-12-09 19:47:00+00:00,47754.64,47785.35,47721.72,47737.29,22.81776,528.0,262.0,266.0 230 | 2021-12-09 19:48:00+00:00,47737.28,47768.52,47730.22,47741.69,17.26888,468.0,214.0,254.0 231 | 2021-12-09 19:49:00+00:00,47741.7,47832.3,47731.58,47832.12,21.46646,551.0,350.0,201.0 232 | 2021-12-09 19:50:00+00:00,47832.12,47861.42,47805.37,47825.35,30.15849,492.0,241.0,251.0 233 | 2021-12-09 19:51:00+00:00,47825.34,47864.41,47822.25,47839.91,16.78082,458.0,224.0,234.0 234 | 2021-12-09 19:52:00+00:00,47839.91,47866.21,47761.09,47791.86,32.88954,624.0,224.0,400.0 235 | 2021-12-09 19:53:00+00:00,47791.85,47794.99,47750.79,47774.88,13.56606,422.0,231.0,191.0 236 | 2021-12-09 19:54:00+00:00,47774.87,47774.88,47730.0,47739.92,16.26292,447.0,175.0,272.0 237 | 2021-12-09 19:55:00+00:00,47739.92,47750.0,47700.0,47700.0,7.83787,419.0,158.0,261.0 238 | 2021-12-09 19:56:00+00:00,47700.01,47704.92,47650.0,47664.08,30.64117,623.0,247.0,376.0 239 | 2021-12-09 19:57:00+00:00,47664.08,47707.13,47628.11,47700.0,29.99506,612.0,312.0,300.0 240 | 2021-12-09 19:58:00+00:00,47700.01,47723.9,47684.7,47684.71,13.30957,442.0,200.0,242.0 241 | 2021-12-09 19:59:00+00:00,47684.71,47687.43,47610.0,47613.53,19.37786,669.0,325.0,344.0 242 | 2021-12-09 20:00:00+00:00,47615.89,47639.99,47556.54,47622.66,32.48367,845.0,461.0,384.0 243 | 2021-12-09 20:01:00+00:00,47622.67,47711.0,47615.53,47701.42,25.2616,782.0,446.0,336.0 244 | 2021-12-09 20:02:00+00:00,47701.42,47702.0,47658.49,47662.57,16.97593,621.0,307.0,314.0 245 | 2021-12-09 20:03:00+00:00,47662.56,47699.21,47574.14,47600.0,27.45793,741.0,347.0,394.0 246 | 2021-12-09 20:04:00+00:00,47600.0,47618.93,47560.24,47560.24,26.17596,795.0,388.0,407.0 247 | 2021-12-09 20:05:00+00:00,47560.24,47593.44,47525.34,47546.41,54.96504,1022.0,459.0,563.0 248 | 2021-12-09 20:06:00+00:00,47550.05,47610.39,47520.22,47549.62,23.15354,696.0,320.0,376.0 249 | 2021-12-09 20:07:00+00:00,47549.61,47557.78,47500.22,47500.32,24.56703,743.0,337.0,406.0 250 | 2021-12-09 20:08:00+00:00,47500.32,47561.32,47464.0,47531.68,148.56746,1648.0,746.0,902.0 251 | 2021-12-09 20:09:00+00:00,47531.68,47531.69,47422.98,47474.84,46.3633,1192.0,554.0,638.0 252 | 2021-12-09 20:10:00+00:00,47474.83,47540.35,47466.13,47523.78,39.09088,900.0,495.0,405.0 253 | 2021-12-09 20:11:00+00:00,47523.79,47527.95,47422.98,47424.23,31.63602,844.0,446.0,398.0 254 | 2021-12-09 20:12:00+00:00,47424.24,47428.18,47373.34,47383.05,86.47801,1708.0,685.0,1023.0 255 | 2021-12-09 20:13:00+00:00,47383.05,47455.21,47359.61,47369.22,38.52295,1167.0,590.0,577.0 256 | 2021-12-09 20:14:00+00:00,47368.63,47426.97,47366.47,47398.99,23.3264,732.0,355.0,377.0 257 | 2021-12-09 20:15:00+00:00,47396.77,47515.6,47387.56,47484.0,33.34442,988.0,609.0,379.0 258 | 2021-12-09 20:16:00+00:00,47484.02,47640.0,47463.01,47610.0,63.41772,1313.0,779.0,534.0 259 | 2021-12-09 20:17:00+00:00,47610.01,47639.09,47540.92,47587.78,31.68534,756.0,400.0,356.0 260 | 2021-12-09 20:18:00+00:00,47587.77,47631.37,47581.77,47593.45,21.96321,529.0,312.0,217.0 261 | 2021-12-09 20:19:00+00:00,47593.45,47684.07,47516.78,47668.48,30.1008,749.0,416.0,333.0 262 | 2021-12-09 20:20:00+00:00,47668.48,47719.76,47600.0,47608.32,28.41382,677.0,358.0,319.0 263 | 2021-12-09 20:21:00+00:00,47608.32,47636.14,47557.31,47561.9,14.6481,499.0,282.0,217.0 264 | 2021-12-09 20:22:00+00:00,47561.9,47564.28,47510.23,47537.36,27.89854,711.0,354.0,357.0 265 | 2021-12-09 20:23:00+00:00,47537.35,47626.05,47517.58,47603.73,15.20852,491.0,262.0,229.0 266 | 2021-12-09 20:24:00+00:00,47603.73,47618.94,47547.03,47561.55,13.0622,433.0,247.0,186.0 267 | 2021-12-09 20:25:00+00:00,47561.54,47561.55,47415.57,47485.45,30.66192,844.0,422.0,422.0 268 | 2021-12-09 20:26:00+00:00,47485.44,47513.0,47428.01,47496.79,15.11078,468.0,243.0,225.0 269 | 2021-12-09 20:27:00+00:00,47496.8,47600.0,47488.74,47598.51,15.38874,451.0,270.0,181.0 270 | 2021-12-09 20:28:00+00:00,47599.99,47600.0,47545.32,47563.62,18.85786,540.0,274.0,266.0 271 | 2021-12-09 20:29:00+00:00,47564.41,47575.44,47513.43,47520.81,11.71755,400.0,207.0,193.0 272 | 2021-12-09 20:30:00+00:00,47520.8,47612.95,47475.43,47596.02,16.64406,553.0,275.0,278.0 273 | 2021-12-09 20:31:00+00:00,47595.99,47600.0,47539.59,47550.0,12.97183,382.0,196.0,186.0 274 | 2021-12-09 20:32:00+00:00,47550.01,47550.01,47500.0,47500.01,13.12036,388.0,137.0,251.0 275 | 2021-12-09 20:33:00+00:00,47500.0,47511.86,47420.61,47429.41,21.81849,539.0,246.0,293.0 276 | 2021-12-09 20:34:00+00:00,47433.89,47433.89,47390.0,47407.01,23.96631,573.0,276.0,297.0 277 | 2021-12-09 20:35:00+00:00,47407.01,47444.72,47390.01,47407.28,27.4922,544.0,273.0,271.0 278 | 2021-12-09 20:36:00+00:00,47407.27,47478.09,47333.0,47349.98,56.81844,1084.0,505.0,579.0 279 | 2021-12-09 20:37:00+00:00,47349.98,47398.64,47338.12,47384.68,18.65384,679.0,352.0,327.0 280 | 2021-12-09 20:38:00+00:00,47384.68,47471.14,47369.01,47431.52,28.99028,595.0,343.0,252.0 281 | 2021-12-09 20:39:00+00:00,47431.53,47475.58,47411.21,47466.91,26.54801,523.0,302.0,221.0 282 | 2021-12-09 20:40:00+00:00,47468.1,47471.92,47378.84,47378.85,11.74243,480.0,205.0,275.0 283 | 2021-12-09 20:41:00+00:00,47378.85,47416.24,47374.12,47386.23,13.3052,411.0,179.0,232.0 284 | 2021-12-09 20:42:00+00:00,47384.53,47394.1,47359.01,47370.0,11.69128,438.0,175.0,263.0 285 | 2021-12-09 20:43:00+00:00,47370.0,47394.0,47320.0,47375.45,24.46085,844.0,403.0,441.0 286 | 2021-12-09 20:44:00+00:00,47374.55,47413.54,47349.6,47375.43,26.76865,565.0,291.0,274.0 287 | 2021-12-09 20:45:00+00:00,47375.43,47395.73,47327.03,47330.99,28.49977,752.0,311.0,441.0 288 | 2021-12-09 20:46:00+00:00,47330.99,47411.18,47326.31,47400.27,33.36038,805.0,391.0,414.0 289 | 2021-12-09 20:47:00+00:00,47400.28,47434.98,47342.48,47426.16,26.48458,693.0,355.0,338.0 290 | 2021-12-09 20:48:00+00:00,47426.16,47577.35,47420.77,47472.43,56.4821,1322.0,745.0,577.0 291 | 2021-12-09 20:49:00+00:00,47468.03,47480.47,47393.22,47406.91,24.33013,734.0,404.0,330.0 292 | 2021-12-09 20:50:00+00:00,47406.92,47425.45,47361.93,47388.39,22.62333,709.0,354.0,355.0 293 | 2021-12-09 20:51:00+00:00,47388.39,47474.31,47388.39,47439.26,22.96322,659.0,404.0,255.0 294 | 2021-12-09 20:52:00+00:00,47439.26,47511.09,47439.25,47495.15,46.88827,888.0,467.0,421.0 295 | 2021-12-09 20:53:00+00:00,47495.14,47501.39,47441.44,47462.05,16.45068,553.0,306.0,247.0 296 | 2021-12-09 20:54:00+00:00,47462.05,47529.14,47444.19,47479.15,33.29875,871.0,451.0,420.0 297 | 2021-12-09 20:55:00+00:00,47479.15,47507.47,47442.0,47450.28,31.25648,894.0,494.0,400.0 298 | 2021-12-09 20:56:00+00:00,47450.28,47560.0,47413.01,47503.41,34.96715,896.0,525.0,371.0 299 | 2021-12-09 20:57:00+00:00,47503.4,47577.42,47503.4,47550.82,32.12153,821.0,403.0,418.0 300 | 2021-12-09 20:58:00+00:00,47550.82,47678.84,47550.0,47659.0,62.19299,1229.0,754.0,475.0 301 | 2021-12-09 20:59:00+00:00,47659.01,47724.39,47636.66,47683.61,51.47722,1295.0,769.0,526.0 302 | 2021-12-09 21:00:00+00:00,47683.61,47766.42,47642.87,47739.04,35.37194,1250.0,728.0,522.0 303 | 2021-12-09 21:01:00+00:00,47739.04,47785.33,47705.42,47728.06,40.00747,1044.0,608.0,436.0 304 | 2021-12-09 21:02:00+00:00,47728.06,47740.02,47667.0,47672.17,27.71807,649.0,365.0,284.0 305 | 2021-12-09 21:03:00+00:00,47672.17,47672.18,47573.23,47580.97,46.11501,911.0,421.0,490.0 306 | 2021-12-09 21:04:00+00:00,47577.22,47604.93,47563.45,47594.35,14.56052,533.0,290.0,243.0 307 | 2021-12-09 21:05:00+00:00,47594.35,47624.54,47568.65,47601.64,18.86074,609.0,309.0,300.0 308 | 2021-12-09 21:06:00+00:00,47601.65,47628.73,47574.3,47605.98,22.21953,527.0,312.0,215.0 309 | 2021-12-09 21:07:00+00:00,47605.99,47677.55,47602.23,47666.0,17.8799,578.0,398.0,180.0 310 | 2021-12-09 21:08:00+00:00,47666.0,47753.11,47659.71,47750.01,21.7333,609.0,357.0,252.0 311 | 2021-12-09 21:09:00+00:00,47750.0,47779.43,47707.99,47708.0,27.39377,684.0,398.0,286.0 312 | 2021-12-09 21:10:00+00:00,47708.0,47771.51,47707.99,47730.43,38.66754,782.0,401.0,381.0 313 | 2021-12-09 21:11:00+00:00,47730.42,47739.05,47693.64,47715.77,22.53717,541.0,282.0,259.0 314 | 2021-12-09 21:12:00+00:00,47715.77,47744.14,47704.42,47744.1,20.60496,559.0,348.0,211.0 315 | 2021-12-09 21:13:00+00:00,47744.09,47787.43,47730.46,47781.6,11.01188,491.0,295.0,196.0 316 | 2021-12-09 21:14:00+00:00,47778.43,47790.27,47750.0,47761.98,20.16038,570.0,292.0,278.0 317 | 2021-12-09 21:15:00+00:00,47761.98,47786.77,47742.25,47786.76,11.5763,624.0,404.0,220.0 318 | 2021-12-09 21:16:00+00:00,47786.76,47787.49,47707.31,47722.11,49.14269,780.0,353.0,427.0 319 | 2021-12-09 21:17:00+00:00,47722.11,47742.48,47707.33,47742.48,19.72314,517.0,275.0,242.0 320 | 2021-12-09 21:18:00+00:00,47742.47,47889.76,47742.47,47877.49,45.41375,1167.0,744.0,423.0 321 | 2021-12-09 21:19:00+00:00,47880.12,47936.06,47850.8,47866.59,37.02553,942.0,507.0,435.0 322 | 2021-12-09 21:20:00+00:00,47866.64,47915.0,47830.02,47845.67,40.28473,839.0,421.0,418.0 323 | 2021-12-09 21:21:00+00:00,47845.68,47872.34,47791.24,47857.5,22.02304,644.0,369.0,275.0 324 | 2021-12-09 21:22:00+00:00,47857.5,47857.51,47829.49,47846.58,13.41622,370.0,171.0,199.0 325 | 2021-12-09 21:23:00+00:00,47846.58,47916.44,47750.34,47804.58,86.81257,1066.0,434.0,632.0 326 | 2021-12-09 21:24:00+00:00,47804.59,47821.37,47768.77,47791.47,39.37471,590.0,353.0,237.0 327 | 2021-12-09 21:25:00+00:00,47790.0,47798.69,47750.09,47753.06,9.70395,403.0,203.0,200.0 328 | 2021-12-09 21:26:00+00:00,47753.08,47823.27,47743.59,47804.87,28.69929,636.0,367.0,269.0 329 | 2021-12-09 21:27:00+00:00,47801.53,47947.19,47801.53,47924.73,29.55061,814.0,501.0,313.0 330 | 2021-12-09 21:28:00+00:00,47924.73,47968.86,47913.33,47917.26,30.93005,674.0,333.0,341.0 331 | 2021-12-09 21:29:00+00:00,47917.27,47917.27,47851.7,47881.96,11.08948,425.0,232.0,193.0 332 | 2021-12-09 21:30:00+00:00,47881.96,47897.26,47836.42,47883.82,10.01128,415.0,237.0,178.0 333 | 2021-12-09 21:31:00+00:00,47883.81,47883.82,47841.61,47847.89,11.94444,408.0,203.0,205.0 334 | 2021-12-09 21:32:00+00:00,47847.89,47866.25,47791.42,47828.23,26.71098,594.0,245.0,349.0 335 | 2021-12-09 21:33:00+00:00,47828.24,47838.99,47819.83,47826.93,11.8311,373.0,220.0,153.0 336 | 2021-12-09 21:34:00+00:00,47824.35,47831.96,47803.34,47831.95,9.0322,352.0,192.0,160.0 337 | 2021-12-09 21:35:00+00:00,47831.95,47856.34,47734.95,47745.4,44.49419,718.0,336.0,382.0 338 | 2021-12-09 21:36:00+00:00,47745.4,47783.93,47745.4,47777.82,36.50914,372.0,174.0,198.0 339 | 2021-12-09 21:37:00+00:00,47777.82,47817.89,47777.81,47809.75,31.19494,348.0,231.0,117.0 340 | 2021-12-09 21:38:00+00:00,47809.76,47855.17,47800.0,47816.89,12.798,458.0,239.0,219.0 341 | 2021-12-09 21:39:00+00:00,47816.88,47925.05,47816.88,47914.8,31.26477,598.0,343.0,255.0 342 | 2021-12-09 21:40:00+00:00,47914.81,47948.57,47895.09,47910.98,39.69053,615.0,297.0,318.0 343 | 2021-12-09 21:41:00+00:00,47910.98,47910.99,47864.33,47868.44,13.39162,453.0,215.0,238.0 344 | 2021-12-09 21:42:00+00:00,47868.44,48040.0,47861.75,47982.54,52.3262,1252.0,725.0,527.0 345 | 2021-12-09 21:43:00+00:00,47982.54,48032.09,47951.72,48016.11,17.38277,581.0,329.0,252.0 346 | 2021-12-09 21:44:00+00:00,48016.11,48111.0,48016.1,48110.99,46.65428,1197.0,740.0,457.0 347 | 2021-12-09 21:45:00+00:00,48111.0,48141.18,48053.34,48092.43,58.01201,1175.0,681.0,494.0 348 | 2021-12-09 21:46:00+00:00,48092.43,48139.7,48062.49,48101.85,56.64367,718.0,362.0,356.0 349 | 2021-12-09 21:47:00+00:00,48101.85,48101.86,48012.99,48022.4,22.17756,621.0,289.0,332.0 350 | 2021-12-09 21:48:00+00:00,48027.21,48029.99,47966.81,47994.29,36.71756,713.0,377.0,336.0 351 | 2021-12-09 21:49:00+00:00,47994.29,48067.51,47982.49,48053.47,26.82734,524.0,313.0,211.0 352 | 2021-12-09 21:50:00+00:00,48047.01,48084.82,48018.38,48038.6,19.35252,668.0,353.0,315.0 353 | 2021-12-09 21:51:00+00:00,48038.61,48100.0,48036.03,48100.0,18.53592,623.0,387.0,236.0 354 | 2021-12-09 21:52:00+00:00,48100.0,48110.93,48035.0,48045.36,21.73614,586.0,352.0,234.0 355 | 2021-12-09 21:53:00+00:00,48045.37,48110.86,47986.55,48020.12,60.12551,815.0,531.0,284.0 356 | 2021-12-09 21:54:00+00:00,48020.13,48076.97,48013.92,48054.14,13.72089,428.0,223.0,205.0 357 | 2021-12-09 21:55:00+00:00,48054.13,48054.14,48003.11,48012.24,14.14883,466.0,249.0,217.0 358 | 2021-12-09 21:56:00+00:00,48008.23,48026.95,48000.7,48008.44,8.11234,336.0,203.0,133.0 359 | 2021-12-09 21:57:00+00:00,48008.44,48053.83,47986.36,48018.45,18.30073,512.0,288.0,224.0 360 | 2021-12-09 21:58:00+00:00,48018.44,48018.5,47915.76,47969.21,42.21731,903.0,424.0,479.0 361 | 2021-12-09 21:59:00+00:00,47966.86,48011.99,47962.86,47980.29,26.82471,525.0,277.0,248.0 362 | -------------------------------------------------------------------------------- /finplotter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tysonwu/stack-orderflow/37fc4811950bce5fd40295a4c25ee443538dc4fc/finplotter/__init__.py -------------------------------------------------------------------------------- /finplotter/finplot_library/live.py: -------------------------------------------------------------------------------- 1 | ''' 2 | For simplifying plot updating. 3 | ''' 4 | 5 | import finplot 6 | 7 | 8 | class Live: 9 | def __init__(self): 10 | self.colors = {} 11 | self.item = None 12 | self.item_create_func_name = '' 13 | 14 | 15 | def item_create_call(self, name): 16 | val = getattr(finplot, name) 17 | if not callable(val): 18 | return val 19 | def wrap_call(*args, **kwargs): 20 | if 'gfx' in kwargs: # only used in subsequent update calls 21 | del kwargs['gfx'] 22 | item = val(*args, **kwargs) 23 | if isinstance(item, finplot.pg.GraphicsObject): # some kind of plot? 24 | setattr(self, 'item', item) 25 | setattr(self, 'item_create_func_name', val.__name__) 26 | # update to the "root" colors dict, if set 27 | if self.colors: 28 | item.colors.update(self.colors) 29 | # from hereon, use the item.colors instead, if present 30 | if hasattr(item, 'colors'): 31 | setattr(self, 'colors', item.colors) 32 | return self 33 | return item 34 | return wrap_call 35 | 36 | 37 | def item_update_call(self, name): 38 | if name == self.item_create_func_name: 39 | def wrap_call(*args, **kwargs): 40 | item = object.__getattribute__(self, 'item') 41 | ka = {'gfx':kwargs.get('gfx', True)} # only gfx parameter used 42 | assert len(args) == 1, 'only one unnamed argument allowed for live plots' 43 | item.update_data(*args, **ka) 44 | return wrap_call 45 | return getattr(self.item, name) 46 | 47 | 48 | def __getattribute__(self, name): 49 | try: 50 | return object.__getattribute__(self, name) 51 | except: 52 | pass 53 | # check if we're creating the plot, or updating data on an already created one 54 | if object.__getattribute__(self, 'item') is None: 55 | return self.item_create_call(name) 56 | return self.item_update_call(name) -------------------------------------------------------------------------------- /finplotter/finplot_library/pdplot.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Used as Pandas plotting backend. 3 | ''' 4 | 5 | import finplot 6 | 7 | 8 | def plot(df, x, y, kind, **kwargs): 9 | _x = df.index if y is None else df[x] 10 | try: 11 | _y = df[x].reset_index(drop=True) if y is None else df[y] 12 | except: 13 | _y = df.reset_index(drop=True) 14 | kwargs = dict(kwargs) 15 | if 'by' in kwargs: 16 | del kwargs['by'] 17 | if kind in ('candle', 'candle_ochl', 'candlestick', 'candlestick_ochl', 'volume', 'volume_ocv', 'renko'): 18 | if 'candle' in kind: 19 | return finplot.candlestick_ochl(df, **kwargs) 20 | elif 'volume' in kind: 21 | return finplot.volume_ocv(df, **kwargs) 22 | elif 'renko' in kind: 23 | return finplot.renko(df, **kwargs) 24 | elif kind == 'scatter': 25 | if 'style' not in kwargs: 26 | kwargs['style'] = 'o' 27 | return finplot.plot(_x, _y, **kwargs) 28 | elif kind == 'bar': 29 | return finplot.bar(_x, _y, **kwargs) 30 | elif kind in ('barh', 'horiz_time_volume'): 31 | return finplot.horiz_time_volume(df, **kwargs) 32 | elif kind in ('heatmap'): 33 | return finplot.heatmap(df, **kwargs) 34 | elif kind in ('labels'): 35 | return finplot.labels(df, **kwargs) 36 | elif kind in ('hist', 'histogram'): 37 | return finplot.hist(df, **kwargs) 38 | else: 39 | if x is None: 40 | _x = df 41 | _y = None 42 | if 'style' not in kwargs: 43 | kwargs['style'] = None 44 | return finplot.plot(_x, _y, **kwargs) 45 | -------------------------------------------------------------------------------- /finplotter/finplotter.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timezone 2 | import pytz 3 | 4 | import pyqtgraph as pg 5 | 6 | import finplotter.finplot_library as fplt 7 | 8 | 9 | class FinPlotter: 10 | 11 | BULL = '#1ABC9C' 12 | BEAR = '#E74C3C' 13 | 14 | def __init__(self, metadata=None, figsize=(1800, 1000)): 15 | self.ax = None # the canvas 16 | self._load_metadata(metadata) 17 | self._style_setup(figsize) 18 | 19 | 20 | def _load_metadata(self, metadata): 21 | if metadata is not None: 22 | self.inst = metadata.get('inst', '') 23 | self.interval = metadata.get('interval', '') 24 | 25 | 26 | def _style_setup(self, figsize): 27 | 28 | fplt.foreground = '#D6DBDF' 29 | fplt.background = '#151E26' 30 | fplt.legend_border_color = '#ffffff30' # make transparent 31 | fplt.legend_fill_color = '#ffffff10' # make transparent 32 | fplt.legend_text_color = fplt.foreground 33 | fplt.top_graph_scale = 2 # top graph and bottom graph has ratio of r:1 34 | fplt.winx, fplt.winy, fplt.winw, fplt.winh = 0, 0, figsize[0], figsize[1] 35 | 36 | # set max zoom: for orderflow data, allow more zoom (default was 20) 37 | fplt.max_zoom_points = 20 38 | 39 | # set bull body to same color as bull frame; otherwise it is default background color (transparent) 40 | # candlestick color 41 | fplt.candle_bull_color = self.BULL 42 | fplt.candle_bull_body_color = self.BULL 43 | fplt.candle_bear_color = self.BEAR 44 | 45 | # creat plot 46 | self.ax = fplt.create_plot( 47 | title=self.inst, 48 | rows=1, 49 | maximize=False, 50 | init_zoom_periods=18, 51 | ) 52 | 53 | axis_pen = fplt._makepen(color=fplt.foreground) 54 | self.ax.crosshair.vline.pen.setColor(pg.mkColor(fplt.foreground)) 55 | self.ax.crosshair.hline.pen.setColor(pg.mkColor(fplt.foreground)) 56 | self.ax.crosshair.xtext.setColor(fplt.foreground) 57 | self.ax.crosshair.ytext.setColor(fplt.foreground) 58 | 59 | self.ax.axes['left']['item'].setPen(axis_pen) 60 | self.ax.axes['left']['item'].setTextPen(axis_pen) 61 | self.ax.axes['bottom']['item'].setPen(axis_pen) 62 | self.ax.axes['bottom']['item'].setTextPen(axis_pen) 63 | 64 | 65 | def plot_candlestick(self, data, with_volume=False): 66 | 67 | if with_volume: 68 | assert len(data.columns) == 5, f'There should be exactly five columns named o, h, l, c, v under {with_volume=} Please check with your passed data.' 69 | else: 70 | assert len(data.columns) == 4, f'There should be exactly four columns named o, h, l, c under {with_volume=}. Please check with your passed data.' 71 | 72 | ohlc = data.copy() 73 | ax = self.ax 74 | 75 | # placeholder for tick info; updated with fplt.set_time_inspector(func) 76 | hover_label = fplt.add_legend('', ax=self.ax) 77 | 78 | # add candlestick 79 | # this is the version of candlestick without orderflow data 80 | candlestick_plot = fplt.candlestick_ochl(datasrc=ohlc[['o', 'c', 'h', 'l']], candle_width=0.7, ax=ax) 81 | 82 | if with_volume: 83 | # add volume 84 | transparency = '45' 85 | volume_plot = fplt.volume_ocv(ohlc[['o', 'c', 'v']], candle_width=0.7, ax=ax.overlay()) 86 | volume_plot.colors.update({ 87 | 'bull_frame': fplt.candle_bull_color + transparency, 88 | 'bull_body': fplt.candle_bull_body_color + transparency, 89 | 'bear_frame': fplt.candle_bear_color + transparency, 90 | 'bear_body': fplt.candle_bear_color + transparency, 91 | }) 92 | 93 | # set gridlines 94 | ax.showGrid(x=True, y=True, alpha=0.2) 95 | 96 | # add legend of ohlc data 97 | def update_legend_text(x, y): 98 | dt = datetime.fromtimestamp(x // 1000000000) 99 | utcdt = dt.astimezone(pytz.utc).replace(tzinfo=timezone.utc) 100 | # dt = dt.replace(tzinfo=timezone.utc) 101 | row = ohlc.loc[utcdt] 102 | # format html with the candle and set legend 103 | fmt = '%%s' % (self.BULL if (row['o'] < row['c']).all() else self.BEAR) 104 | rawtxt = '%%s %%s   O: %s H: %s L: %s C: %s' % (fmt, fmt, fmt, fmt) 105 | hover_label.setText(rawtxt % (self.inst, self.interval, row['o'], row['h'], row['l'], row['c'])) 106 | fplt.set_time_inspector(update_legend_text, ax=ax, when='hover') 107 | 108 | # additional crosshair info 109 | def enrich_info(x, y, xtext, ytext): 110 | o = ohlc.iloc[x]['o'] 111 | h = ohlc.iloc[x]['h'] 112 | l = ohlc.iloc[x]['l'] 113 | c = ohlc.iloc[x]['c'] 114 | add_xt = f'\t{xtext}' 115 | add_yt = f'\tLevel: {ytext}\n\n\tOpen: {o}\n\tHigh: {h}\n\tLow: {l}\n\tClose: {c}' 116 | return add_xt, add_yt 117 | 118 | fplt.add_crosshair_info(enrich_info, ax=ax) 119 | 120 | 121 | def add_line(self, data, color='#F4D03F', width=1, legend=None): 122 | fplt.plot(data, ax=self.ax, color=color, width=width, legend=legend) 123 | 124 | 125 | def show(self): 126 | fplt.show() -------------------------------------------------------------------------------- /market_profile.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | import numpy as np 4 | import pandas as pd 5 | 6 | def _midmax_idx(array): 7 | if len(array) == 0: 8 | return None 9 | 10 | # Find candidate maxima 11 | maxima_idxs = np.argwhere(array == np.amax(array))[:,0] 12 | if len(maxima_idxs) == 1: 13 | return maxima_idxs[0] 14 | elif len(maxima_idxs) <= 1: 15 | return None 16 | 17 | # Find the distances from the midpoint to find 18 | # the maxima with the least distance 19 | midpoint = len(array) / 2 20 | v_norm = np.vectorize(np.linalg.norm) 21 | maximum_idx = np.argmin(v_norm(maxima_idxs - midpoint)) 22 | 23 | return maxima_idxs[maximum_idx] 24 | 25 | 26 | def find_poc(profile) -> float: 27 | """Calculates metrics based on market profiles 28 | 29 | Args: 30 | profile (pd.DataFrame): a dataframe with price levels as index and one column named 'q' for quantity 31 | """ 32 | poc_idx = _midmax_idx(profile['q'].tolist()) 33 | if poc_idx is not None: 34 | poc_volume = profile['q'].iloc[poc_idx] 35 | poc_price_level = profile.index[poc_idx] 36 | else: 37 | poc_volume = None 38 | poc_price_level = None 39 | 40 | return poc_volume, poc_price_level 41 | 42 | 43 | class MarketProfileSlice: 44 | 45 | def __init__(self, inst: str, timepoint: datetime, ohlcv: dict, orderflow: np.ndarray): 46 | ''' 47 | ohlcv 48 | o: open 49 | h: high 50 | l: low 51 | c: close 52 | v: volume 53 | pot_ask: pace of tape of ask 54 | pot_bid: pace of tape of bid 55 | pot: pace of tape 56 | 57 | orderflow 58 | p: price level 59 | q: total volume 60 | d: volume delta 61 | b: bid volume 62 | a: ask volume 63 | ''' 64 | self.inst = inst 65 | self.timepoint = timepoint 66 | 67 | # read ohlcv data 68 | self.open = ohlcv['o'] 69 | self.high = ohlcv['h'] 70 | self.low = ohlcv['l'] 71 | self.close = ohlcv['c'] 72 | self.volume_qty = ohlcv['v'] 73 | # pace of tape 74 | self.pot = ohlcv['pot'] 75 | self.pot_ask = ohlcv['pot_ask'] 76 | self.pot_bid = ohlcv['pot_bid'] 77 | 78 | self.open_range = (self.open, self.close) 79 | self.profile_range = (self.high, self.low) 80 | self.mid_price = round((self.high + self.low) / 2, 8) 81 | 82 | # read orderflow data 83 | self.n_levels = len(orderflow) 84 | self.price_levels = np.array([row[0] for row in orderflow]) 85 | self.price_levels_range = (max(self.price_levels), min(self.price_levels)) 86 | self.delta_qty = round(sum([row[2] for row in orderflow]), 8) 87 | self.total_bid_qty = round(sum([row[3] for row in orderflow]), 8) 88 | self.total_ask_qty = round(sum([row[4] for row in orderflow]), 8) 89 | 90 | self.profile = pd.DataFrame({'q': [row[1] for row in orderflow]}, index=self.price_levels) 91 | # delta: defined as ask volume minus bid volume 92 | self.delta_profile = pd.DataFrame({timepoint: [row[2] for row in orderflow]}, index=self.price_levels) 93 | self.bidask_profile = pd.DataFrame({'b': [row[3] for row in orderflow], 'a': [row[4] for row in orderflow]}, index=self.price_levels) 94 | # calc POC 95 | self.poc_volume, self.poc_price_level = find_poc(self.profile) 96 | -------------------------------------------------------------------------------- /orderflow_plotter.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timezone 2 | import pytz 3 | import os 4 | 5 | import pandas as pd 6 | import numpy as np 7 | import pyqtgraph as pg 8 | import talib as ta 9 | 10 | import finplotter.finplot_library as fplt 11 | 12 | ''' 13 | Example references come from the original finplot package. 14 | Known issue: drawing heatmap for orderflow slot hightlights and bid-ask volume labels are resource-intensive. 15 | ''' 16 | 17 | class OrderflowPlotter: 18 | 19 | ema_n = [20, 50, 200] 20 | macd = [12, 26, 9] 21 | stoch_rsi = [14, 3, 3] 22 | 23 | def __init__(self, token: str, interval: str, increment: float, ohlcv: pd.DataFrame, mp_slice: list): 24 | self.token = token 25 | self.interval = interval 26 | self.increment = increment 27 | self.ohlcv = ohlcv 28 | self.mp_slice = mp_slice 29 | self.plots = {} # pyqtgraph plot objects 30 | 31 | 32 | def update_datasrc(self, ohlcv, mp_slice): 33 | self.ohlcv = ohlcv 34 | self.mp_slice = mp_slice 35 | 36 | 37 | def trim_datasrc(self, keep: int = None): 38 | '''This part yet to be fixed: trimming dataframe like this causes error in front-end (xaxis cannot be updated)''' 39 | if keep is not None: 40 | print(len(self.ohlcv)) 41 | if len(self.ohlcv) > keep: 42 | self.ohlcv = self.ohlcv.iloc[-keep:] 43 | self.mp_slice = self.mp_slice[-keep:] 44 | print(f'Trimmed datasource as datasource length exceeds {keep}') 45 | 46 | 47 | def calculate_plot_features(self): 48 | 49 | self.ohlcv['d'] = [mp.delta_qty for mp in self.mp_slice] 50 | self.ohlcv['poc'] = [mp.poc_price_level for mp in self.mp_slice] 51 | 52 | for n in self.ema_n: 53 | self.ohlcv[f'ema{n}'] = ta.EMA(self.ohlcv['c'], timeperiod=n) 54 | 55 | self.ohlcv['macd'], self.ohlcv['macd_signal'], self.ohlcv['macd_diff'] = ta.MACD(self.ohlcv['c'], fastperiod=self.macd[0], slowperiod=self.macd[1], signalperiod=self.macd[2]) 56 | 57 | self.ohlcv['fastk'], self.ohlcv['fastd'] = ta.STOCHRSI(self.ohlcv['c'], timeperiod=self.stoch_rsi[0], fastk_period=self.stoch_rsi[1], fastd_period=self.stoch_rsi[2], fastd_matype=0) 58 | 59 | ''' 60 | Uncomment this to enable text and heatmap which can be slow 61 | ''' 62 | delta_heatmap_rows = [None] * len(self.ohlcv) 63 | # make heatmap df 64 | for idx in range(len(self.ohlcv)): 65 | delta_heatmap_rows[idx] = self.mp_slice[idx].delta_profile.T 66 | # concat rows 67 | self.delta_heatmap = pd.concat(delta_heatmap_rows, axis=0, sort=True) 68 | 69 | price_levels_text_rows = [None] * len(self.ohlcv) 70 | 71 | for idx, mp in enumerate(self.mp_slice): 72 | bidask_profile = mp.bidask_profile.reset_index().rename(columns={'index': 'p'}) 73 | bidask_profile['t'] = int(mp.timepoint.timestamp()) 74 | price_levels_text_rows[idx] = bidask_profile 75 | self.price_level_texts = pd.concat(price_levels_text_rows, axis=0, sort=True).reset_index(drop=True) 76 | self.price_level_texts['a'] = self.price_level_texts['a'].apply(lambda x: self.human_format(x)) 77 | self.price_level_texts['b'] = self.price_level_texts['b'].apply(lambda x: self.human_format(x)) 78 | 79 | ts = [int(t.timestamp()) for t in self.ohlcv.index] 80 | self.pot_heatmap = self.ohlcv[['pot_ask', 'pot_bid']].copy().rename(columns={'pot_ask': 1, 'pot_bid': 0}) 81 | 82 | self.pot_df = self.ohlcv[['pot_ask', 'pot_bid']].copy().reset_index(drop=True) 83 | self.pot_df['ts'] = ts 84 | self.pot_df['ask_label_height'] = 1.5 85 | self.pot_df['bid_label_height'] = 0.5 86 | self.pot_df['ask_label'] = self.pot_df['pot_ask'].apply(lambda x: self.human_format(x)) 87 | self.pot_df['bid_label'] = self.pot_df['pot_bid'].apply(lambda x: self.human_format(x)) 88 | 89 | self.cvd = np.cumsum(self.ohlcv['d']) 90 | fplt.refresh() 91 | 92 | 93 | def orderflow_plot(self): 94 | 95 | if self.ohlcv.empty: 96 | return None 97 | 98 | self.calculate_plot_features() 99 | 100 | # =======widnow setup========================================================================= 101 | # plotting 102 | ema_colors = ['#33DCCD50', '#ADE03670', '#F4D03F80'] 103 | fplt.foreground = '#D6DBDF' 104 | fplt.background = '#151E26' 105 | fplt.legend_border_color = '#ffffff30' # make transparent 106 | fplt.legend_fill_color = '#ffffff10' # make transparent 107 | fplt.legend_text_color = fplt.foreground 108 | # this is overriden if rows_stretch_factor is set in fplt.create_plot() 109 | # fplt.top_graph_scale = 3 # top graph and bottom graph has ratio of r:1 110 | fplt.winx, fplt.winy, fplt.winw, fplt.winh = 100,100,1600,1600 111 | 112 | ax, ax5, ax3, ax2, ax4 = fplt.create_plot( 113 | title='StackOrderflow', 114 | rows=5, # main candlestick = ax / pace of tape = ax5 / MACD = ax3 / CVD = ax2 / StochRSI = ax4 115 | maximize=False, 116 | init_zoom_periods=18, 117 | row_stretch_factors=[3, 0.3, 1, 1, 1], 118 | ) 119 | 120 | # placeholder for tick info; updated with fplt.set_time_inspector(func) 121 | hover_label = fplt.add_legend('', ax=ax) 122 | 123 | # set max zoom: for orderflow data, allow more zoom (default was 20) 124 | fplt.max_zoom_points = 5 125 | fplt.lod_labels = 700 # when number of labels exceed this number, stop generating them 126 | 127 | # =======start plotting========================================================================= 128 | 129 | print('Drawing plot...') 130 | # add candlestick 131 | # this is the version of candlestick without orderflow data 132 | # candlestick_plot = fplt.candlestick_ochl(datasrc=self.ohlcv[['o', 'c', 'h', 'l']], candle_width=0.7, ax=ax) 133 | 134 | # and this is the version with orderflow data; thinner candle and put aside 135 | self.plots['candlestick'] = fplt.candlestick_ochl(datasrc=self.ohlcv[['o', 'c', 'h', 'l']], candle_width=.075, ax=ax) 136 | self.plots['candlestick'].x_offset = -0.4 # thin candle to the left 137 | 138 | # add volume 139 | self.plots['volume'] = fplt.volume_ocv(self.ohlcv[['o', 'c', 'v']], candle_width=0.2, ax=ax.overlay(scale=0.18)) 140 | 141 | # poc plot 142 | # can choose below either: plot a connected curve of POCs, or explicitly mark the POC level for each point 143 | self.plots['poc'] = fplt.plot(self.ohlcv['poc'], ax=ax, legend='POC', color='#008FFF') 144 | 145 | # for t, poc in self.ohlcv['poc'].to_dict().items(): 146 | # fplt.add_line(p0=(t-timedelta(seconds=24), poc), p1=(t+timedelta(seconds=24), poc), color='#008FFF90', width=2, ax=ax) 147 | 148 | # plot EMAs 149 | for n, color in zip(self.ema_n, ema_colors): 150 | self.plots[f'ema{n}'] = fplt.plot(self.ohlcv[f'ema{n}'], ax=ax, legend=f'EMA {n}', color=color) 151 | 152 | # add heatmap 153 | self.plots['delta_heatmap'] = fplt.delta_heatmap(self.delta_heatmap, filter_limit=0.7, whiteout=0.0, rect_size=1.0) 154 | 155 | 156 | small_font = pg.Qt.QtGui.QFont() 157 | small_font.setPixelSize(9) 158 | 159 | ''' 160 | Uncomment this to enable text and heatmap which can be slow 161 | ''' 162 | self.plots['bid_labels'] = fplt.labels( 163 | self.price_level_texts[['t', 'p', 'b']], ax=ax, anchor=(1, 0.5), color=fplt.foreground, qfont=small_font 164 | ) 165 | self.plots['ask_labels'] = fplt.labels( 166 | self.price_level_texts[['t', 'p', 'a']], ax=ax, anchor=(0, 0.5), color=fplt.foreground, qfont=small_font 167 | ) 168 | 169 | # ============================================================================================== 170 | # add PoT table 171 | ''' 172 | Ref: examples/bubble-table.py 173 | ''' 174 | def skip_y_crosshair_info(x, y, xt, yt): # we don't want any Y crosshair info on the table 175 | return xt, '' 176 | 177 | ax5.set_visible(yaxis=False) 178 | fplt.add_crosshair_info(skip_y_crosshair_info, ax=ax5) 179 | fplt.set_y_range(0, 2, ax5) 180 | 181 | pot_colmap = fplt.ColorMap([0.0, 0.3, 1.0], [[255, 255, 255, 10], [255, 155, 0, 200], [210, 48, 9, 230]]) # traffic light colors 182 | 183 | # background color 184 | medium_font = pg.Qt.QtGui.QFont() 185 | medium_font.setPixelSize(11) 186 | 187 | self.plots['pot_heatmap'] = fplt.heatmap(self.pot_heatmap, colmap=pot_colmap, colcurve=lambda x: x, ax=ax5) 188 | 189 | self.plots['pot_ask'] = fplt.labels(self.pot_df[['ts', 'ask_label_height', 'ask_label']], ax=ax5, anchor=(0.5, 0.5), color=fplt.foreground, qfont=medium_font) 190 | self.plots['pot_bid'] = fplt.labels(self.pot_df[['ts', 'bid_label_height', 'bid_label']], ax=ax5, anchor=(0.5, 0.5), color=fplt.foreground, qfont=medium_font) 191 | 192 | # maybe find a better way to deal with this 193 | fplt.legend_border_color = '#151E26' 194 | fplt.legend_fill_color = '#ffffff80' 195 | fplt.legend_text_color = '#151E26' 196 | fplt.add_legend('ASK (upper) BID (lower)', ax= ax5) 197 | # revert back after adding this legend 198 | fplt.legend_border_color = '#ffffff30' 199 | fplt.legend_fill_color = '#ffffff10' 200 | fplt.legend_text_color = fplt.foreground 201 | 202 | # and set background 203 | vb = self.plots['pot_heatmap'].getViewBox() 204 | vb.setBackgroundColor('#00000000') 205 | 206 | # ============================================================================================== 207 | # plot MACD 208 | self.plots['macd_diff'] = fplt.volume_ocv(self.ohlcv[['o', 'c', 'macd_diff']], ax=ax3, candle_width=0.2, colorfunc=fplt.strength_colorfilter) 209 | self.plots['macd'] = fplt.plot(self.ohlcv['macd'], ax=ax3, legend=f'MACD ({self.macd[0]}, {self.macd[1]}, {self.macd[2]})') 210 | self.plots['macd_signal'] = fplt.plot(self.ohlcv['macd_signal'], ax=ax3, legend='Signal') 211 | 212 | vb = self.plots['macd'].getViewBox() 213 | vb.setBackgroundColor('#00000020') 214 | 215 | # ============================================================================================== 216 | ''' 217 | Ref: examples/snp500.py 218 | ''' 219 | # plot cvd 220 | line_color = '#F4D03F' 221 | self.plots['cvd'] = fplt.plot(self.cvd, ax=ax2, legend='CVD', color=line_color, fillLevel=0, brush=line_color+'10') 222 | # and set background 223 | vb = self.plots['cvd'].getViewBox() 224 | vb.setBackgroundColor('#00000000') 225 | 226 | # ============================================================================================== 227 | # plot stoch RSI 228 | self.plots['stochrsi_fastk'] = fplt.plot(self.ohlcv['fastk'], ax=ax4, legend=f'StochRSI Fast k: {self.stoch_rsi[1]} Timeperiod: {self.stoch_rsi[0]}') 229 | self.plots['stochrsi_fastd'] = fplt.plot(self.ohlcv['fastd'], ax=ax4, legend=f'StochRSI Fast d: {self.stoch_rsi[2]} Timeperiod: {self.stoch_rsi[0]}') 230 | 231 | thresholds = [20, 80] 232 | for th in thresholds: 233 | rsi_threshold_line = pg.InfiniteLine(pos=th, angle=0, pen=fplt._makepen(color='#ffffff50', style='- - ')) 234 | ax4.addItem(rsi_threshold_line, ignoreBounds=True) 235 | fplt.add_band(*thresholds, color='#2980B920', ax=ax4) 236 | 237 | vb = self.plots['stochrsi_fastk'].getViewBox() 238 | vb.setBackgroundColor('#00000020') 239 | 240 | # ============================================================================================== 241 | ''' 242 | Ref: examples/complicated.py 243 | ''' 244 | # set bull body to same color as bull frame; otherwise it is default background color (transparent) 245 | bull = '#1ABC9C' 246 | bear = '#E74C3C' 247 | fplt.candle_bull_color = bull 248 | fplt.candle_bull_body_color = bull 249 | fplt.candle_bear_color = bear 250 | self.plots['candlestick'].colors.update({ 251 | 'bull_body': fplt.candle_bull_color 252 | }) 253 | 254 | transparency = '45' 255 | self.plots['volume'].colors.update({ 256 | 'bull_frame': fplt.candle_bull_color + transparency, 257 | 'bull_body': fplt.candle_bull_body_color + transparency, 258 | 'bear_frame': fplt.candle_bear_color + transparency, 259 | 'bear_body': fplt.candle_bear_color + transparency, 260 | }) 261 | 262 | # set gridlines 263 | ax.showGrid(x=True, y=True, alpha=0.2) 264 | ax2.showGrid(x=True, y=True, alpha=0.1) 265 | ax3.showGrid(x=True, y=True, alpha=0.1) 266 | ax4.showGrid(x=True, y=True, alpha=0.1) 267 | ax5.showGrid(x=True, y=False, alpha=0.1) 268 | 269 | # add YAxis item at the right 270 | # ax.axes['right'] = {'item': fplt.YAxisItem(vb=ax.vb, orientation='right')} 271 | # ax2.axes['right'] = {'item': fplt.YAxisItem(vb=ax2.vb, orientation='right')} 272 | 273 | # add legend of ohlcv data 274 | ''' 275 | Ref: examples/snp500.py 276 | ''' 277 | def update_legend_text(x, y): 278 | dt = datetime.fromtimestamp(x // 1000000000) 279 | utcdt = dt.astimezone(pytz.utc).replace(tzinfo=timezone.utc) 280 | # dt = dt.replace(tzinfo=timezone.utc) 281 | row = self.ohlcv.loc[utcdt] 282 | # format html with the candle and set legend 283 | fmt = '%%s' % (bull if (row['o'] < row['c']).all() else bear) 284 | rawtxt = '%%s %%s   O: %s H: %s L: %s C: %s Delta: %s' % (fmt, fmt, fmt, fmt, fmt) 285 | hover_label.setText(rawtxt % (self.token, self.interval, row['o'], row['h'], row['l'], row['c'], row['d'])) 286 | fplt.set_time_inspector(update_legend_text, ax=ax, when='hover') 287 | 288 | # additional crosshair info 289 | def enrich_info(x, y, xtext, ytext): 290 | # o = self.ohlcv.iloc[x]['o'] 291 | # h = self.ohlcv.iloc[x]['h'] 292 | # l = self.ohlcv.iloc[x]['l'] 293 | # c = self.ohlcv.iloc[x]['c'] 294 | mp = self.mp_slice[x] 295 | bapr = mp.bidask_profile.copy() 296 | y_increment = self.increment / 2 297 | if y > mp.price_levels_range[0] + y_increment or y < mp.price_levels_range[1] - y_increment: 298 | add_yt = f'\tLevel: {ytext}' # not showing orderflow info if cursor is outside range 299 | else: 300 | bapr.index = bapr.index - y_increment # 301 | plr = bapr[bapr.index <= y].iloc[-1] # the price level row 302 | pl = round(plr.name + y_increment, 8) # the original price level 303 | dpr = mp.delta_profile 304 | a, b, d = plr['a'], plr['b'], dpr.loc[pl].values[0] 305 | add_yt = f'\tLevel: {ytext}\n\n\tAsk: {a}\n\tBid: {b}\n\tDelta: {d}' # not showing ask and bid value if cursor is outside range 306 | add_xt = f'\t{xtext}' 307 | return add_xt, add_yt 308 | 309 | fplt.add_crosshair_info(enrich_info, ax=ax) 310 | 311 | ''' 312 | Ref: examples/complicated.py 313 | ''' 314 | # set dark themes ==================== 315 | pg.setConfigOptions(foreground=fplt.foreground, background=fplt.background) 316 | 317 | # window background 318 | for win in fplt.windows: 319 | win.setBackground(fplt.background) 320 | 321 | # axis, crosshair, candlesticks, volumes 322 | axs = [ax for win in fplt.windows for ax in win.axs] 323 | vbs = set([ax.vb for ax in axs]) 324 | axs += fplt.overlay_axs 325 | axis_pen = fplt._makepen(color=fplt.foreground) 326 | for ax in axs: 327 | ax.axes['left']['item'].setPen(axis_pen) 328 | ax.axes['left']['item'].setTextPen(axis_pen) 329 | ax.axes['bottom']['item'].setPen(axis_pen) 330 | ax.axes['bottom']['item'].setTextPen(axis_pen) 331 | if ax.crosshair is not None: 332 | ax.crosshair.vline.pen.setColor(pg.mkColor(fplt.foreground)) 333 | ax.crosshair.hline.pen.setColor(pg.mkColor(fplt.foreground)) 334 | ax.crosshair.xtext.setColor(fplt.foreground) 335 | ax.crosshair.ytext.setColor(fplt.foreground) 336 | # ==================================== 337 | 338 | 339 | def show(self): 340 | return fplt.show() 341 | 342 | 343 | @staticmethod 344 | # add price level bid ask text 345 | def human_format(num, sigfig=2): 346 | ''' 347 | Ref: https://stackoverflow.com/questions/579310/formatting-long-numbers-as-strings-in-python 348 | ''' 349 | # 1g means 1 sigfig 350 | num = float(f'{num:.{sigfig}g}') 351 | magnitude = 0 352 | while abs(num) >= 1000: 353 | magnitude += 1 354 | num /= 1000.0 355 | return '{}{}'.format('{:f}'.format(num).rstrip('0').rstrip('.'), ['', 'K', 'M', 'B', 'T'][magnitude]) 356 | 357 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | finplot==1.9.0 2 | more-itertools==9.0.0 3 | numpy==1.24.2 4 | pandas==1.5.3 5 | PyOpenGL==3.1.6 6 | PyOpenGL-accelerate==3.1.6 7 | PyQt6==6.4.2 8 | pyqtgraph==0.13.1 9 | pytz==2022.7.1 10 | TA-Lib==0.4.25 11 | unicorn-binance-websocket-api==1.41.0 12 | unicorn-fy==0.12.2 --------------------------------------------------------------------------------