nkaz001 / hftbacktest

A high-frequency trading and market-making backtesting tool in Python and Rust, which accounts for limit orders, queue positions, and latencies, utilizing full tick data for trades and order books, with real-world crypto market-making examples for Binance Futures
MIT License
1.78k stars 357 forks source link

How to distinguish between available balance and frozen balance #135

Open ffssea opened 2 weeks ago

ffssea commented 2 weeks ago

When a strategy opens a short position, the short position value is added directly to the balance. How to distinguish between available balance and frozen balance in backtesting?

nkaz001 commented 2 weeks ago

StateValues's balance doesn't include the value of open positions. Your equity is computed as balance + position * price - fee.

ffssea commented 2 weeks ago

Thanks for your reply. I think your code has given the answer, but I can't find how to call it in the backtest.

impl AssetType for LinearAsset {
    fn amount(&self, exec_price: f64, qty: f64) -> f64 {
        self.contract_size * exec_price * qty
    }

    fn equity(&self, price: f64, balance: f64, position: f64, fee: f64) -> f64 {
        balance + self.contract_size * position * price - fee
    }
}
nkaz001 commented 2 weeks ago

For now, you'll need to compute it manually due to compatibility with the live bot. You can access state values using hbt.state_values(asset_no).

ffssea commented 2 weeks ago

I need to check the status of the order when calculating the available balance. However, checking the logs shows that the status of the new order is still Status::New after 18 minutes of submission. I useExchangeKind::PartialFillExchange and IOC to submit orders.

image

nkaz001 commented 2 weeks ago

The order type and time-in-force were not properly handled. This has been fixed at 126ccee3382a5132a650a9cd3c9e1a76ff86108d.

ffssea commented 2 weeks ago

Thank you for your selflessness. Now the IOC order execution strategy is correct. I still have some doubts. As shown in the two pictures below, first, the local timestamp in the order has become microseconds. Is this to match the exchange timestamp? Second, when observing the variables during debugging, it is found that the timestamp in the order is less than the current timestamp, and the market has changed and the transaction cannot be completed. Is this caused by order delay? It is difficult to effectively judge the current effective balance in the backtest process dominated by hbt.elapse(). I really hope that the author can consider adding the functions of setting the initial balance, available balance, frozen balance, and total balance. pic 1

image

pic 2

image
nkaz001 commented 2 weeks ago

In your screenshot, the timestamp is in nanoseconds. I'm not sure what you meant by your question.

In the backtesting logic, there is no manipulation of the timestamp unit. It's crucial to ensure that your data (such as feed and order latency) and your code maintain a consistent time unit.

The local_timestamp in Order represents the time when the order is requested.

Regarding setting the initial balance, I will consider it, but I believe it's something that can be easily managed by the user's code. Adding this feature would complicate the builder code, and it would require setting the balance for each asset individually.

I'm not sure where you're having difficulty in computing your total balance, which is your equity value, correct? The following snippet may help you.

let depth = hbt.depth(asset_no);
let price = (depth.best_bid() + depth_best_ask()) / 2.0;
let state_values = hbt.state_values(asset_no);
let equity = state_values.balance + state_values.position * price - state_values.fee;

However, if you need to calculate something like available margin to open more positions, that depends on the exchange's margin calculation and must be handled on your end.