Getting Started
Printing the best bid and the best ask
[1]:
from numba import njit
import numpy as np
# numba.njit is strongly recommended for fast backtesting.
@njit
def print_bbo(hbt):
# Iterating until hftbacktest reaches the end of data.
# Elapses 60-sec every iteration.
# Time unit is the same as data's timestamp's unit.
# Timestamp of the sample data is in nanoseconds.
while hbt.elapse(60 * 1e9) == 0:
# Gets the market depth for the first asset.
depth = hbt.depth(0)
# Prints the best bid and the best offer.
print(
'current_timestamp:', hbt.current_timestamp,
', best_bid:', np.round(depth.best_bid, 1),
', best_ask:', np.round(depth.best_ask, 1)
)
return True
[2]:
from hftbacktest import BacktestAsset, HashMapMarketDepthBacktest
asset = (
BacktestAsset()
# Sets the data to feed for this asset.
#
# Due to the vast size of tick-by-tick market depth and trade data,
# loading the entire dataset into memory can be challenging,
# particularly when backtesting across multiple days.
# HftBacktest offers lazy loading support and is compatible with npy and preferably npz.
#
# For details on the normalized feed data, refer to the following documents.
# * https://hftbacktest.readthedocs.io/en/latest/data.html
# * https://hftbacktest.readthedocs.io/en/latest/tutorials/Data%20Preparation.html
.data(['usdm/btcusdt_20240809.npz'])
# Sets the initial snapshot (optional).
.initial_snapshot('usdm/btcusdt_20240808_eod.npz')
# Asset type:
# * Linear
# * Inverse.
# 1.0 represents the contract size, which is the value of the asset per quoted price.
.linear_asset(1.0)
# HftBacktest provides two built-in latency models.
# * constant_latency
# * intp_order_latency
# To implement your own latency model, please use Rust.
#
# Time unit is the same as data's timestamp's unit. Timestamp of the sample data is in nanoseconds.
# Sets the order entry latency and response latency to 10ms.
.constant_latency(10_000_000, 10_000_000)
# HftBacktest provides several types of built-in queue position models.
# Please find the details in the documents below.
# https://hftbacktest.readthedocs.io/en/latest/tutorials/Probability%20Queue%20Models.html
#
# To implement your own queue position model, please use Rust.
.risk_adverse_queue_model()
# HftBacktest provides two built-in exchange models.
# * no_partial_fill_exchange
# * partial_fill_exchange
# To implement your own exchange model, please use Rust.
.no_partial_fill_exchange()
# HftBacktest provides several built-in fee models.
# * trading_value_fee_model
# * trading_qty_fee_model
# * flat_per_trade_fee_model
#
# 0.02% maker fee and 0.07% taker fee. If the fee is negative, it represents a rebate.
# For example, -0.00005 represents a 0.005% rebate for the maker order.
.trading_value_fee_model(0.0002, 0.0007)
# Tick size of this asset: minimum price increasement
.tick_size(0.1)
# Lot size of this asset: minimum trading unit.
.lot_size(0.001)
# Sets the capacity of the vector that stores trades occurring in the market.
# If you set the size, you need call `clear_last_trades` to clear the vector.
# A value of 0 indicates that no market trades are stored. (Default)
.last_trades_capacity(0)
)
# HftBacktest provides several types of built-in market depth implementations.
# HashMapMarketDepthBacktest constructs a Backtest using a HashMap-based market depth implementation.
# Another useful implementation is ROIVectorMarketDepth, which is utilized in ROIVectorMarketDepthBacktest.
# Please find the details in the document below.
hbt = HashMapMarketDepthBacktest([asset])
You can see the best bid and best ask every 60 seconds. Since the price is a 32-bit float, there may be floating-point errors. Be careful when using it. In the example, for readability, the price is rounded based on the tick size.
[3]:
print_bbo(hbt)
current_timestamp: 1723161661500000000 , best_bid: 61594.1 , best_ask: 61594.2
current_timestamp: 1723161721500000000 , best_bid: 61576.5 , best_ask: 61576.6
current_timestamp: 1723161781500000000 , best_bid: 61629.6 , best_ask: 61629.7
current_timestamp: 1723161841500000000 , best_bid: 61621.5 , best_ask: 61621.6
current_timestamp: 1723161901500000000 , best_bid: 61583.9 , best_ask: 61584.0
[3]:
True
HftBacktest cannot be reused. Therefore, after using the backtest, make sure to close it. If you use the backtest after closing, it will crash.
[4]:
_ = hbt.close()
Feeding the data
When you possess adequate memory, preloading the data into memory and providing it as input will be more efficient than lazy-loading during repeated backtesting.
[5]:
btcusdt_20230809 = np.load('usdm/btcusdt_20240809.npz')['data']
btcusdt_20230808_eod = np.load('usdm/btcusdt_20240808_eod.npz')['data']
asset = (
BacktestAsset()
.data([btcusdt_20230809])
.initial_snapshot(btcusdt_20230808_eod)
.linear_asset(1.0)
.constant_latency(10_000_000, 10_000_000)
.risk_adverse_queue_model()
.no_partial_fill_exchange()
.trading_value_fee_model(0.0002, 0.0007)
.tick_size(0.1)
.lot_size(0.001)
)
[6]:
hbt = HashMapMarketDepthBacktest([asset])
print_bbo(hbt)
_ = hbt.close()
current_timestamp: 1723161661500000000 , best_bid: 61594.1 , best_ask: 61594.2
current_timestamp: 1723161721500000000 , best_bid: 61576.5 , best_ask: 61576.6
current_timestamp: 1723161781500000000 , best_bid: 61629.6 , best_ask: 61629.7
current_timestamp: 1723161841500000000 , best_bid: 61621.5 , best_ask: 61621.6
current_timestamp: 1723161901500000000 , best_bid: 61583.9 , best_ask: 61584.0
Getting the market depth
[7]:
@njit
def print_3depth(hbt):
while hbt.elapse(60 * 1e9) == 0:
print('current_timestamp:', hbt.current_timestamp)
# Gets the market depth for the first asset, in the same order as when you created the backtest.
depth = hbt.depth(0)
# a key of bid_depth or ask_depth is price in ticks.
# (integer) price_tick = price / tick_size
i = 0
for tick_price in range(depth.best_ask_tick, depth.best_ask_tick + 100):
qty = depth.ask_qty_at_tick(tick_price)
if qty > 0:
print(
'ask: ',
qty,
'@',
np.round(tick_price * depth.tick_size, 1)
)
i += 1
if i == 3:
break
i = 0
for tick_price in range(depth.best_bid_tick, max(depth.best_bid_tick - 100, 0), -1):
qty = depth.bid_qty_at_tick(tick_price)
if qty > 0:
print(
'bid: ',
qty,
'@',
np.round(tick_price * depth.tick_size, 1)
)
i += 1
if i == 3:
break
return True
[8]:
hbt = HashMapMarketDepthBacktest([asset])
print_3depth(hbt)
_ = hbt.close()
current_timestamp: 1723161661500000000
ask: 1.759 @ 61594.2
ask: 0.006 @ 61594.4
ask: 0.114 @ 61595.2
bid: 3.526 @ 61594.1
bid: 0.016 @ 61594.0
bid: 0.002 @ 61593.9
current_timestamp: 1723161721500000000
ask: 2.575 @ 61576.6
ask: 0.004 @ 61576.7
ask: 0.455 @ 61577.0
bid: 2.558 @ 61576.5
bid: 0.002 @ 61576.0
bid: 0.515 @ 61575.5
current_timestamp: 1723161781500000000
ask: 0.131 @ 61629.7
ask: 0.005 @ 61630.1
ask: 0.005 @ 61630.5
bid: 5.742 @ 61629.6
bid: 0.247 @ 61629.4
bid: 0.034 @ 61629.3
current_timestamp: 1723161841500000000
ask: 0.202 @ 61621.6
ask: 0.002 @ 61622.5
ask: 0.003 @ 61622.6
bid: 3.488 @ 61621.5
bid: 0.86 @ 61620.0
bid: 0.248 @ 61619.6
current_timestamp: 1723161901500000000
ask: 1.397 @ 61584.0
ask: 0.832 @ 61585.1
ask: 0.132 @ 61586.0
bid: 3.307 @ 61583.9
bid: 0.01 @ 61583.8
bid: 0.002 @ 61582.0
Submitting an order
[9]:
from hftbacktest import LIMIT, GTC, NONE, NEW, FILLED, CANCELED, EXPIRED
@njit
def print_orders(hbt):
# You can access open orders and also closed orders via hbt.orders.
# Gets the OrderDict for the first asset.
orders = hbt.orders(0)
# hbt.orders is a dictionary, but be aware that it does not support all dict methods, and its keys are order_id (int).
order_values = orders.values()
while order_values.has_next():
order = order_values.get()
order_status = ''
if order.status == NONE:
order_status = 'NONE' # Exchange hasn't received an order yet.
elif order.status == NEW:
order_status = 'NEW'
elif order.status == FILLED:
order_status = 'FILLED'
elif order.status == CANCELED:
order_status = 'CANCELED'
elif order.status == EXPIRED:
order_status = 'EXPIRED'
order_req = ''
if order.req == NONE:
order_req = 'NONE'
elif order.req == NEW:
order_req = 'NEW'
elif order.req == CANCELED:
order_req = 'CANCEL'
print(
'current_timestamp:', hbt.current_timestamp,
', order_id:', order.order_id,
', order_price:', np.round(order.price, 1),
', order_qty:', order.qty,
', order_status:', order_status,
', order_req:', order_req
)
@njit
def submit_order(hbt):
is_order_submitted = False
while hbt.elapse(30 * 1e9) == 0:
# Prints open orders.
print_orders(hbt)
depth = hbt.depth(0)
if not is_order_submitted:
# Submits a buy order at 300 ticks below the best bid for the first asset.
order_id = 1
order_price = depth.best_bid - 300 * depth.tick_size
order_qty = 1
time_in_force = GTC # Good 'till cancel
order_type = LIMIT
hbt.submit_buy_order(0, order_id, order_price, order_qty, time_in_force, order_type, False)
is_order_submitted = True
return True
[10]:
hbt = HashMapMarketDepthBacktest([asset])
submit_order(hbt)
_ = hbt.close()
current_timestamp: 1723161661500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161691500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161721500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161751500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161781500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161811500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161841500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161871500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161901500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
Clearing inactive orders (FILLED, CANCELED, EXPIRED)
[11]:
from hftbacktest import GTC
@njit
def clear_inactive_orders(hbt):
is_order_submitted = False
while hbt.elapse(30 * 1e9) == 0:
print_orders(hbt)
# Removes inactive(FILLED, CANCELED, EXPIRED) orders from hbt.orders for the first asset.
hbt.clear_inactive_orders(0)
depth = hbt.depth(0)
if not is_order_submitted:
order_id = 1
order_price = depth.best_bid - 300 * depth.tick_size
order_qty = 1
time_in_force = GTC
order_type = LIMIT
hbt.submit_buy_order(0, order_id, order_price, order_qty, time_in_force, order_type, False)
is_order_submitted = True
return True
[12]:
hbt = HashMapMarketDepthBacktest([asset])
clear_inactive_orders(hbt)
_ = hbt.close()
current_timestamp: 1723161661500000000 , order_id: 1 , order_price: 61643.8 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
Watching a order status - pending due to order latency
[13]:
from hftbacktest import GTC
@njit
def watch_pending(hbt):
is_order_submitted = False
# Elapses 0.01-sec every iteration.
while hbt.elapse(0.01 * 1e9) == 0:
print_orders(hbt)
hbt.clear_inactive_orders(0)
depth = hbt.depth(0)
if not is_order_submitted:
order_id = 1
order_price = depth.best_bid - 300 * depth.tick_size
order_qty = 1
time_in_force = GTC
order_type = LIMIT
hbt.submit_buy_order(0, order_id, order_price, order_qty, time_in_force, order_type, False)
is_order_submitted = True
# Prevents too many prints
orders = hbt.orders(0)
order = orders.get(order_id)
if order.status == NEW:
return False
return True
The order_status
is None
until the acceptance message is received.
[14]:
hbt = HashMapMarketDepthBacktest([asset])
watch_pending(hbt)
_ = hbt.close()
current_timestamp: 1723161601520000000 , order_id: 1 , order_price: 61629.7 , order_qty: 1.0 , order_status: NONE , order_req: NEW
current_timestamp: 1723161601530000000 , order_id: 1 , order_price: 61629.7 , order_qty: 1.0 , order_status: NEW , order_req: NONE
Waiting for an order response
[15]:
from hftbacktest import GTC
@njit
def wait_for_order_response(hbt):
order_id = 0
is_order_submitted = False
while hbt.elapse(0.01 * 1e9) == 0:
print_orders(hbt)
hbt.clear_inactive_orders(0)
# Prevents too many prints
orders = hbt.orders(0)
if order_id in orders:
if orders.get(order_id).status == NEW:
return False
depth = hbt.depth(0)
if not is_order_submitted:
order_id = 1
order_price = depth.best_bid
order_qty = 1
time_in_force = GTC
order_type = LIMIT
hbt.submit_buy_order(0, order_id, order_price, order_qty, time_in_force, order_type, False)
# Waits for the order response for a given order id for the first asset.
print('an order is submitted at', hbt.current_timestamp)
# Timeout is set 1-second.
hbt.wait_order_response(0, order_id, 1 * 1e9)
print('an order response is received at', hbt.current_timestamp)
is_order_submitted = True
return True
Since the ConstantLatency
model is used, the round-trip latency is exactly 200ms. Ideally, using historical order latency data collected from the live market is the best approach. However, if this data is not available, starting with artificially generated order latency based on feed latency is another option. We will explore this in the following examples.
[16]:
hbt = HashMapMarketDepthBacktest([asset])
wait_for_order_response(hbt)
_ = hbt.close()
an order is submitted at 1723161601510000000
an order response is received at 1723161601530000000
current_timestamp: 1723161601540000000 , order_id: 1 , order_price: 61659.7 , order_qty: 1.0 , order_status: NEW , order_req: NONE
Printing position, balance, fee, and equity
[17]:
@njit
def position(hbt):
is_order_submitted = False
while hbt.elapse(60 * 1e9) == 0:
print_orders(hbt)
hbt.clear_inactive_orders(0)
# Prints position
print(
'current_timestamp:', hbt.current_timestamp,
', position:', hbt.position(0),
', balance:', hbt.state_values(0).balance,
', fee:', hbt.state_values(0).fee
)
depth = hbt.depth(0)
if not is_order_submitted:
order_id = 1
order_price = depth.best_bid
order_qty = 1
time_in_force = GTC
order_type = LIMIT
hbt.submit_buy_order(0, order_id, order_price, order_qty, time_in_force, order_type, False)
# Timeout is set 1-second.
hbt.wait_order_response(0, order_id, 1e9)
is_order_submitted = True
return True
[18]:
hbt = HashMapMarketDepthBacktest([asset])
position(hbt)
_ = hbt.close()
current_timestamp: 1723161661500000000 , position: 0.0 , balance: 0.0 , fee: 0.0
current_timestamp: 1723161721520000000 , order_id: 1 , order_price: 61594.1 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161721520000000 , position: 1.0 , balance: -61594.100000000006 , fee: 12.318820000000002
current_timestamp: 1723161781520000000 , position: 1.0 , balance: -61594.100000000006 , fee: 12.318820000000002
current_timestamp: 1723161841520000000 , position: 1.0 , balance: -61594.100000000006 , fee: 12.318820000000002
current_timestamp: 1723161901520000000 , position: 1.0 , balance: -61594.100000000006 , fee: 12.318820000000002
Canceling an open order
[19]:
@njit
def submit_and_cancel_order(hbt):
is_order_submitted = False
while hbt.elapse(0.1 * 1e9) == 0:
print_orders(hbt)
hbt.clear_inactive_orders(0)
# Cancels if there is an open order
orders = hbt.orders(0)
order_values = orders.values()
while order_values.has_next():
order = order_values.get()
# an order is only cancellable if order status is NEW.
# cancel request is negated if the order is already filled or filled before cancel request is processed.
if order.cancellable:
hbt.cancel(0, order.order_id, False)
# You can see status still NEW and see req CANCEL.
print_orders(hbt)
# cancels request also has order entry/response latencies the same as submitting.
hbt.wait_order_response(0, order.order_id, 1e9)
if not is_order_submitted:
depth = hbt.depth(0)
order_id = 1
order_price = depth.best_bid - 100 * depth.tick_size
order_qty = 1
time_in_force = GTC
order_type = LIMIT
hbt.submit_buy_order(0, order_id, order_price, order_qty, time_in_force, order_type, False)
# Timeout is set 1-second.
hbt.wait_order_response(0, order_id, 1e9)
is_order_submitted = True
else:
if len(hbt.orders(0)) == 0:
return False
return True
[20]:
hbt = HashMapMarketDepthBacktest([asset])
submit_and_cancel_order(hbt)
_ = hbt.close()
current_timestamp: 1723161601720000000 , order_id: 1 , order_price: 61649.7 , order_qty: 1.0 , order_status: NEW , order_req: NONE
current_timestamp: 1723161601720000000 , order_id: 1 , order_price: 61649.7 , order_qty: 1.0 , order_status: NEW , order_req: CANCEL
current_timestamp: 1723161601840000000 , order_id: 1 , order_price: 61649.7 , order_qty: 1.0 , order_status: CANCELED , order_req: NONE
Market order
[21]:
from hftbacktest import MARKET
@njit
def print_orders_exec_price(hbt):
orders = hbt.orders(0)
order_values = orders.values()
while order_values.has_next():
order = order_values.get()
order_status = ''
if order.status == NONE:
order_status = 'NONE'
elif order.status == NEW:
order_status = 'NEW'
elif order.status == FILLED:
order_status = 'FILLED'
elif order.status == CANCELED:
order_status = 'CANCELED'
elif order.status == EXPIRED:
order_status = 'EXPIRED'
order_req = ''
if order.req == NONE:
order_req = 'NONE'
elif order.req == NEW:
order_req = 'NEW'
elif order.req == CANCELED:
order_req = 'CANCEL'
print(
'current_timestamp:', hbt.current_timestamp,
', order_id:', order.order_id,
', order_price:', np.round(order.price, 1),
', order_qty:', order.qty,
', order_status:', order_status,
', exec_price:', np.round(order.exec_price, 1)
)
@njit
def market_order(hbt):
is_order_submitted = False
while hbt.elapse(60 * 1e9) == 0:
print_orders(hbt)
hbt.clear_inactive_orders(0)
state_values = hbt.state_values(0)
print(
'current_timestamp:', hbt.current_timestamp,
', position:', hbt.position(0),
', balance:', state_values.balance,
', fee:', state_values.fee
)
if not is_order_submitted:
depth = hbt.depth(0)
order_id = 1
# Sets an arbitrary price, which does not affect MARKET orders.
order_price = depth.best_bid
order_qty = 1
time_in_force = GTC
order_type = MARKET
hbt.submit_sell_order(0, order_id, order_price, order_qty, time_in_force, order_type, False)
hbt.wait_order_response(0, order_id, 1e9)
# You can see the order immediately filled.
# Also you can see the order executed at the best bid which is different from what it was submitted at.
print('best_bid:', depth.best_bid)
print_orders_exec_price(hbt)
is_order_submitted = True
return True
[22]:
hbt = HashMapMarketDepthBacktest([asset])
market_order(hbt)
_ = hbt.close()
current_timestamp: 1723161661500000000 , position: 0.0 , balance: 0.0 , fee: 0.0
best_bid: 61594.100000000006
current_timestamp: 1723161661520000000 , order_id: 1 , order_price: 61594.1 , order_qty: 1.0 , order_status: FILLED , exec_price: 61594.1
current_timestamp: 1723161721520000000 , order_id: 1 , order_price: 61594.1 , order_qty: 1.0 , order_status: FILLED , order_req: NONE
current_timestamp: 1723161721520000000 , position: -1.0 , balance: 61594.100000000006 , fee: 43.11587
current_timestamp: 1723161781520000000 , position: -1.0 , balance: 61594.100000000006 , fee: 43.11587
current_timestamp: 1723161841520000000 , position: -1.0 , balance: 61594.100000000006 , fee: 43.11587
current_timestamp: 1723161901520000000 , position: -1.0 , balance: 61594.100000000006 , fee: 43.11587
GTX, Post-Only order
[23]:
from hftbacktest import GTX
@njit
def submit_gtx(hbt):
is_order_submitted = False
while hbt.elapse(60 * 1e9) == 0:
print_orders(hbt)
hbt.clear_inactive_orders(0)
state_values = hbt.state_values(0)
print(
'current_timestamp:', hbt.current_timestamp,
', position:', hbt.position(0),
', balance:', state_values.balance,
', fee:', state_values.fee
)
if not is_order_submitted:
depth = hbt.depth(0)
order_id = 1
# Sets a deep price in the opposite side and it will be rejected by GTX.
order_price = depth.best_bid - 100 * depth.tick_size
order_qty = 1
time_in_force = GTX
order_type = LIMIT
hbt.submit_sell_order(0, order_id, order_price, order_qty, time_in_force, order_type, False)
hbt.wait_order_response(0, order_id, 1e9)
is_order_submitted = True
return True
[24]:
hbt = HashMapMarketDepthBacktest([asset])
submit_gtx(hbt)
_ = hbt.close()
current_timestamp: 1723161661500000000 , position: 0.0 , balance: 0.0 , fee: 0.0
current_timestamp: 1723161721520000000 , order_id: 1 , order_price: 61584.1 , order_qty: 1.0 , order_status: EXPIRED , order_req: NONE
current_timestamp: 1723161721520000000 , position: 0.0 , balance: 0.0 , fee: 0.0
current_timestamp: 1723161781520000000 , position: 0.0 , balance: 0.0 , fee: 0.0
current_timestamp: 1723161841520000000 , position: 0.0 , balance: 0.0 , fee: 0.0
current_timestamp: 1723161901520000000 , position: 0.0 , balance: 0.0 , fee: 0.0
Plotting BBO
[25]:
@njit
def plot_bbo(hbt, local_timestamp, best_bid, best_ask):
while hbt.elapse(1 * 1e9) == 0:
# Records data points
local_timestamp.append(hbt.current_timestamp)
depth = hbt.depth(0)
best_bid.append(depth.best_bid)
best_ask.append(depth.best_ask)
return True
[26]:
# Uses Numba list for njit.
from numba.typed import List
from numba import int64, float64
import polars as pl
local_timestamp = List.empty_list(int64, allocated=10000)
best_bid = List.empty_list(float64, allocated=10000)
best_ask = List.empty_list(float64, allocated=10000)
hbt = HashMapMarketDepthBacktest([asset])
plot_bbo(hbt, local_timestamp, best_bid, best_ask)
hbt.close()
df = pl.DataFrame({'timestamp': local_timestamp, 'best_bid': best_bid, 'best_ask': best_ask})
df = df.with_columns(
pl.from_epoch('timestamp', time_unit='ns')
)
df.plot(x='timestamp')
[26]:
Printing stats
[27]:
@njit
def submit_order_stats(hbt, recorder):
buy_order_id = 1
sell_order_id = 2
half_spread = 5 * hbt.depth(0).tick_size
while hbt.elapse(1 * 1e9) == 0:
hbt.clear_inactive_orders(0)
depth = hbt.depth(0)
mid_price = (depth.best_bid + depth.best_ask) / 2.0
if buy_order_id not in hbt.orders(0):
order_price = round((mid_price - half_spread) / depth.tick_size) * depth.tick_size
order_qty = 1
time_in_force = GTX
order_type = LIMIT
hbt.submit_buy_order(0, buy_order_id, order_price, order_qty, time_in_force, order_type, False)
else:
hbt.cancel(0, buy_order_id, False)
if sell_order_id not in hbt.orders(0):
order_price = round((mid_price + half_spread) / depth.tick_size) * depth.tick_size
order_qty = 1
time_in_force = GTX
order_type = LIMIT
hbt.submit_sell_order(0, sell_order_id, order_price, order_qty, time_in_force, order_type, False)
else:
hbt.cancel(0, sell_order_id, False)
recorder.record(hbt)
return True
[28]:
from hftbacktest import Recorder
hbt = HashMapMarketDepthBacktest([asset])
recorder = Recorder(
# The number of assets
hbt.num_assets,
# The buffer size for records
1000000
)
submit_order_stats(hbt, recorder.recorder)
_ = hbt.close()
You can get recorded states using the get
method with the asset number.
[29]:
recorder.get(0)
[29]:
array([(1723161602500000000, 61659.85, 0., 0.000000e+00, 0. , 0, 0., 0. ),
(1723161603500000000, 61659.95, 1., -6.165940e+04, 12.33188, 1, 1., 61659.4),
(1723161604500000000, 61670.85, 1., -6.165940e+04, 12.33188, 1, 1., 61659.4),
(1723161605500000000, 61692.45, 0., 1.200000e+01, 24.66616, 2, 2., 123330.8),
(1723161606500000000, 61693.95, 0., 1.300000e+01, 49.34312, 4, 4., 246715.6),
(1723161607500000000, 61695.45, -1., 6.170740e+04, 61.682 , 5, 5., 308410. ),
(1723161608500000000, 61709.95, -2., 1.234033e+05, 74.02118, 6, 6., 370105.9),
(1723161609500000000, 61707.35, -1., 6.169390e+04, 86.36306, 7, 7., 431815.3),
(1723161610500000000, 61715.85, -1., 6.169390e+04, 86.36306, 7, 7., 431815.3),
(1723161611500000000, 61711.85, -2., 1.234103e+05, 98.70634, 8, 8., 493531.7),
(1723161612500000000, 61713.95, -3., 1.851227e+05, 111.04882, 9, 9., 555244.1),
(1723161613500000000, 61706.15, -4., 2.468371e+05, 123.3917 , 10, 10., 616958.5),
(1723161614500000000, 61708.25, -5., 3.085437e+05, 135.73302, 11, 11., 678665.1),
(1723161615500000000, 61699.75, -6., 3.702525e+05, 148.07478, 12, 12., 740373.9),
(1723161616500000000, 61700.95, -7., 4.319527e+05, 160.41482, 13, 13., 802074.1),
(1723161617500000000, 61698.05, -7., 4.319527e+05, 160.41482, 13, 13., 802074.1),
(1723161618500000000, 61706.95, -7., 4.319527e+05, 160.41482, 13, 13., 802074.1),
(1723161619500000000, 61695.85, -7., 4.319527e+05, 160.41482, 13, 13., 802074.1),
(1723161620500000000, 61713.45, -7., 4.319527e+05, 160.41482, 13, 13., 802074.1),
(1723161621500000000, 61707.65, -7., 4.319527e+05, 160.41482, 13, 13., 802074.1),
(1723161622500000000, 61713.45, -7., 4.319527e+05, 160.41482, 13, 13., 802074.1),
(1723161623500000000, 61704.05, -6., 3.702455e+05, 172.75626, 14, 14., 863781.3),
(1723161624500000000, 61702.45, -5., 3.085419e+05, 185.09698, 15, 15., 925484.9),
(1723161625500000000, 61704.65, -6., 3.702448e+05, 197.43756, 16, 16., 987187.8),
(1723161626500000000, 61704.65, -6., 3.702448e+05, 197.43756, 16, 16., 987187.8),
(1723161627500000000, 61695.35, -5., 3.085406e+05, 209.7784 , 17, 17., 1048892. ),
(1723161628500000000, 61693.75, -4., 2.468458e+05, 222.11736, 18, 18., 1110586.8),
(1723161629500000000, 61693.75, -4., 2.468458e+05, 222.11736, 18, 18., 1110586.8),
(1723161630500000000, 61682.35, -4., 2.468458e+05, 222.11736, 18, 18., 1110586.8),
(1723161631500000000, 61673.85, -3., 1.851640e+05, 234.45372, 19, 19., 1172268.6),
(1723161632500000000, 61666.05, -2., 1.234906e+05, 246.7884 , 20, 20., 1233942. ),
(1723161633500000000, 61671.05, -2., 1.234906e+05, 246.7884 , 20, 20., 1233942. ),
(1723161634500000000, 61673.75, -3., 1.851622e+05, 259.12272, 21, 21., 1295613.6),
(1723161635500000000, 61673.75, -3., 1.851622e+05, 259.12272, 21, 21., 1295613.6),
(1723161636500000000, 61666.05, -3., 1.851622e+05, 259.12272, 21, 21., 1295613.6),
(1723161637500000000, 61670.45, -4., 2.468288e+05, 271.45604, 22, 22., 1357280.2),
(1723161638500000000, 61664.05, -4., 2.468288e+05, 271.45604, 22, 22., 1357280.2),
(1723161639500000000, 61649.05, -3., 1.851652e+05, 283.78876, 23, 23., 1418943.8),
(1723161640500000000, 61645.05, -3., 1.851652e+05, 283.78876, 23, 23., 1418943.8),
(1723161641500000000, 61640.05, -2., 1.235206e+05, 296.11768, 24, 24., 1480588.4),
(1723161642500000000, 61638.45, -1., 6.188100e+04, 308.4456 , 25, 25., 1542228. ),
(1723161643500000000, 61636.05, 0., 2.431000e+02, 320.77318, 26, 26., 1603865.9),
(1723161644500000000, 61641.95, -1., 6.187970e+04, 333.1005 , 27, 27., 1665502.5),
(1723161645500000000, 61641.95, -1., 6.187970e+04, 333.1005 , 27, 27., 1665502.5),
(1723161646500000000, 61644.35, -1., 6.187970e+04, 333.1005 , 27, 27., 1665502.5),
(1723161647500000000, 61636.45, -1., 6.187970e+04, 333.1005 , 27, 27., 1665502.5),
(1723161648500000000, 61630.05, 0., 2.438000e+02, 345.42768, 28, 28., 1727138.4),
(1723161649500000000, 61630.05, 0., 2.438000e+02, 345.42768, 28, 28., 1727138.4),
(1723161650500000000, 61631.65, 0., 2.438000e+02, 345.42768, 28, 28., 1727138.4),
(1723161651500000000, 61639.05, -1., 6.187600e+04, 357.75412, 29, 29., 1788770.6),
(1723161652500000000, 61632.05, -1., 6.187600e+04, 357.75412, 29, 29., 1788770.6),
(1723161653500000000, 61633.95, -1., 6.187600e+04, 357.75412, 29, 29., 1788770.6),
(1723161654500000000, 61632.05, -2., 1.235104e+05, 370.081 , 30, 30., 1850405. ),
(1723161655500000000, 61604.05, -1., 6.187880e+04, 382.40732, 31, 31., 1912036.6),
(1723161656500000000, 61604.05, -1., 6.187880e+04, 382.40732, 31, 31., 1912036.6),
(1723161657500000000, 61607.05, -1., 6.187880e+04, 382.40732, 31, 31., 1912036.6),
(1723161658500000000, 61603.15, 0., 2.722000e+02, 394.72864, 32, 32., 1973643.2),
(1723161659500000000, 61601.15, 1., -6.133040e+04, 407.04916, 33, 33., 2035245.8),
(1723161660500000000, 61595.35, 2., -1.229310e+05, 419.36928, 34, 34., 2096846.4),
(1723161661500000000, 61594.15, 3., -1.845258e+05, 431.68824, 35, 35., 2158441.2),
(1723161662500000000, 61578.15, 4., -2.461194e+05, 444.00696, 36, 36., 2220034.8),
(1723161663500000000, 61565.25, 5., -3.076970e+05, 456.32248, 37, 37., 2281612.4),
(1723161664500000000, 61563.65, 5., -3.076960e+05, 480.9486 , 39, 39., 2404743. ),
(1723161665500000000, 61555.05, 6., -3.692592e+05, 493.26124, 40, 40., 2466306.2),
(1723161666500000000, 61530.85, 7., -4.308138e+05, 505.57216, 41, 41., 2527860.8),
(1723161667500000000, 61522.25, 8., -4.923442e+05, 517.87824, 42, 42., 2589391.2),
(1723161668500000000, 61543. , 7., -4.308214e+05, 530.1828 , 43, 43., 2650914. ),
(1723161669500000000, 61528.05, 7., -4.308214e+05, 530.1828 , 43, 43., 2650914. ),
(1723161670500000000, 61539.85, 8., -4.923490e+05, 542.48832, 44, 44., 2712441.6),
(1723161671500000000, 61524.15, 9., -5.538884e+05, 554.7962 , 45, 45., 2773981. ),
(1723161672500000000, 61524.25, 9., -5.538884e+05, 554.7962 , 45, 45., 2773981. ),
(1723161673500000000, 61535.95, 8., -4.923636e+05, 567.10116, 46, 46., 2835505.8),
(1723161674500000000, 61531.45, 9., -5.538990e+05, 579.40824, 47, 47., 2897041.2),
(1723161675500000000, 61538.85, 9., -5.538990e+05, 579.40824, 47, 47., 2897041.2),
(1723161676500000000, 61536.95, 9., -5.538990e+05, 579.40824, 47, 47., 2897041.2),
(1723161677500000000, 61538.85, 9., -5.538990e+05, 579.40824, 47, 47., 2897041.2),
(1723161678500000000, 61534.75, 9., -5.538990e+05, 579.40824, 47, 47., 2897041.2),
(1723161679500000000, 61538.85, 9., -5.538990e+05, 579.40824, 47, 47., 2897041.2),
(1723161680500000000, 61538.05, 9., -5.538990e+05, 579.40824, 47, 47., 2897041.2),
(1723161681500000000, 61549.25, 9., -5.538990e+05, 579.40824, 47, 47., 2897041.2),
(1723161682500000000, 61552.45, 8., -4.923492e+05, 591.7182 , 48, 48., 2958591. ),
(1723161683500000000, 61552.45, 8., -4.923492e+05, 591.7182 , 48, 48., 2958591. ),
(1723161684500000000, 61552.45, 8., -4.923492e+05, 591.7182 , 48, 48., 2958591. ),
(1723161685500000000, 61565.95, 7., -4.307963e+05, 604.02878, 49, 49., 3020143.9),
(1723161686500000000, 61574.45, 6., -3.692299e+05, 616.34206, 50, 50., 3081710.3),
(1723161687500000000, 61587.55, 6., -3.692299e+05, 616.34206, 50, 50., 3081710.3),
(1723161688500000000, 61592.95, 5., -3.076419e+05, 628.65966, 51, 51., 3143298.3),
(1723161689500000000, 61592.95, 5., -3.076419e+05, 628.65966, 51, 51., 3143298.3),
(1723161690500000000, 61594.15, 5., -3.076419e+05, 628.65966, 51, 51., 3143298.3),
(1723161691500000000, 61598.95, 4., -2.460473e+05, 640.97858, 52, 52., 3204892.9),
(1723161692500000000, 61593.05, 3., -1.844479e+05, 653.29846, 53, 53., 3266492.3),
(1723161693500000000, 61582.55, 3., -1.844479e+05, 653.29846, 53, 53., 3266492.3),
(1723161694500000000, 61582.55, 4., -2.460299e+05, 665.61486, 54, 54., 3328074.3),
(1723161695500000000, 61582.55, 4., -2.460299e+05, 665.61486, 54, 54., 3328074.3),
(1723161696500000000, 61587.15, 4., -2.460299e+05, 665.61486, 54, 54., 3328074.3),
(1723161697500000000, 61587.15, 4., -2.460299e+05, 665.61486, 54, 54., 3328074.3),
(1723161698500000000, 61588.75, 4., -2.460299e+05, 665.61486, 54, 54., 3328074.3),
(1723161699500000000, 61586.75, 4., -2.460289e+05, 690.25034, 56, 56., 3451251.7),
(1723161700500000000, 61582.05, 5., -3.076151e+05, 702.56758, 57, 57., 3512837.9),
(1723161701500000000, 61572.05, 6., -3.691967e+05, 714.8839 , 58, 58., 3574419.5),
(1723161702500000000, 61587.45, 5., -3.076241e+05, 727.19842, 59, 59., 3635992.1),
(1723161703500000000, 61577.95, 5., -3.076241e+05, 727.19842, 59, 59., 3635992.1),
(1723161704500000000, 61582.05, 5., -3.076241e+05, 727.19842, 59, 59., 3635992.1),
(1723161705500000000, 61572.05, 5., -3.076189e+05, 751.83042, 61, 61., 3759152.1),
(1723161706500000000, 61574.05, 4., -2.460463e+05, 764.14494, 62, 62., 3820724.7),
(1723161707500000000, 61574.05, 4., -2.460463e+05, 764.14494, 62, 62., 3820724.7),
(1723161708500000000, 61576.05, 3., -1.844717e+05, 776.45986, 63, 63., 3882299.3),
(1723161709500000000, 61577.55, 2., -1.228951e+05, 788.77518, 64, 64., 3943875.9),
(1723161710500000000, 61581.95, 1., -6.131710e+04, 801.09078, 65, 65., 4005453.9),
(1723161711500000000, 61565.65, 1., -6.131710e+04, 801.09078, 65, 65., 4005453.9),
(1723161712500000000, 61561.15, 2., -1.228823e+05, 813.40382, 66, 66., 4067019.1),
(1723161713500000000, 61570.45, 2., -1.228813e+05, 838.02826, 68, 68., 4190141.3),
(1723161714500000000, 61572.45, 1., -6.131040e+04, 850.34244, 69, 69., 4251712.2),
(1723161715500000000, 61565.65, 1., -6.131040e+04, 850.34244, 69, 69., 4251712.2),
(1723161716500000000, 61561.95, 2., -1.228756e+05, 862.65548, 70, 70., 4313277.4),
(1723161717500000000, 61557.05, 3., -1.844370e+05, 874.96776, 71, 71., 4374838.8),
(1723161718500000000, 61561.95, 3., -1.844370e+05, 874.96776, 71, 71., 4374838.8),
(1723161719500000000, 61568.05, 2., -1.228746e+05, 887.28024, 72, 72., 4436401.2),
(1723161720500000000, 61576.55, 1., -6.130600e+04, 899.59396, 73, 73., 4497969.8),
(1723161721500000000, 61576.55, 1., -6.130600e+04, 899.59396, 73, 73., 4497969.8),
(1723161722500000000, 61589.95, 1., -6.130600e+04, 899.59396, 73, 73., 4497969.8),
(1723161723500000000, 61593.95, 0., 2.844000e+02, 911.91204, 74, 74., 4559560.2),
(1723161724500000000, 61615.15, -1., 6.187880e+04, 924.23092, 75, 75., 4621154.6),
(1723161725500000000, 61615.15, -1., 6.187880e+04, 924.23092, 75, 75., 4621154.6),
(1723161726500000000, 61615.15, -1., 6.187880e+04, 924.23092, 75, 75., 4621154.6),
(1723161727500000000, 61617.05, -2., 1.234944e+05, 936.55404, 76, 76., 4682770.2),
(1723161728500000000, 61618.15, -3., 1.851120e+05, 948.87756, 77, 77., 4744387.8),
(1723161729500000000, 61612.55, -3., 1.851120e+05, 948.87756, 77, 77., 4744387.8),
(1723161730500000000, 61609.95, -2., 1.235000e+05, 961.19996, 78, 78., 4805999.8),
(1723161731500000000, 61607.95, -1., 6.189060e+04, 973.52184, 79, 79., 4867609.2),
(1723161732500000000, 61608.95, -1., 6.189060e+04, 973.52184, 79, 79., 4867609.2),
(1723161733500000000, 61606.05, -1., 6.189060e+04, 973.52184, 79, 79., 4867609.2),
(1723161734500000000, 61608.45, -1., 6.189060e+04, 973.52184, 79, 79., 4867609.2),
(1723161735500000000, 61615.95, -2., 1.234995e+05, 985.84362, 80, 80., 4929218.1),
(1723161736500000000, 61618.15, -3., 1.851159e+05, 998.1669 , 81, 81., 4990834.5),
(1723161737500000000, 61605.55, -3., 1.851159e+05, 998.1669 , 81, 81., 4990834.5),
(1723161738500000000, 61613.85, -3., 1.851159e+05, 998.1669 , 81, 81., 4990834.5),
(1723161739500000000, 61619.95, -4., 2.467303e+05, 1010.48978, 82, 82., 5052448.9),
(1723161740500000000, 61636.65, -4., 2.467303e+05, 1010.48978, 82, 82., 5052448.9),
(1723161741500000000, 61649.75, -5., 3.083675e+05, 1022.81722, 83, 83., 5114086.1),
(1723161742500000000, 61653.45, -6., 3.700177e+05, 1035.14726, 84, 84., 5175736.3),
(1723161743500000000, 61668.55, -7., 4.316716e+05, 1047.47804, 85, 85., 5237390.2),
(1723161744500000000, 61668.55, -7., 4.316716e+05, 1047.47804, 85, 85., 5237390.2),
(1723161745500000000, 61673.45, -6., 3.700036e+05, 1059.81164, 86, 86., 5299058.2),
(1723161746500000000, 61675.55, -7., 4.316775e+05, 1072.14642, 87, 87., 5360732.1),
(1723161747500000000, 61671.35, -8., 4.933535e+05, 1084.48162, 88, 88., 5422408.1),
(1723161748500000000, 61656.75, -7., 4.316827e+05, 1096.81578, 89, 89., 5484078.9),
(1723161749500000000, 61660.05, -7., 4.316827e+05, 1096.81578, 89, 89., 5484078.9),
(1723161750500000000, 61662.05, -8., 4.933433e+05, 1109.1479 , 90, 90., 5545739.5),
(1723161751500000000, 61652.05, -7., 4.316817e+05, 1121.48022, 91, 91., 5607401.1),
(1723161752500000000, 61673.45, -7., 4.316817e+05, 1121.48022, 91, 91., 5607401.1),
(1723161753500000000, 61680.65, -8., 4.933556e+05, 1133.815 , 92, 92., 5669075. ),
(1723161754500000000, 61672.45, -7., 4.316754e+05, 1146.15104, 93, 93., 5730755.2),
(1723161755500000000, 61659.95, -6., 3.700035e+05, 1158.48542, 94, 94., 5792427.1),
(1723161756500000000, 61661.25, -7., 4.316639e+05, 1170.8175 , 95, 95., 5854087.5),
(1723161757500000000, 61654.25, -7., 4.316639e+05, 1170.8175 , 95, 95., 5854087.5),
(1723161758500000000, 61650.05, -6., 3.700101e+05, 1183.14826, 96, 96., 5915741.3),
(1723161759500000000, 61650.05, -6., 3.700101e+05, 1183.14826, 96, 96., 5915741.3),
(1723161760500000000, 61654.25, -6., 3.700101e+05, 1183.14826, 96, 96., 5915741.3),
(1723161761500000000, 61652.65, -5., 3.083563e+05, 1195.47902, 97, 97., 5977395.1),
(1723161762500000000, 61663.95, -5., 3.083563e+05, 1195.47902, 97, 97., 5977395.1),
(1723161763500000000, 61656.05, -5., 3.083563e+05, 1195.47902, 97, 97., 5977395.1),
(1723161764500000000, 61656.05, -6., 3.700129e+05, 1207.81034, 98, 98., 6039051.7),
(1723161765500000000, 61626.35, -6., 3.700129e+05, 1207.81034, 98, 98., 6039051.7),
(1723161766500000000, 61629.85, -6., 3.700129e+05, 1207.81034, 98, 98., 6039051.7),
(1723161767500000000, 61629.85, -6., 3.700129e+05, 1207.81034, 98, 98., 6039051.7),
(1723161768500000000, 61633.05, -6., 3.700129e+05, 1207.81034, 98, 98., 6039051.7),
(1723161769500000000, 61645.45, -7., 4.316465e+05, 1220.13706, 99, 99., 6100685.3),
(1723161770500000000, 61645.45, -7., 4.316465e+05, 1220.13706, 99, 99., 6100685.3),
(1723161771500000000, 61649.75, -7., 4.316465e+05, 1220.13706, 99, 99., 6100685.3),
(1723161772500000000, 61640.85, -7., 4.316465e+05, 1220.13706, 99, 99., 6100685.3),
(1723161773500000000, 61642.05, -7., 4.316465e+05, 1220.13706, 99, 99., 6100685.3),
(1723161774500000000, 61629.85, -7., 4.316465e+05, 1220.13706, 99, 99., 6100685.3),
(1723161775500000000, 61629.75, -7., 4.316465e+05, 1220.13706, 99, 99., 6100685.3),
(1723161776500000000, 61635.25, -8., 4.932767e+05, 1232.4631 , 100, 100., 6162315.5),
(1723161777500000000, 61618.75, -7., 4.316419e+05, 1244.79006, 101, 101., 6223950.3),
(1723161778500000000, 61615.75, -6., 3.700237e+05, 1257.1137 , 102, 102., 6285568.5),
(1723161779500000000, 61602.7 , -5., 3.084085e+05, 1269.43674, 103, 103., 6347183.7),
(1723161780500000000, 61609.75, -4., 2.468063e+05, 1281.75718, 104, 104., 6408785.9),
(1723161781500000000, 61629.65, -5., 3.084165e+05, 1294.07922, 105, 105., 6470396.1),
(1723161782500000000, 61634.15, -6., 3.700467e+05, 1306.40526, 106, 106., 6532026.3),
(1723161783500000000, 61631.95, -5., 3.084131e+05, 1318.73198, 107, 107., 6593659.9),
(1723161784500000000, 61625.55, -4., 2.467817e+05, 1331.05826, 108, 108., 6655291.3),
(1723161785500000000, 61617.05, -3., 1.851567e+05, 1343.38326, 109, 109., 6716916.3),
(1723161786500000000, 61620.05, -2., 1.235401e+05, 1355.70658, 110, 110., 6778532.9),
(1723161787500000000, 61620.05, -2., 1.235401e+05, 1355.70658, 110, 110., 6778532.9),
(1723161788500000000, 61620.05, -2., 1.235401e+05, 1355.70658, 110, 110., 6778532.9),
(1723161789500000000, 61617.05, -1., 6.192050e+04, 1368.0305 , 111, 111., 6840152.5),
(1723161790500000000, 61616.15, 0., 3.039000e+02, 1380.35382, 112, 112., 6901769.1),
(1723161791500000000, 61616.15, 0., 3.039000e+02, 1380.35382, 112, 112., 6901769.1),
(1723161792500000000, 61618.05, 0., 3.039000e+02, 1380.35382, 112, 112., 6901769.1),
(1723161793500000000, 61642.05, -1., 6.192250e+04, 1392.67754, 113, 113., 6963387.7),
(1723161794500000000, 61649.95, -2., 1.235651e+05, 1405.00606, 114, 114., 7025030.3),
(1723161795500000000, 61666.45, -3., 1.852155e+05, 1417.33614, 115, 115., 7086680.7),
(1723161796500000000, 61658.05, -3., 1.852155e+05, 1417.33614, 115, 115., 7086680.7),
(1723161797500000000, 61648.45, -2., 1.235579e+05, 1429.66766, 116, 116., 7148338.3),
(1723161798500000000, 61642.05, -2., 1.235589e+05, 1454.32702, 118, 118., 7271635.1),
(1723161799500000000, 61638.85, -1., 6.191730e+04, 1466.65534, 119, 119., 7333276.7),
(1723161800500000000, 61631.95, 0., 2.789000e+02, 1478.98302, 120, 120., 7394915.1),
(1723161801500000000, 61623.05, 1., -6.135250e+04, 1491.3093 , 121, 121., 7456546.5),
(1723161802500000000, 61626.65, 1., -6.135250e+04, 1491.3093 , 121, 121., 7456546.5),
(1723161803500000000, 61625.05, 1., -6.135250e+04, 1491.3093 , 121, 121., 7456546.5),
(1723161804500000000, 61612.05, 2., -1.229771e+05, 1503.63422, 122, 122., 7518171.1),
(1723161805500000000, 61618.05, 1., -6.136450e+04, 1515.95674, 123, 123., 7579783.7),
(1723161806500000000, 61616.15, 1., -6.136450e+04, 1515.95674, 123, 123., 7579783.7),
(1723161807500000000, 61616.15, 1., -6.136450e+04, 1515.95674, 123, 123., 7579783.7),
(1723161808500000000, 61602.45, 1., -6.136450e+04, 1515.95674, 123, 123., 7579783.7),
(1723161809500000000, 61592.05, 2., -1.229664e+05, 1528.27712, 124, 124., 7641385.6),
(1723161810500000000, 61580.25, 3., -1.845580e+05, 1540.59544, 125, 125., 7702977.2),
(1723161811500000000, 61580.25, 3., -1.845580e+05, 1540.59544, 125, 125., 7702977.2),
(1723161812500000000, 61586.25, 2., -1.229772e+05, 1552.9116 , 126, 126., 7764558. ),
(1723161813500000000, 61594.45, 1., -6.139040e+04, 1565.22896, 127, 127., 7826144.8),
(1723161814500000000, 61606.85, 0., 2.045000e+02, 1577.54794, 128, 128., 7887739.7),
(1723161815500000000, 61630.35, -1., 6.181190e+04, 1589.86942, 129, 129., 7949347.1),
(1723161816500000000, 61638.05, -2., 1.234427e+05, 1602.19558, 130, 130., 8010977.9),
(1723161817500000000, 61626.25, -2., 1.234427e+05, 1602.19558, 130, 130., 8010977.9),
(1723161818500000000, 61626.25, -2., 1.234427e+05, 1602.19558, 130, 130., 8010977.9),
(1723161819500000000, 61613.65, -2., 1.234427e+05, 1602.19558, 130, 130., 8010977.9),
(1723161820500000000, 61608.15, -1., 6.182950e+04, 1614.51822, 131, 131., 8072591.1),
(1723161821500000000, 61624.65, -2., 1.234381e+05, 1626.83994, 132, 132., 8134199.7),
(1723161822500000000, 61624.65, -2., 1.234381e+05, 1626.83994, 132, 132., 8134199.7),
(1723161823500000000, 61624.65, -2., 1.234381e+05, 1626.83994, 132, 132., 8134199.7),
(1723161824500000000, 61624.65, -2., 1.234381e+05, 1626.83994, 132, 132., 8134199.7),
(1723161825500000000, 61622.55, -1., 6.181390e+04, 1639.16478, 133, 133., 8195823.9),
(1723161826500000000, 61622.55, -1., 6.181390e+04, 1639.16478, 133, 133., 8195823.9),
(1723161827500000000, 61621.65, -1., 6.181390e+04, 1639.16478, 133, 133., 8195823.9),
(1723161828500000000, 61615.95, 0., 1.927000e+02, 1651.48902, 134, 134., 8257445.1),
(1723161829500000000, 61621.55, 1., -6.142270e+04, 1663.8121 , 135, 135., 8319060.5),
(1723161830500000000, 61621.55, 1., -6.142270e+04, 1663.8121 , 135, 135., 8319060.5),
(1723161831500000000, 61614.05, 1., -6.142270e+04, 1663.8121 , 135, 135., 8319060.5),
(1723161832500000000, 61611.55, 2., -1.230363e+05, 1676.13482, 136, 136., 8380674.1),
(1723161833500000000, 61620.05, 2., -1.230363e+05, 1676.13482, 136, 136., 8380674.1),
(1723161834500000000, 61622.55, 1., -6.141570e+04, 1688.45894, 137, 137., 8442294.7),
(1723161835500000000, 61621.55, 1., -6.141470e+04, 1713.10794, 139, 139., 8565539.7),
(1723161836500000000, 61630.35, 0., 2.073000e+02, 1725.43234, 140, 140., 8627161.7),
(1723161837500000000, 61613.75, -1., 6.183810e+04, 1737.7585 , 141, 141., 8688792.5),
(1723161838500000000, 61613.75, -1., 6.183810e+04, 1737.7585 , 141, 141., 8688792.5),
(1723161839500000000, 61605.05, -1., 6.183810e+04, 1737.7585 , 141, 141., 8688792.5),
(1723161840500000000, 61616.05, -2., 1.234437e+05, 1750.07962, 142, 142., 8750398.1),
(1723161841500000000, 61621.55, -3., 1.850603e+05, 1762.40294, 143, 143., 8812014.7),
(1723161842500000000, 61633.95, -4., 2.466823e+05, 1774.72734, 144, 144., 8873636.7),
(1723161843500000000, 61638.05, -5., 3.083167e+05, 1787.05422, 145, 145., 8935271.1),
(1723161844500000000, 61634.95, -4., 2.466791e+05, 1799.38174, 146, 146., 8996908.7),
(1723161845500000000, 61634.95, -4., 2.466791e+05, 1799.38174, 146, 146., 8996908.7),
(1723161846500000000, 61638.05, -5., 3.083145e+05, 1811.70882, 147, 147., 9058544.1),
(1723161847500000000, 61634.95, -5., 3.083155e+05, 1836.36406, 149, 149., 9181820.3),
(1723161848500000000, 61626.05, -4., 2.466811e+05, 1848.69094, 150, 150., 9243454.7),
(1723161849500000000, 61629.95, -4., 2.466811e+05, 1848.69094, 150, 150., 9243454.7),
(1723161850500000000, 61629.95, -4., 2.466811e+05, 1848.69094, 150, 150., 9243454.7),
(1723161851500000000, 61632.25, -4., 2.466811e+05, 1848.69094, 150, 150., 9243454.7),
(1723161852500000000, 61635.95, -5., 3.083139e+05, 1861.0175 , 151, 151., 9305087.5),
(1723161853500000000, 61635.95, -5., 3.083139e+05, 1861.0175 , 151, 151., 9305087.5),
(1723161854500000000, 61638.05, -5., 3.083139e+05, 1861.0175 , 151, 151., 9305087.5),
(1723161855500000000, 61636.25, -4., 2.466763e+05, 1873.34502, 152, 152., 9366725.1),
(1723161856500000000, 61638.05, -4., 2.466763e+05, 1873.34502, 152, 152., 9366725.1),
(1723161857500000000, 61636.05, -5., 3.083149e+05, 1885.67274, 153, 153., 9428363.7),
(1723161858500000000, 61641.45, -6., 3.699515e+05, 1898.00006, 154, 154., 9490000.3),
(1723161859500000000, 61641.45, -6., 3.699515e+05, 1898.00006, 154, 154., 9490000.3),
(1723161860500000000, 61643.25, -6., 3.699515e+05, 1898.00006, 154, 154., 9490000.3),
(1723161861500000000, 61657.25, -7., 4.315953e+05, 1910.32882, 155, 155., 9551644.1),
(1723161862500000000, 61671.45, -8., 4.932531e+05, 1922.66038, 156, 156., 9613301.9),
(1723161863500000000, 61668.05, -8., 4.932531e+05, 1922.66038, 156, 156., 9613301.9),
(1723161864500000000, 61669.15, -8., 4.932531e+05, 1922.66038, 156, 156., 9613301.9),
(1723161865500000000, 61666.75, -8., 4.932531e+05, 1922.66038, 156, 156., 9613301.9),
(1723161866500000000, 61665.05, -7., 4.315869e+05, 1934.99362, 157, 157., 9674968.1),
(1723161867500000000, 61657.15, -6., 3.699223e+05, 1947.32654, 158, 158., 9736632.7),
(1723161868500000000, 61657.15, -6., 3.699223e+05, 1947.32654, 158, 158., 9736632.7),
(1723161869500000000, 61657.15, -6., 3.699223e+05, 1947.32654, 158, 158., 9736632.7),
(1723161870500000000, 61657.15, -6., 3.699223e+05, 1947.32654, 158, 158., 9736632.7),
(1723161871500000000, 61666.75, -7., 4.315799e+05, 1959.65806, 159, 159., 9798290.3),
(1723161872500000000, 61651.55, -6., 3.699137e+05, 1971.9913 , 160, 160., 9859956.5),
(1723161873500000000, 61638.05, -5., 3.082627e+05, 1984.3215 , 161, 161., 9921607.5),
(1723161874500000000, 61634.35, -4., 2.466251e+05, 1996.64902, 162, 162., 9983245.1),
(1723161875500000000, 61638.85, -4., 2.466251e+05, 1996.64902, 162, 162., 9983245.1),
(1723161876500000000, 61638.15, -5., 3.082645e+05, 2008.9769 , 163, 163., 10044884.5),
(1723161877500000000, 61621.65, -4., 2.466269e+05, 2021.30442, 164, 164., 10106522.1),
(1723161878500000000, 61611.65, -3., 1.850057e+05, 2033.62866, 165, 165., 10168143.3),
(1723161879500000000, 61614.95, -4., 2.466179e+05, 2045.9511 , 166, 166., 10229755.5),
(1723161880500000000, 61614.15, -4., 2.466179e+05, 2045.9511 , 166, 166., 10229755.5),
(1723161881500000000, 61614.15, -4., 2.466179e+05, 2045.9511 , 166, 166., 10229755.5),
(1723161882500000000, 61614.15, -4., 2.466179e+05, 2045.9511 , 166, 166., 10229755.5),
(1723161883500000000, 61616.15, -4., 2.466179e+05, 2045.9511 , 166, 166., 10229755.5),
(1723161884500000000, 61623.95, -5., 3.082345e+05, 2058.27442, 167, 167., 10291372.1),
(1723161885500000000, 61627.95, -6., 3.698589e+05, 2070.5993 , 168, 168., 10352996.5),
(1723161886500000000, 61621.45, -6., 3.698589e+05, 2070.5993 , 168, 168., 10352996.5),
(1723161887500000000, 61620.45, -5., 3.082380e+05, 2082.92348, 169, 169., 10414617.4),
(1723161888500000000, 61617.55, -4., 2.466181e+05, 2095.24746, 170, 170., 10476237.3),
(1723161889500000000, 61609.45, -3., 1.850011e+05, 2107.57086, 171, 171., 10537854.3),
(1723161890500000000, 61609.45, -3., 1.850011e+05, 2107.57086, 171, 171., 10537854.3),
(1723161891500000000, 61605.95, -3., 1.850011e+05, 2107.57086, 171, 171., 10537854.3),
(1723161892500000000, 61605.95, -3., 1.850011e+05, 2107.57086, 171, 171., 10537854.3),
(1723161893500000000, 61596.55, -3., 1.850011e+05, 2107.57086, 171, 171., 10537854.3),
(1723161894500000000, 61595.65, -2., 1.234051e+05, 2119.89006, 172, 172., 10599450.3),
(1723161895500000000, 61580.75, -1., 6.180990e+04, 2132.2091 , 173, 173., 10661045.5),
(1723161896500000000, 61575.05, 0., 2.297000e+02, 2144.52514, 174, 174., 10722625.7),
(1723161897500000000, 61585.05, 0., 2.297000e+02, 2144.52514, 174, 174., 10722625.7),
(1723161898500000000, 61578.25, 0., 2.297000e+02, 2144.52514, 174, 174., 10722625.7),
(1723161899500000000, 61578.25, 0., 2.297000e+02, 2144.52514, 174, 174., 10722625.7),
(1723161900500000000, 61583.95, -1., 6.180850e+04, 2156.8409 , 175, 175., 10784204.5),
(1723161901500000000, 61583.95, -1., 6.180850e+04, 2156.8409 , 175, 175., 10784204.5),
(1723161902500000000, 61583.95, -1., 6.180850e+04, 2156.8409 , 175, 175., 10784204.5),
(1723161903500000000, 61585.05, -2., 1.233929e+05, 2169.15778, 176, 176., 10845788.9)],
dtype={'names': ['timestamp', 'price', 'position', 'balance', 'fee', 'num_trades', 'trading_volume', 'trading_value'], 'formats': ['<i8', '<f8', '<f8', '<f8', '<f8', '<i8', '<f8', '<f8'], 'offsets': [0, 8, 16, 24, 32, 40, 48, 56], 'itemsize': 64, 'aligned': True})
Additionally, the to_npz
method saves all records into an npz file, with the asset number as the key for the data.
[30]:
recorder.to_npz('example_record.npz')
HftBacktest also provides a performance reporting tool based on the records. Please see the details here.
[31]:
from hftbacktest.stats import LinearAssetRecord
# Constructs the LinearAssetRecord from the recorded data.
record = LinearAssetRecord(recorder.get(0))
# Generates the statistics.
# You can generate monthly and daily statistics, as well as custom metrics.
stats = record.stats()
# Prints the summary.
stats.summary()
[31]:
start | end | SR | Sortino | Return | MaxDrawdown | DailyNumberOfTrades | DailyTradingValue | ReturnOverMDD | ReturnOverTrade | MaxPositionValue |
---|---|---|---|---|---|---|---|---|---|---|
datetime[μs] | datetime[μs] | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 |
2024-08-09 00:00:00 | 2024-08-09 00:05:00 | -624.497686 | -664.628958 | -1846.54472 | 1902.18778 | 50688.0 | 3.1236e9 | -0.970748 | -0.00017 | 553849.65 |
[32]:
stats.plot()

Bokeh using Holoviews is also supported.
[33]:
import holoviews as hv
hv.extension('bokeh')
stats.plot(backend='holoviews')
[33]: