Closed zhangyiyan617 closed 3 months ago
I plan to provide a more detailed example later, but for now, please refer to the example below. It will give you a basic understanding of cross-asset backtesting.
One important factor not covered in this example is the need to adjust for latency when trading two assets on different exchanges located in different regions. For instance, if you're backtesting an arbitrage strategy between BTCUSDT on Binance Futures and BTCUSDT on Bybit, you must consider that Binance Futures is presumably hosted in AWS Tokyo, while Bybit operates in AWS Singapore. Since data feeds and order executions occur within their respective regions, you need to account for the latency between Tokyo and Singapore, to ensure accurate backtesting results.
It may be a bit outdated, but Geographic Latency in Crypto: How to Optimally Colocate Your AWS Trading Server to Any Exchange API provides a good explanation of latency between regions.
For even more realistic results, it’s best to collect data from one location where you actually plan to trade.
from numba import njit
import numpy as np
from hftbacktest import BacktestAsset, HashMapMarketDepthBacktest, Recorder, ALL_ASSETS, LIMIT, GTX
from hftbacktest.stats import LinearAssetRecord
@njit
def test(hbt, recorder):
half_spread_tick = 5
while hbt.elapse(10 * 1e9) == 0:
hbt.clear_inactive_orders(ALL_ASSETS)
for asset_no in range(2):
depth = hbt.depth(asset_no)
tick_size = depth.tick_size
mid_price = (depth.best_bid + depth.best_ask) / 2.0
buy_order_id = 10 * asset_no + 1
sell_order_id = 10 * asset_no + 2
if buy_order_id not in hbt.orders(asset_no):
order_price = round((mid_price - half_spread_tick * tick_size) / tick_size) * tick_size
order_qty = 1
time_in_force = GTX
order_type = LIMIT
hbt.submit_buy_order(asset_no, buy_order_id, order_price, order_qty, time_in_force, order_type, False)
else:
hbt.cancel(asset_no, buy_order_id, False)
if sell_order_id not in hbt.orders(asset_no):
order_price = round((mid_price + half_spread_tick * tick_size) / tick_size) * tick_size
order_qty = 1
time_in_force = GTX
order_type = LIMIT
hbt.submit_sell_order(asset_no, sell_order_id, order_price, order_qty, time_in_force, order_type, False)
else:
hbt.cancel(asset_no, sell_order_id, False)
recorder.record(hbt)
btcusdt = (
BacktestAsset()
.data(['usdm/btcusdt_20240809.npz'])
.initial_snapshot('usdm/btcusdt_20240808_eod.npz')
.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)
)
ethusdt = (
BacktestAsset()
.data(['usdm/ethusdt_20240809.npz'])
.initial_snapshot('usdm/ethusdt_20240808_eod.npz')
.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.01)
.lot_size(0.001)
)
hbt = HashMapMarketDepthBacktest([btcusdt, ethusdt])
recorder = Recorder(
hbt.num_assets,
1000
)
test(hbt, recorder.recorder)
_ = hbt.close()
Thank you for providing the code example! Looking forward to more rust examples!!!
I am using the backtesting framework and want to implement a cross-exchange arbitrage strategy. Specifically, could you please help me to load and synchronize market data from two different exchanges and configure the framework to execute trades on both exchanges? Thank you!