├── .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 | 
26 |
27 | ### Example 02: Simple candlestick plot with technical indicator panels
28 | 
29 |
30 | ### Example 03: Orderflow plot with static data
31 | 
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
--------------------------------------------------------------------------------