Working with Market Depth and Trades

Display 3-depth

[1]:
from numba import njit

@njit
def print_3depth(hbt):
    while hbt.elapse(60 * 1e6):
        # a key of bid_depth or ask_depth is price in tick format.
        # (integer) price_tick = price / tick_size
        print('current_timestamp:', hbt.current_timestamp)
        i = 0
        for tick_price in range(hbt.best_ask_tick, hbt.high_ask_tick + 1):
            if tick_price in hbt.ask_depth:
                print(
                    'ask: ',
                    hbt.ask_depth[tick_price],
                    '@',
                    round(tick_price * hbt.tick_size, 3)
                )
                i += 1
                if i == 3:
                    break
        i = 0
        for tick_price in range(hbt.best_bid_tick, hbt.low_bid_tick - 1, -1):
            if tick_price in hbt.bid_depth:
                print(
                    'bid: ',
                    hbt.bid_depth[tick_price],
                    '@',
                    round(tick_price * hbt.tick_size, 3)
                )
                i += 1
                if i == 3:
                    break
    return True
[2]:
import numpy as np

btcusdt_20230405 = np.load('btcusdt_20230405.npz')['data']
btcusdt_20230404_eod = np.load('btcusdt_20230404_eod.npz')['data']
[3]:
from hftbacktest import HftBacktest, FeedLatency, Linear

hbt = HftBacktest(
    btcusdt_20230405,
    tick_size=0.1,
    lot_size=0.001,
    maker_fee=0.0002,
    taker_fee=0.0007,
    order_latency=FeedLatency(),
    asset_type=Linear,
    snapshot=btcusdt_20230404_eod
)

print_3depth(hbt)
current_timestamp: 1680652860032116
ask:  9.228 @ 28150.8
ask:  0.387 @ 28150.9
ask:  3.996 @ 28151.0
bid:  3.135 @ 28150.7
bid:  0.002 @ 28150.6
bid:  0.813 @ 28150.5
current_timestamp: 1680652920032116
ask:  1.224 @ 28144.2
ask:  0.223 @ 28144.3
ask:  0.001 @ 28144.5
bid:  10.529 @ 28144.1
bid:  0.168 @ 28144.0
bid:  0.29 @ 28143.9
current_timestamp: 1680652980032116
ask:  3.397 @ 28150.0
ask:  1.282 @ 28150.1
ask:  0.003 @ 28150.4
bid:  7.951 @ 28149.9
bid:  0.02 @ 28149.8
bid:  0.02 @ 28149.7
current_timestamp: 1680653040032116
ask:  3.905 @ 28145.8
ask:  1.695 @ 28145.9
ask:  0.003 @ 28146.0
bid:  5.793 @ 28145.7
bid:  0.059 @ 28145.6
bid:  0.044 @ 28145.5
current_timestamp: 1680653100032116
ask:  6.8 @ 28140.6
ask:  0.001 @ 28140.7
ask:  0.004 @ 28141.1
bid:  2.416 @ 28140.5
bid:  0.004 @ 28140.4
bid:  0.012 @ 28140.3
current_timestamp: 1680653160032116
ask:  3.666 @ 28143.9
ask:  1.422 @ 28144.0
ask:  1.455 @ 28144.1
bid:  3.189 @ 28143.8
bid:  5.136 @ 28143.7
bid:  0.012 @ 28143.5
[3]:
True

Order Book Imbalance

[4]:
@njit
def orderbookimbalance(hbt, out):
    while hbt.elapse(10 * 1e6):
        mid_price = (hbt.best_bid + hbt.best_ask) / 2.0

        sum_ask_qty_50bp = 0.0
        sum_ask_qty = 0.0
        for tick_price in range(hbt.best_ask_tick, hbt.high_ask_tick + 1):
            if tick_price in hbt.ask_depth:
                ask_price = tick_price * hbt.tick_size
                depth_from_mid = (ask_price - mid_price) / mid_price
                if depth_from_mid > 0.01:
                    break
                sum_ask_qty += hbt.ask_depth[tick_price]

                if depth_from_mid <= 0.005:
                    sum_ask_qty_50bp = sum_ask_qty


        sum_bid_qty_50bp = 0.0
        sum_bid_qty = 0.0
        for tick_price in range(hbt.best_bid_tick, hbt.low_bid_tick - 1, -1):
            if tick_price in hbt.bid_depth:
                bid_price = tick_price * hbt.tick_size
                depth_from_mid = (mid_price - bid_price) / mid_price
                if depth_from_mid > 0.01:
                    break
                sum_bid_qty += hbt.bid_depth[tick_price]

                if depth_from_mid <= 0.005:
                    sum_bid_qty_50bp = sum_bid_qty

        imbalance_50bp = sum_bid_qty_50bp - sum_ask_qty_50bp
        imbalance_1pct = sum_bid_qty - sum_ask_qty
        imbalance_tob = hbt.bid_depth[hbt.best_bid_tick] - hbt.ask_depth[hbt.best_ask_tick]

        out.append((hbt.current_timestamp, imbalance_tob, imbalance_50bp, imbalance_1pct))
    return True
[5]:
from numba.typed import List
from numba.types import Tuple, float64

hbt = HftBacktest(
    btcusdt_20230405,
    tick_size=0.1,
    lot_size=0.001,
    maker_fee=0.0002,
    taker_fee=0.0007,
    order_latency=FeedLatency(),
    asset_type=Linear,
    snapshot=btcusdt_20230404_eod
)

tup_ty = Tuple((float64, float64, float64, float64))
out = List.empty_list(tup_ty, allocated=100_000)

orderbookimbalance(hbt, out)
[5]:
True
[6]:
import polars as pl

df = pl.DataFrame(out).transpose()
df.columns = ['Local Timestamp', 'TOB Imbalance', '0.5% Imbalance', '1% Imbalance']
df = df.with_columns(
    pl.from_epoch('Local Timestamp', time_unit='us')
)

df
[6]:
shape: (41, 4)
Local TimestampTOB Imbalance0.5% Imbalance1% Imbalance
datetime[μs]f64f64f64
2023-04-05 00:00:10.03211610.6279.522-521.026
2023-04-05 00:00:20.032116-4.868-152.592-684.247
2023-04-05 00:00:30.0321160.59-161.95-701.843
2023-04-05 00:00:40.0321163.962-142.51-669.033
2023-04-05 00:00:50.0321164.912-114.827-653.331
2023-04-05 00:01:00.032116-6.093273.067-755.551
2023-04-05 00:01:10.03211610.201-36.007-701.998
2023-04-05 00:01:20.0321163.009121.205-735.241
2023-04-05 00:01:30.0321168.383381.521-704.022
2023-04-05 00:01:40.0321168.672166.934-691.313
2023-04-05 00:01:50.032116-2.575393.212-695.99
2023-04-05 00:02:00.0321169.305165.543-701.034
2023-04-05 00:05:00.032116-4.384504.205-1005.883
2023-04-05 00:05:10.032116-10.432506.615-952.28
2023-04-05 00:05:20.03211620.341542.513-917.164
2023-04-05 00:05:30.0321163.113587.247-858.536
2023-04-05 00:05:40.0321161.212542.287-901.735
2023-04-05 00:05:50.0321163.997184.424-833.991
2023-04-05 00:06:00.032116-0.477180.863-825.373
2023-04-05 00:06:10.032116-3.77525.716-887.492
2023-04-05 00:06:20.032116-5.273434.96-1004.985
2023-04-05 00:06:30.032116-4.487570.354-837.517
2023-04-05 00:06:40.032116-6.186565.936-838.518
2023-04-05 00:06:50.0321163.351534.445-870.112
[7]:
import matplotlib.pyplot as plt

plt.plot(df['Local Timestamp'], df['TOB Imbalance'])
plt.plot(df['Local Timestamp'], df['0.5% Imbalance'])
plt.plot(df['Local Timestamp'], df['1% Imbalance'])

plt.legend(['TOB Imbalance', '0.5% Imbalance', '1% Imbalance'])
[7]:
<matplotlib.legend.Legend at 0x7f271704caf0>
../_images/tutorials_Working_with_Market_Depth_and_Trades_9_1.png

Display last trades between the step

[31]:
from hftbacktest import COL_EXCH_TIMESTAMP, COL_SIDE, COL_PRICE, COL_QTY

@njit
def print_trades(hbt):
    while hbt.elapse(60 * 1e6):
        print('-------------------------------------------------------------------------------')
        print('current_timestamp:', hbt.current_timestamp)

        num = 0
        for trade in hbt.last_trades:
            if num > 10:
                print('...')
                break
            print(
                'exch_timestamp:',
                trade[COL_EXCH_TIMESTAMP],
                'buy' if trade[COL_SIDE] == 1 else 'sell',
                trade[COL_QTY],
                '@',
                trade[COL_PRICE]
            )
            num += 1

        hbt.clear_last_trades()
    return True
[32]:
hbt = HftBacktest(
    btcusdt_20230405,
    tick_size=0.1,
    lot_size=0.001,
    maker_fee=0.0002,
    taker_fee=0.0007,
    order_latency=FeedLatency(),
    asset_type=Linear,
    snapshot=btcusdt_20230404_eod,
    trade_list_size=10_000
)

print_trades(hbt)
-------------------------------------------------------------------------------
current_timestamp: 1680652860032116
exch_timestamp: 1680652804962000.0 sell 0.001 @ 28155.1
exch_timestamp: 1680652804964000.0 buy 0.012 @ 28155.2
exch_timestamp: 1680652804966000.0 buy 0.002 @ 28155.2
exch_timestamp: 1680652804968000.0 buy 0.003 @ 28155.2
exch_timestamp: 1680652804981000.0 sell 0.019 @ 28155.1
exch_timestamp: 1680652804981000.0 sell 0.004 @ 28155.1
exch_timestamp: 1680652804981000.0 sell 0.001 @ 28155.1
exch_timestamp: 1680652804981000.0 sell 0.02 @ 28155.1
exch_timestamp: 1680652804981000.0 sell 0.013 @ 28155.1
exch_timestamp: 1680652804981000.0 sell 0.002 @ 28155.1
exch_timestamp: 1680652804981000.0 sell 0.001 @ 28155.0
...
-------------------------------------------------------------------------------
current_timestamp: 1680652920032116
exch_timestamp: 1680652860008000.0 buy 1.887 @ 28150.8
exch_timestamp: 1680652860008000.0 buy 0.139 @ 28150.8
exch_timestamp: 1680652860009000.0 buy 0.053 @ 28150.8
exch_timestamp: 1680652860580000.0 buy 0.007 @ 28150.8
exch_timestamp: 1680652860605000.0 buy 0.063 @ 28150.8
exch_timestamp: 1680652860659000.0 sell 0.006 @ 28150.7
exch_timestamp: 1680652860659000.0 sell 0.011 @ 28150.7
exch_timestamp: 1680652860674000.0 buy 0.018 @ 28150.8
exch_timestamp: 1680652860696000.0 sell 0.009 @ 28150.7
exch_timestamp: 1680652860696000.0 sell 0.061 @ 28150.7
exch_timestamp: 1680652860821000.0 sell 0.05 @ 28150.7
...
-------------------------------------------------------------------------------
current_timestamp: 1680652980032116
exch_timestamp: 1680652920308000.0 buy 0.013 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.001 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.02 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.036 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.002 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.011 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.004 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.028 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.026 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.035 @ 28144.2
exch_timestamp: 1680652920308000.0 buy 0.026 @ 28144.2
...
-------------------------------------------------------------------------------
current_timestamp: 1680653040032116
exch_timestamp: 1680652980140000.0 buy 0.086 @ 28150.0
exch_timestamp: 1680652980140000.0 buy 0.002 @ 28150.0
exch_timestamp: 1680652980140000.0 buy 0.012 @ 28150.0
exch_timestamp: 1680652980140000.0 sell 0.02 @ 28149.9
exch_timestamp: 1680652980140000.0 sell 0.488 @ 28149.9
exch_timestamp: 1680652980140000.0 sell 0.195 @ 28149.9
exch_timestamp: 1680652980140000.0 sell 0.791 @ 28149.9
exch_timestamp: 1680652980213000.0 buy 0.007 @ 28150.0
exch_timestamp: 1680652980239000.0 sell 0.062 @ 28149.9
exch_timestamp: 1680652980266000.0 buy 0.001 @ 28150.0
exch_timestamp: 1680652980271000.0 buy 0.006 @ 28150.0
...
-------------------------------------------------------------------------------
current_timestamp: 1680653100032116
exch_timestamp: 1680653040100000.0 buy 0.007 @ 28145.8
exch_timestamp: 1680653040117000.0 buy 0.001 @ 28145.8
exch_timestamp: 1680653040117000.0 buy 0.006 @ 28145.8
exch_timestamp: 1680653040119000.0 buy 0.014 @ 28145.8
exch_timestamp: 1680653040120000.0 buy 0.008 @ 28145.8
exch_timestamp: 1680653040535000.0 sell 0.2 @ 28145.7
exch_timestamp: 1680653040652000.0 buy 0.004 @ 28145.8
exch_timestamp: 1680653040652000.0 buy 0.02 @ 28145.8
exch_timestamp: 1680653040652000.0 buy 0.216 @ 28145.8
exch_timestamp: 1680653040652000.0 buy 0.904 @ 28145.8
exch_timestamp: 1680653040838000.0 buy 0.008 @ 28145.8
...
-------------------------------------------------------------------------------
current_timestamp: 1680653160032116
exch_timestamp: 1680653100101000.0 sell 0.02 @ 28140.5
exch_timestamp: 1680653100182000.0 buy 0.007 @ 28140.6
exch_timestamp: 1680653100197000.0 buy 0.005 @ 28140.6
exch_timestamp: 1680653100230000.0 sell 0.02 @ 28140.5
exch_timestamp: 1680653100303000.0 buy 0.007 @ 28140.6
exch_timestamp: 1680653100341000.0 buy 0.017 @ 28140.6
exch_timestamp: 1680653100358000.0 sell 0.009 @ 28140.5
exch_timestamp: 1680653100358000.0 sell 0.041 @ 28140.5
exch_timestamp: 1680653100628000.0 buy 0.008 @ 28140.6
exch_timestamp: 1680653100706000.0 sell 0.004 @ 28140.5
exch_timestamp: 1680653100707000.0 sell 0.001 @ 28140.5
...
[32]:
True

Rolling Volume-Weighted Average Price

[10]:
@njit
def rolling_vwap(hbt, out):
    buy_amount_bin = np.zeros(100_000, np.float64)
    buy_qty_bin = np.zeros(100_000, np.float64)
    sell_amount_bin = np.zeros(100_000, np.float64)
    sell_qty_bin = np.zeros(100_000, np.float64)

    idx = 0
    last_trade_price = np.nan

    while hbt.elapse(10 * 1e6):
        for trade in hbt.last_trades:
            if trade[COL_SIDE] == 1:
                buy_amount_bin[idx] += trade[COL_PRICE] * trade[COL_QTY]
                buy_qty_bin[idx] += trade[COL_QTY]
            else:
                sell_amount_bin[idx] += trade[COL_PRICE] * trade[COL_QTY]
                sell_qty_bin[idx] += trade[COL_QTY]
        hbt.clear_last_trades()
        idx += 1

        if idx >= 1:
            vwap10sec = np.divide(
                buy_amount_bin[idx - 1] + sell_amount_bin[idx - 1],
                buy_qty_bin[idx - 1] + sell_qty_bin[idx - 1]
            )
        else:
            vwap10sec = np.nan

        if idx >= 6:
            vwap1m = np.divide(
                np.sum(buy_amount_bin[idx - 6:idx]) + np.sum(sell_amount_bin[idx - 6:idx]),
                np.sum(buy_qty_bin[idx - 6:idx]) + np.sum(sell_qty_bin[idx - 6:idx])
            )
            buy_vwap1m = np.divide(np.sum(buy_amount_bin[idx - 6:idx]), np.sum(buy_qty_bin[idx - 6:idx]))
            sell_vwap1m = np.divide(np.sum(sell_amount_bin[idx - 6:idx]), np.sum(sell_qty_bin[idx - 6:idx]))
        else:
            vwap1m = np.nan
            buy_vwap1m = np.nan
            sell_vwap1m = np.nan

        out.append((hbt.current_timestamp, vwap10sec, vwap1m, buy_vwap1m, sell_vwap1m))
    return True
[11]:
hbt = HftBacktest(
    btcusdt_20230405,
    tick_size=0.1,
    lot_size=0.001,
    maker_fee=0.0002,
    taker_fee=0.0007,
    order_latency=FeedLatency(),
    asset_type=Linear,
    snapshot=btcusdt_20230404_eod,
    trade_list_size=1_000_000
)

tup_ty = Tuple((float64, float64, float64, float64, float64))
out = List.empty_list(tup_ty, allocated=100_000)

rolling_vwap(hbt, out)
[11]:
True
[12]:
df = pl.DataFrame(out).transpose()
df.columns = ['Local Timestamp', '10-sec VWAP', '1-min VWAP', '1-min Buy VWAP', '1-min Sell VWAP']
df = df.with_columns(
    pl.from_epoch('Local Timestamp', time_unit='us')
)

df
[12]:
shape: (41, 5)
Local Timestamp10-sec VWAP1-min VWAP1-min Buy VWAP1-min Sell VWAP
datetime[μs]f64f64f64f64
2023-04-05 00:00:10.03211628152.252939NaNNaNNaN
2023-04-05 00:00:20.03211628155.780263NaNNaNNaN
2023-04-05 00:00:30.03211628158.015906NaNNaNNaN
2023-04-05 00:00:40.03211628155.813019NaNNaNNaN
2023-04-05 00:00:50.03211628156.783983NaNNaNNaN
2023-04-05 00:01:00.03211628154.53794928155.54814128155.91552328155.223338
2023-04-05 00:01:10.03211628148.39635628153.36813228154.89094728152.319296
2023-04-05 00:01:20.03211628146.82521828152.03928928153.90190528150.83132
2023-04-05 00:01:30.03211628144.45538628150.62770628151.93409828149.81655
2023-04-05 00:01:40.03211628144.43218728149.56018128150.54068528148.888516
2023-04-05 00:01:50.03211628144.34037328147.62310728148.05773828147.411383
2023-04-05 00:02:00.03211628142.73518128146.01225628146.17516328145.918324
2023-04-05 00:05:00.03211628140.81082428142.41103728142.707128142.244012
2023-04-05 00:05:10.03211628139.18217628141.26419628142.57067728140.811206
2023-04-05 00:05:20.03211628138.9542728140.26358128140.27318528140.260421
2023-04-05 00:05:30.03211628139.4947228139.94606928139.4377128140.107281
2023-04-05 00:05:40.03211628139.72091728139.8203328139.22368328140.005444
2023-04-05 00:05:50.03211628140.15115528139.60269728139.72324328139.493739
2023-04-05 00:06:00.03211628143.47725728139.92713228140.20671228139.635456
2023-04-05 00:06:10.03211628142.32327228140.33800928140.3508628140.321614
2023-04-05 00:06:20.03211628138.5484328140.39180528140.71612428140.025369
2023-04-05 00:06:30.03211628137.61151528139.95831328139.96278128139.951255
2023-04-05 00:06:40.03211628140.10748728139.96588328139.96437328139.9681
2023-04-05 00:06:50.03211628139.44971928139.74609228139.84188628139.673659
[13]:
plt.plot(df['Local Timestamp'], df['10-sec VWAP'])
plt.plot(df['Local Timestamp'], df['1-min VWAP'])
plt.plot(df['Local Timestamp'], df['1-min Buy VWAP'])
plt.plot(df['Local Timestamp'], df['1-min Sell VWAP'])

plt.legend(['10-sec VWAP', '1-min VWAP', '1-min Buy VWAP', '1-min Sell VWAP'])
[13]:
<matplotlib.legend.Legend at 0x7f271ad2bac0>
../_images/tutorials_Working_with_Market_Depth_and_Trades_17_1.png