From Classical Paper to Practical Trader: A Quantitative Journey, Part VI

Crypto Chassis
Open Crypto Trading Initiative
4 min readFeb 15, 2021
Photo by David Marcu on Unsplash

In the previous post, we had a thorough discussion on the challenges of and the solutions to backtesting market making strategies in high frequency trading (HFT). The demonstrational simulation run was performed on a pure market making strategy. We deliberately chose a bad day for Bitcoin throughout which its market price continuously dropped. By the end of the post, we saw that the performance of such a plain and simple strategy was disappointing in the context of a trending market due to adverse selection. In this post, we’ll present an interesting idea that aims at not only solving the adverse selection problem but also inspiring you to explore more HFT ideas. We’ll show that reading academic papers can be hilarious and that transforming them into code can be rewarding.

Back in 1984, economist Richard Roll published a paper entitled “A Simple Implicit Measure of the Effective Bid-Ask Spread in an Efficient Market”.

Abstract for Richard Roll’s seminal paper

The equation in this paper’s abstract claims that the effective bid-ask spread is twice the square root of the negation of the autocovariance of the price-change time-series.

Equation for the autocovariance of the price-change time-series

In order for the equation to make sense, the theoretical autocovariance has to be negative (ensuing that the term under the square root to be positive). With negative autocovariance, a trade is likely to be followed by a trade in the opposite direction, a situation that reduces the adverse selection risk exposed to a market maker (see this stackexchange question). Therefore we can reason that if the observed autocovariance is indeed negative, it is relatively safe for us to run market making, but if the observed autocovariance becomes positive, it becomes riskier for us to run market making (due to adverse selection). Bingo! The autocovariance of the price-change time-series can possibly serve as an HFT indicator telling us when we should proceed with market-making and when we should pause for a moment. Let’s call it intelligent market making strategy (as opposed to pure market making strategy). Coding-wise we can reuse almost all of the logics presented in our previous post showing how to backtest a market making strategy using historical data. The only addition (highlighted in bold shown below) is a small conditional check near the beginning of each iteration round of the replay loop: it takes second-by-second mid prices from the past 21 seconds (arbitrarily chosen) for a given moment, computes the price-change time-series, and computes the covariance between the price-change time-series and the lagged price-change time-series. If the result is greater than 0, we’d be intelligent and avoid market marking activity at this given moment and move on to the next moment.

while start_time_seconds < end_time_seconds:
print(
f"replaying {pd.Timestamp(start_time_seconds, unit='s')} (unix timestamp {start_time_seconds})")
df_1['change'] = df_1[(df_1.index < start_time_seconds) &
(df_1.index >= start_time_seconds - 21)]['mid_price'].diff()
df_1['lag_change'] = df_1['change'].shift()
c = df_1['change'].cov(df_1['lag_change'])
mid_price = df_1[df_1.index <= start_time_seconds].tail(
n=1).iloc[0]['mid_price']
if c > 0:
start_time_seconds += period_seconds
balance['time_seconds'].append(start_time_seconds)
balance['btc'].append(balance['btc'][-1])
balance['usd'].append(balance['usd'][-1])
balance['total'].append(balance['btc'][-1] * mid_price + balance['usd'][-1])
continue
...

Output:

      time_seconds    btc          usd        total
0 1611187200 0.100 3549.397000 7098.794000
1 1611187230 0.099 3584.894519 7098.797549
2 1611187260 0.098 3620.421357 7101.703287
3 1611187290 0.098 3620.421357 7106.178947
4 1611187320 0.098 3620.428478 7109.875768
... ... ... ... ...
2876 1611273480 0.102 3485.993266 6629.304826
2877 1611273510 0.101 3516.767773 6624.682203
2878 1611273540 0.101 3516.767773 6636.866843
2879 1611273570 0.101 3516.767773 6622.090543
2880 1611273600 0.101 3516.767773 6629.419103

After a whole day’s work, the intelligent market making strategy ended up with a total capital of $6629. The pure market making strategy used in the previous post ended up with a total capital of $6593. Great in that we’ve reduced the capital loss. Let’s make a side-by-side plot showing their balances as a function of time:

Balance time series. Blue curve is BTC balance, orange curve is USD balance, green curve is total balance. Left: pure market making. Right: intelligent market making.

We can clearly observe that the inventory imbalance has been significantly reduced, i.e. the BTC to USD capital ratio for the intelligent market making strategy remains to be very close to 1 from the beginning of the day to the end of the day. Great in that this is quite desired by a market maker.

To sum up, in this post we’ve performed an inspiring experiment on market making by translating knowledge in a classical academic paper to code manageable by a practical trader. We are a group of explorers and doers, and wish you the best luck in your adventures!

Follow us on Github at https://github.com/crypto-chassis. 🚙

Disclaimer: This is an educational post rather than investment/financial advice.

--

--