Single Order Execution: A Quantitative Study

Photo by Stephen Dawson on Unsplash

In one of our previous articles entitled “Single Order Execution: The Traps of A Careless TWAP”, we conducted a comprehensive analysis on one of the most commonly used single order execution algorithms i.e. TWAP. The specific focus of that discussion was on the potential pitfalls hidden in a carelessly constructed TWAP algorithm. Many of the aspects mentioned there are silently and dangerously ignored by some commercially available crypto softwares. So, be really careful. In today’s article we will push TWAP further by highlighting and polishing a single most forgotten aspect in single order executions: Backtesting. Using a quantitative approach we will show that historical market data can provide us with useful information regarding the choices of single order execution algorithm parameter values and different parameter values can potentially lead to profoundly different outcomes.

Problem Statement: A fund manager would like to allocate $1,000,000 to purchasing Bitcoins. S/he wants to split this single large order into a series of smaller child orders and the goal is to complete the whole purchase within 1 hour. S/he needs quantitative answers to the following questions:

[1] What is the potential difference between splitting the large order into 100 child orders versus splitting it into 300 child orders?

[2] What is the potential difference between placing the child orders 0.01% below the mid price versus placing them 0.01% above the mid price given a taker trading fee of 0.2% and a maker trading fee of 0.1%?

One way to solve these problems is to conduct A/B testing using smaller amount of real funds. Another solution is to perform backtesting using historical market data. We choose the second solution to answer these questions.

Head over to our open source library and build the single order execution application. The process as documented there is quite simple:

mkdir app/build
cd app/build
rm -rf * (if rebuild from scratch)
cmake -DCMAKE_PROJECT_INCLUDE=<path-to-user_specified_cmake_include> ..
cmake --build . --target single_order_execution

Download historical market data using the convenience script: --exchange coinbase --base-asset btc --quote-asset usd --start-date 2021-07-01 --end-date 2021-07-02 --historical-market-data-directory <anywhere-you-like>

Run the above built executable by feeding the following environment variables as documented:

export TRADING_MODE=backtest
export EXCHANGE=coinbase
export START_TIME=2021-07-01T00:00:00Z
export ORDER_SIDE=buy
export TAKER_FEE=0.002
export MAKER_FEE=0.001
export HISTORICAL_MARKET_DATA_DIRECTORY=<where-you-downloaded-it>

These environment variables are quite self-explanatory and you can always inspect their detailed meanings as documented. What we have set up and executed here is a backtest procedure using TWAP strategy for BTC-USD on Coinbase starting the purchase at 2021–07–01T00:00:00Z and ending it within 1 hour splitting into 100 child orders (i.e. ORDER_REFRESH_INTERVAL_SECONDS=36) with each order’s price 0.01% below the mid price. The program finishes in a fraction of a second and there are 4 files written to the current directory (which can be customized):


They serve as the simulated database for a real exchange and contain all the relevant information needed for performing post-trade analysis. For example, the summary.csv file is a one-liner and immediately tells us that by the end of the execution we should have $526,661 left as cash and should have paid $472 as trading fee. The most powerful part of backtesting is its ability to quickly obtain answers to various questions for various scenarios by simply changing some parameter values on the computer and simply running the backtest executable again. To answer the first question in our problem statement, let us


This means we are testing splitting into 300 child orders instead of 100. In a fraction of a second the summary.csv file tells us that by the end of the execution we should have $689,813 left as cash and should have paid $309 as trading fee. Well, that’s not good because we want to buy Bitcoin rather than leaving a pile of cash on the balance sheet. Here we’ll give the reader an interesting exercise: reason about why this happens by examining order-update.csv and the downloaded historical market data files. To answer the second question in our problem statement, let us


This means that we are testing placing a child order 0.01% above the mid price instead of below it. In a fraction of a second the summary.csv file tells us that by the end of the execution we should have $120,353 left as cash and should have paid $1525 as trading fee. By placing the child order at a price merely 2 bps higher than before, we can achieve a much higher final completion while only paying slightly higher trading fees (due to taker orders having higher trading fees). In fact,private-trade.csv provides all the detailed information about which trades were fulfilled as us being takers and which trades were fulfilled as us being makers.

We can see that backtesting is a very useful tool for researching single order execution algorithm parameter values and comparing their outcomes for decision making. In finance and trading, we prefer quantitative answers over gut-feelings. How about you? If you are interested in our work or collaborating with us, join us on Discord: 🎉 We specialize in market data collection, high speed trading system, infrastructure optimization, and proprietary market making. You’re welcome to hire us as engineers, liquidity providers, traders, or asset managers.

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



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store