Documentation Index Fetch the complete documentation index at: https://mintlify.com/nkaz001/hftbacktest/llms.txt
Use this file to discover all available pages before exploring further.
Overview
HftBacktest provides integration with Bybit’s linear perpetual futures, supporting both backtesting and live trading with the same algorithm code. The Bybit connector is currently under active development.
Development Status : Bybit support is under development. Use at your own risk and report any issues you encounter.
Setup
1. Build the Connector
Build the connector from source:
cd hftbacktest
cargo build --release --package connector
Create a Bybit configuration file:
# Mainnet:
# - Linear: wss://stream.bybit.com/v5/public/linear
# Testnet:
# - Linear: wss://stream-testnet.bybit.com/v5/public/linear
public_url = "wss://stream-testnet.bybit.com/v5/public/linear"
# Mainnet: wss://stream.bybit.com/v5/private
# Testnet: wss://stream-testnet.bybit.com/v5/private
private_url = "wss://stream-testnet.bybit.com/v5/private"
# Mainnet: wss://stream.bybit.com/v5/trade
# Testnet: wss://stream-testnet.bybit.com/v5/trade
trade_url = "wss://stream-testnet.bybit.com/v5/trade"
# Mainnet: https://api.bybit.com
# Testnet: https://api-testnet.bybit.com
rest_url = "https://api-testnet.bybit.com"
# Linear
category = "linear"
order_prefix = ""
api_key = "YOUR_API_KEY"
secret = "YOUR_SECRET_KEY"
Symbols must be uppercase for Bybit (e.g., BTCUSDT, not btcusdt).
3. Run the Connector
Start the connector:
connector --name bybit-futures --connector bybit --config bybit.toml
Data Conversion
Bybit provides two conversion methods depending on your needs:
Method 1: Fused Multi-Level Depth
Use convert_fused to combine multiple order book depth levels (1, 25, 50, 200, etc.) into a single market depth representation:
from hftbacktest.data.utils import bybit
# Fuse all depth levels for more accurate order book
data = bybit.convert_fused(
input_filename = "examples/bybit/btcusdt_20250926.gz" ,
tick_size = 0.1 ,
lot_size = 0.001 ,
output_filename = "btcusdt_20250926.npz"
)
print ( f "Loaded { len (data) } events from fused conversion" )
When to use:
You have data with multiple depth levels
You want the most accurate order book reconstruction
You need to handle overlapping depth updates from different levels
Method 2: Single Depth Level
Use convert_depth to process only a specific depth level:
from hftbacktest.data.utils.bybit import BybitDepthLevel
import bybit
# Process only level 200 data
data = bybit.convert_depth(
input_filename = "examples/bybit/btcusdt_20250926.gz" ,
single_depth_level = BybitDepthLevel. LEVEL_200 ,
output_filename = "btcusdt_20250926.npz"
)
print ( f "Loaded { len (data) } events from single depth conversion" )
Available depth levels:
BybitDepthLevel. LEVEL_1 # Top of book
BybitDepthLevel. LEVEL_25 # 25 levels
BybitDepthLevel. LEVEL_50 # 50 levels (default)
BybitDepthLevel. LEVEL_100 # 100 levels
BybitDepthLevel. LEVEL_200 # 200 levels
BybitDepthLevel. LEVEL_1000 # 1000 levels
When to use:
You only need one specific depth level
Your data contains a single depth level
You want faster processing
Bybit feed format contains local timestamp followed by JSON:
1758841137168651303 {"topic":"orderbook.1.BTCUSDT","type":"snapshot","ts":1758841134603,"data":{"s":"BTCUSDT","b":[["109378.80","1.273"]],"a":[["109378.90","6.278"]],"u":14869255,"seq":457514742271},"cts":1758841134598}
1758841138293664629 {"topic":"publicTrade.BTCUSDT","type":"snapshot","ts":1758841135824,"data":[{"T":1758841135823,"s":"BTCUSDT","S":"Buy","v":"0.020","p":"109378.90","L":"ZeroPlusTick","i":"2a74ac78-691c-5b54-9dbe-5aafe8364627","BT":false,"RPI":false,"seq":457514743450}]}
1758845432767124368 {"topic":"orderbook.50.BTCUSDT","type":"delta","ts":1758845431663,"data":{"s":"BTCUSDT","b":[["109147.80","1.687"],["109144.10","0.027"]],"a":[["109148.00","0"],["109154.30","0.076"]],"u":21111423,"seq":457538796406},"cts":1758845431662}
Complete Example
Here’s a complete example using Bybit data:
import numpy as np
from numba import njit
from hftbacktest import BacktestAsset, HashMapMarketDepthBacktest
from hftbacktest.data.utils import bybit
from hftbacktest.data.utils.bybit import BybitDepthLevel
@njit
def market_making_algo ( hbt ):
while hbt.elapse( 2.5e8 ) == 0 : # 250ms intervals
depth = hbt.depth( 0 )
# Prints the best bid and ask
print (
'current_timestamp:' , hbt.current_timestamp,
', best_bid:' , np.round(depth.best_bid, 1 ),
', best_ask:' , np.round(depth.best_ask, 1 )
)
return True
if __name__ == "__main__" :
# Example 1: Using convert_fused for multi-level depth
data = bybit.convert_fused(
input_filename = "examples/bybit/btcusdt_20250926.gz" ,
tick_size = 0.1 ,
lot_size = 0.001 ,
)
print ( f "Loaded { len (data) } events from fused conversion" )
# Example 2: Using convert_depth for single level
data_single = bybit.convert_depth(
input_filename = "examples/bybit/btcusdt_20250926.gz" ,
single_depth_level = BybitDepthLevel. LEVEL_200
)
print ( f "Loaded { len (data_single) } events from single depth conversion" )
# Create backtest asset
asset = (
BacktestAsset()
.data(data)
.linear_asset( 1.0 )
.power_prob_queue_model( 2.0 )
.no_partial_fill_exchange()
.trading_value_fee_model( - 0.00005 , 0.0007 )
.tick_size( 0.1 )
.lot_size( 0.001 )
)
hbt = HashMapMarketDepthBacktest([asset])
market_making_algo(hbt)
Live Trading (Rust)
Basic Live Bot
use hftbacktest :: {
live :: {
Instrument ,
LiveBot ,
LiveBotBuilder ,
LoggingRecorder ,
ipc :: iceoryx :: IceoryxUnifiedChannel ,
},
prelude :: { Bot , ErrorKind , HashMapMarketDepth },
};
use tracing :: error;
fn prepare_live () -> LiveBot < IceoryxUnifiedChannel , HashMapMarketDepth > {
LiveBotBuilder :: new ()
. register ( Instrument :: new (
"bybit-futures" , // connector name
"BTCUSDT" , // symbol (uppercase!)
0.1 , // tick_size
0.001 , // lot_size
HashMapMarketDepth :: new ( 0.1 , 0.001 ),
0 ,
))
. error_handler ( | error | {
match error . kind {
ErrorKind :: ConnectionInterrupted => {
error! ( "ConnectionInterrupted" );
}
ErrorKind :: CriticalConnectionError => {
error! ( "CriticalConnectionError" );
}
ErrorKind :: OrderError => {
let error = error . value ();
error! ( ? error , "OrderError" );
}
ErrorKind :: Custom ( errno ) => {
error! ( % errno , "custom" );
}
}
Ok (())
})
. build ()
. unwrap ()
}
fn main () {
tracing_subscriber :: fmt :: init ();
let mut hbt = prepare_live ();
hbt . run () . unwrap ();
// Grid trading parameters
let relative_half_spread = 0.0001 ;
let relative_grid_interval = 0.0001 ;
let grid_num = 2 ;
let min_grid_step = 0.1 ; // tick size
let skew = relative_half_spread / grid_num as f64 ;
let order_qty = 0.001 ;
let max_position = grid_num as f64 * order_qty ;
let mut recorder = LoggingRecorder :: new ();
gridtrading (
& mut hbt ,
& mut recorder ,
relative_half_spread ,
relative_grid_interval ,
grid_num ,
min_grid_step ,
skew ,
order_qty ,
max_position ,
)
. unwrap ();
hbt . close () . unwrap ();
}
Comparison: Fused vs Single Depth
Fused Multi-Level
Single Depth Level
Advantages:
More accurate order book representation
Combines all depth levels intelligently
Better for strategies requiring full depth visibility
Disadvantages:
More processing overhead
Requires tick_size and lot_size parameters
Larger resulting dataset
Best for:
Market making strategies
Strategies analyzing order book depth
When you have multi-level depth data
Advantages:
Faster processing
Simpler configuration
Smaller resulting dataset
Disadvantages:
Only processes one depth level
May miss depth information from other levels
Best for:
Strategies using specific depth level
When you only have one depth level
Faster prototyping and testing
Best Practices
Choose the Right Depth Level Select depth level based on your strategy:
LEVEL_1 : For tick-by-tick trading
LEVEL_50 : Good balance for most strategies
LEVEL_200+ : For deep order book analysis
Test Thoroughly Bybit integration is under development:
Start with testnet
Test with small positions
Report issues on GitHub
Handle Depth Updates Bybit sends snapshot and delta updates:
Snapshots rebuild the book
Deltas update incrementally
The converter handles both automatically
Symbol Format Always use uppercase symbols for Bybit:
Fee Structure
Bybit Linear Perpetual fee structure:
Account Type Maker Fee Taker Fee Standard 0.0200% 0.0550% Professional 0.0150% 0.0450% Market Maker -0.0050% 0.0700%
Use .trading_value_fee_model(-0.00005, 0.0007) to model market maker rebates in your backtests.
Resources