Why Market Makers Should Know about Market Takers Inside-out

Crypto Chassis
Open Crypto Trading Initiative
8 min readOct 31, 2022
Photo by Mick Haupt on Unsplash

Greetings, Ladies and Gentlemen! Welcome back. Hope you are doing well in this crypto winter. As mentioned in our previous article entitled “Atomic Arbitrage: A Quantitative Study”, we have been performing comprehensive experiments in the battlefield of live trading for the past couple of months and encountered a lot of interesting phenomena and problems which we spent large amounts of time to deep dive into. Today we are glad to share one of the discoveries that we made about market making in this new article. If you are a market maker or plan to become a market maker, then we definitely have something in common. To keep the story succinct, let us go straight to the problem that we want to solve: whenever our market making order becomes open on the order book, we’d like to estimate its probability of getting filled by the next trade event.

Placing an order at the right price level on the order book reflects the core competency of a market maker and is truly a fine art. In normal cases, the price shouldn’t be too close to the top level of the order book (i.e. best bid/ask) so as to avoid adverse selection by market takers. The price shouldn’t be too far away from the top of the order book either so as to at least get the order filled within some finite period of time. So what should the right price level be? Each market maker has his or her own answers. For us, we seek our answers from our frenemy: market takers. Why are market takers so relevant and important? After a market maker places an order and the order becomes open on the order book, s/he relies on a market taker to submit a counter order some time later on to match the existing maker order. The maker order and the taker order together complete the trade for the two counter-parties and the trade seals the deal. Market will become crippled if market takers are missing (It will also become crippled if market makers are missing). Therefore for market makers, it is of vital importance to know and understand the dynamics of market takers. On one hand, market takers are market makers’ friends because takers fill the orders from makers. On the other hand, market takers are market makers’ enemies because takers adversely select makers. To have a winning strategy in the battlefield as a market maker, we need to know our frenemy inside-out. From a market maker’s perspective, there are so many attributes that we can study about market takers. In a previous article entitled “Analyze Trade Arrivals in Crypto Markets”, we presented a comprehensive study on the inter-trade arrival time. In this article we will focus on another important attribute of market takers’ orders: the sizes. We would like to answer three questions: [1] To what extent can information about the sizes of these orders help us improve our own pricing? [2] What is the probability distribution of these orders’ sizes? [3] Last but most importantly, how do we estimate the probability of our own order getting filled by the next trade event?

Let us dive straight into data using an example taken from the battlefield. Around UTC time 2022–10–20T21:03:59Z, our market making buy order on FTX’s SOL/USD was completely filled at a price of $28.02 with a quantity of 0.45 in one single transaction. As an exercise for the reader, use the technique outlined in our previous article entitled “Atomic Arbitrage: A Quantitative Study” to find out the footprint of this trade from the public trade records. Remember that whenever you need data, one of the simplest ways is to use our free data service. For this exercise, the download is located at https://api.cryptochassis.com/v1/trade/ftx/sol-usd?startTime=2022-10-20. You get a gzipped csv file containing all the public trade records for FTX’s SOL/USD on 2022–10–20). We’ll skip the detailed steps and only paste the final results into the table shown below: these are the trades that are relevant to us. We have annotated the row corresponding to our own order and highlighted it with bold fonts. Other rows were produced by other market makers’ orders.

time_seconds,price,size,is_buyer_maker
1666299839.173033,28.0925,20,1
1666299839.173033,28.0925,0.01,1
1666299839.173033,28.0925,0.02,1
1666299839.173033,28.0925,0.02,1
1666299839.173033,28.0925,0.02,1
1666299839.173033,28.09,20,1
1666299839.173033,28.09,77.03,1
1666299839.173033,28.09,96.18,1
1666299839.173033,28.0875,77,1
1666299839.173033,28.0875,96.16,1
1666299839.173033,28.0875,96.2,1
1666299839.173033,28.085,76.99,1
1666299839.173033,28.085,96.16,1
1666299839.173033,28.0825,0.18,1
1666299839.173033,28.0825,0.05,1
1666299839.173033,28.0825,1.61,1
1666299839.173033,28.0825,0.08,1
1666299839.173033,28.0825,28.13,1
1666299839.173033,28.0825,124.28,1
1666299839.173033,28.0825,96.5,1
1666299839.173033,28.0825,4.28,1
1666299839.173033,28.0825,392.95,1
1666299839.173033,28.08,104.59,1
1666299839.173033,28.08,17.41,1
1666299839.173033,28.08,96.83,1
1666299839.173033,28.08,152.42,1
1666299839.173033,28.08,412.62,1
1666299839.173033,28.08,121.19,1
1666299839.173033,28.08,176.27,1
1666299839.173033,28.0775,0.05,1
1666299839.173033,28.0775,73.89,1
1666299839.173033,28.0775,0.1,1
1666299839.173033,28.0775,28.88,1
1666299839.173033,28.0775,96.83,1
1666299839.173033,28.0775,7.23,1
1666299839.173033,28.0775,8.85,1
1666299839.173033,28.0775,107.98,1
1666299839.173033,28.0775,87.4,1
1666299839.173033,28.0775,358.74,1
1666299839.173033,28.075,0.25,1
1666299839.173033,28.075,0.02,1
1666299839.173033,28.075,79.74,1
1666299839.173033,28.075,8,1
1666299839.173033,28.075,96.28,1
1666299839.173033,28.075,27.12,1
1666299839.173033,28.075,17.8,1
1666299839.173033,28.0725,35.62,1
1666299839.173033,28.0725,104.3,1
1666299839.173033,28.0725,116.65,1
1666299839.173033,28.0725,10,1
1666299839.173033,28.0725,12,1
1666299839.173033,28.0725,35.62,1
1666299839.173033,28.07,193.6,1
1666299839.173033,28.07,72.69,1
1666299839.173033,28.07,72.7,1
1666299839.173033,28.07,96.83,1
1666299839.173033,28.07,5.88,1
1666299839.173033,28.07,71.24,1
1666299839.173033,28.07,1000,1
1666299839.173033,28.0675,0.48,1
1666299839.173033,28.0675,0.05,1
1666299839.173033,28.0675,0.14,1
1666299839.173033,28.0675,122.86,1
1666299839.173033,28.0675,10,1
1666299839.173033,28.0675,102.34,1
1666299839.173033,28.0675,95.23,1
1666299839.173033,28.0675,30.13,1
1666299839.173033,28.0675,215.49,1
1666299839.173033,28.065,0.01,1
1666299839.173033,28.065,0.01,1
1666299839.173033,28.065,0.01,1
1666299839.173033,28.065,0.01,1
1666299839.173033,28.065,0.01,1
1666299839.173033,28.065,0.01,1
1666299839.173033,28.065,74.24,1
1666299839.173033,28.065,195.97,1
1666299839.173033,28.065,102.34,1
1666299839.173033,28.065,18.13,1
1666299839.173033,28.065,23.89,1
1666299839.173033,28.065,217.92,1
1666299839.173033,28.0625,0.01,1
1666299839.173033,28.0625,17.98,1
1666299839.173033,28.0625,0.08,1
1666299839.173033,28.0625,101.9,1
1666299839.173033,28.0625,10,1
1666299839.173033,28.0625,10,1
1666299839.173033,28.0625,10,1
1666299839.173033,28.0625,257.03,1
1666299839.173033,28.0625,575.18,1
1666299839.173033,28.0625,71.26,1
1666299839.173033,28.06,0.04,1
1666299839.173033,28.06,0.01,1
1666299839.173033,28.06,0.01,1
1666299839.173033,28.06,0.01,1
1666299839.173033,28.06,0.01,1
1666299839.173033,28.06,0.01,1
1666299839.173033,28.06,0.01,1
1666299839.173033,28.06,0.09,1
1666299839.173033,28.06,2.28,1
1666299839.173033,28.06,0.01,1
1666299839.173033,28.06,101.46,1
1666299839.173033,28.06,511.18,1
1666299839.173033,28.06,28.13,1
1666299839.173033,28.0575,0.01,1
1666299839.173033,28.0575,99.32,1
1666299839.173033,28.0575,891.02,1
1666299839.173033,28.055,196.04,1
1666299839.173033,28.055,98.86,1
1666299839.173033,28.055,0.02,1
1666299839.173033,28.0525,97.63,1
1666299839.173033,28.0525,838.04,1
1666299839.173033,28.0525,89.11,1
1666299839.173033,28.05,0.03,1
1666299839.173033,28.05,0.12,1
1666299839.173033,28.05,70.84,1
1666299839.173033,28.05,28.72,1
1666299839.173033,28.05,24.78,1
1666299839.173033,28.0475,44.3,1
1666299839.173033,28.0475,97.63,1
1666299839.173033,28.045,1.31,1
1666299839.173033,28.045,0.01,1
1666299839.173033,28.045,0.09,1
1666299839.173033,28.045,196.11,1
1666299839.173033,28.045,97.63,1
1666299839.173033,28.045,758.95,1
1666299839.173033,28.045,22,1
1666299839.173033,28.0425,1,1
1666299839.173033,28.0425,1,1
1666299839.173033,28.0425,0.3,1
1666299839.173033,28.0425,0.02,1
1666299839.173033,28.0425,0.03,1
1666299839.173033,28.0425,97.63,1
1666299839.173033,28.0425,20.98,1
1666299839.173033,28.04,10,1
1666299839.173033,28.04,27.12,1
1666299839.173033,28.04,96.28,1
1666299839.173033,28.0375,355.49,1
1666299839.173033,28.0375,96.28,1
1666299839.173033,28.0375,110,1
1666299839.173033,28.035,0.01,1
1666299839.173033,28.035,0.02,1
1666299839.173033,28.035,196.18,1
1666299839.173033,28.035,71.09,1
1666299839.173033,28.0325,0.37,1
1666299839.173033,28.0325,40.37,1
1666299839.173033,28.0325,83.94,1
1666299839.173033,28.03,0.02,1
1666299839.173033,28.03,5.42,1
1666299839.173033,28.03,178.37,1
1666299839.173033,28.03,39.06,1
1666299839.173033,28.0275,0.06,1
1666299839.173033,28.0275,356.77,1
1666299839.173033,28.025,0.03,1
1666299839.173033,28.025,0.17,1
1666299839.173033,28.025,196.25,1
1666299839.173033,28.025,1616.57,1
1666299839.173033,28.0225,0.09,1
1666299839.173033,28.0225,0.36,1
1666299839.173033,28.02,0.08,1
1666299839.173033,28.02,0.01,1
1666299839.173033,28.02,0.01,1
1666299839.173033,28.02,27.05,1
1666299839.173033,28.02,0.45,1 <---- Our Market Making Buy Order
1666299839.173033,28.0175,0.82,1
1666299839.173033,28.0175,0.44,1
1666299839.173033,28.0175,0.14,1
1666299839.173033,28.0175,0.06,1
1666299839.173033,28.0175,0.06,1
1666299839.173033,28.015,0.25,1
1666299839.173033,28.015,0.03,1
1666299839.173033,28.015,44.06,1
1666299839.173033,28.0125,2.29,1
1666299839.173033,28.0125,16.51,1
1666299839.173033,28.0125,10,1
1666299839.173033,28.01,0.01,1
1666299839.173033,28.01,0.28,1
1666299839.173033,28.01,0.07,1
1666299839.173033,28.01,46.41,1
1666299839.173033,28.01,0.05,1
1666299839.173033,28.01,50,1
1666299839.173033,28.01,1.52,1
1666299839.173033,28.01,129.32,1
1666299839.173033,28.0075,0.4,1
1666299839.173033,28.0075,0.15,1
1666299839.173033,28.0075,0.01,1
1666299839.173033,28.0075,300,1
1666299839.173033,28.005,1.39,1
1666299839.173033,28.005,1.17,1
1666299839.173033,28.005,0.15,1
1666299839.173033,28.005,49.44,1
1666299839.173033,28.0025,0.15,1
1666299839.173033,28.0025,0.01,1
1666299839.173033,28.0025,0.59,1
1666299839.173033,28.0025,500,1
1666299839.173033,28.0025,0.36,1
1666299839.173033,28.0025,1,1
1666299839.173033,28.0025,0.02,1
1666299839.173033,28.0025,0.13,1
1666299839.173033,28.0025,0.01,1
1666299839.173033,28.0025,0.01,1
1666299839.173033,28.0025,0.48,1
1666299839.173033,28,0.23,1
1666299839.173033,28,0.61,1
1666299839.173033,28,0.02,1
1666299839.173033,28,16.23,1
1666299839.173033,28,0.01,1
1666299839.173033,28,0.32,1
1666299839.173033,28,0.14,1
1666299839.173033,28,0.28,1
1666299839.173033,28,0.74,1
1666299839.173033,28,2.4,1
1666299839.173033,28,0.56,1
1666299839.173033,28,0.02,1
1666299839.173033,28,35.71,1
1666299839.173033,28,0.01,1
1666299839.173033,28,20,1
1666299839.173033,28,0.01,1
1666299839.173033,28,0.04,1
1666299839.173033,28,1.53,1
1666299839.173033,28,3,1
1666299839.173033,28,1,1
1666299839.173033,28,5,1
1666299839.173033,28,7.14,1
1666299839.173033,28,0.03,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,8,1
1666299839.173033,28,0.6,1
1666299839.173033,28,10,1
1666299839.173033,28,35.71,1
1666299839.173033,28,1.78,1
1666299839.173033,28,0.35,1
1666299839.173033,28,0.1,1
1666299839.173033,28,1,1
1666299839.173033,28,1.78,1
1666299839.173033,28,3.57,1
1666299839.173033,28,0.01,1
1666299839.173033,28,0.1,1
1666299839.173033,28,100,1
1666299839.173033,28,1,1
1666299839.173033,28,0.1,1
1666299839.173033,28,0.22,1
1666299839.173033,28,0.02,1
1666299839.173033,28,0.01,1
1666299839.173033,28,5,1
1666299839.173033,28,1,1
1666299839.173033,28,5.45,1
1666299839.173033,28,1,1
1666299839.173033,28,1,1
1666299839.173033,28,5,1
1666299839.173033,28,0.03,1
1666299839.173033,28,0.4,1
1666299839.173033,28,17.85,1
1666299839.173033,28,0.5,1
1666299839.173033,28,34.66,1
1666299839.173033,28,0.03,1
1666299839.173033,28,0.71,1
1666299839.173033,28,0.08,1
1666299839.173033,28,0.01,1
1666299839.173033,28,3,1
1666299839.173033,28,0.1,1
1666299839.173033,28,71.42,1
1666299839.173033,28,75.07,1
1666299839.173033,28,5,1
1666299839.173033,28,4,1
1666299839.173033,28,1000,1
1666299839.173033,28,0.4,1
1666299839.173033,28,66.96,1
1666299839.173033,28,60,1
1666299839.173033,28,5,1
1666299839.173033,28,4,1
1666299839.173033,28,2.25,1
1666299839.173033,28,25,1
1666299839.173033,28,10,1
1666299839.173033,28,5,1
1666299839.173033,28,0.89,1
1666299839.173033,28,6,1
1666299839.173033,28,0.5,1
1666299839.173033,28,25.15,1
1666299839.173033,28,0.02,1
1666299839.173033,28,0.01,1
1666299839.173033,28,1.78,1
1666299839.173033,28,1,1
1666299839.173033,28,1,1
1666299839.173033,28,5.91,1
1666299839.173033,28,10,1
1666299839.173033,28,30,1
1666299839.173033,28,0.6,1
1666299839.173033,28,2,1
1666299839.173033,28,30,1
1666299839.173033,28,7.14,1
1666299839.173033,28,0.8,1
1666299839.173033,28,0.09,1
1666299839.173033,28,21.19,1
1666299839.173033,28,542.36,1
1666299839.173033,28,27.16,1

With the power of data, we can clearly see what happened: a taker with very deep pocket placed and executed one single large order in one single transaction. From the size column of the table, we can easily calculate the size of his/her order: 18942.87 SOL. From the size and the price columns of the table, we can easily calculate the USD size of this order: 531290.6841 USD. Surprise, right? This taker executed one single large order with a value of about half a million dollars! Although we won’t be able to know the identity of the taker nor his/her motivations behind issuing such a big order, yet with the power of data, we are ready to extract useful information from him/her to improve our own trading. At this point, we can readily answer the first question raised at the beginning of this article:”To what extent can information about the sizes of these orders help us improve our own pricing?”. From the last row of the table shown above, we can infer that if we knew ahead-of-time that there would be such a large incoming taker sell order, we would have placed our market making buy order at the price level of $28 instead of $28.02, and it is very likely that we would have ended up with the same fill quantity but with a steep price discount of 7.1 bps ( = 28.02 / 28–1). Now let us try to answer the second question:”What is the probability distribution of these orders’ sizes?”. Using the same csv file mentioned above that contains all the public trade records for FTX’s SOL/USD on 2022–10–20, we can perform some simple data plotting and equation fitting. First we can separate the rows into two categories. One category has value 1 in the column is_buyer_maker which means that these takers are sellers. Another category has value 0 in the column is_buyer_maker which means that these takers are buyers. Let us focus on the case where takers are sellers and therefore we filter the rows by the column is_buyer_maker being 1. Then we can group the rows by their timestamps such that for a given timestamp we sum up the values in the size column. The result becomes a representation of all the taker orders with their sizes. It looks something like this:

time_seconds    size0      1666224003.310423  101.091      1666224003.313872    0.012      1666224003.442892    0.013      1666224003.532315    0.014      1666224003.557571    0.01...                  ...     ...24808  1666310375.416373   17.1824809  1666310376.564144    0.0124810   1666310395.15285   81.0724811  1666310395.430413    5.2224812  1666310395.476339   30.26

To have a direct feeling about the statistical attributes of the order sizes, we can plot the data as a histogram.

Order size histogram.

We can perform the same analysis on many different days, with many different instruments, and for many different exchanges. After doing all that hard work, what we observed was that: a. Most histograms have long tails. b. Some histograms resemble the exponential distribution. c. Some histograms resemble the skewed normal distribution. d. The order size is always positive (for sure). These observations lead us to propose that the most appropriate statistical model used to describe the order sizes should be the so-called gamma distribution (https://en.wikipedia.org/wiki/Gamma_distribution). To visualize gamma distribution, we can fit our data to it and overlay a plot of it on top of the histogram:

Order size histogram and fitting curve by gamma distribution.

By the way, Python’s SciPy package provides comprehensive support for gamma distribution: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.gamma.html.

With an equation describing the market taker order sizes, we are ready to answer the last and the most important question:”How do we estimate the probability of our own order getting filled by the next trade event?” Suppose that at some moment in time, we place a maker buy order at price level p on the order book and our own order size is small enough. Assume the L2 order book snapshot on the bid side looks like this:

p1,s1
p2,s2
...
pn,sn
p,s
...

Here p1,p2,…,pn are all the price levels higher than p. s1,s2,…,sn are the aggregated order sizes for all the orders resting on the corresponding price levels, respectively. By the way, you can easily obtain such order book snapshots in realtime using C++ or Python with the help of our library (https://github.com/crypto-chassis/ccapi#specify-subscription-market-depth). The probability that our own order gets filled by the next trade event is equal to the probability that the next incoming taker sell order’s size is greater than s1+s2+…+sn. If we denote gamma distribution’s cumulative distribution function (the regularized gamma function) as f, then that probability is equal to 1-f(s1+s2+…+sn). Then if we think, for example, 30% of the order flow on the crypto markets is toxic and want to avoid being hit by a toxic order, we can calculate the desired price level of our maker order by requiring that the probability that our own order gets filled by the next trade event is less than 70%, i.e. our price level p should satisfy 1-f(s1+s2+…+sn) = 0.7. Mission completed.

That’s all. If you are interested in our work or collaborating with us, join us on Discord: https://discord.gg/b5EKcp9s8T and find us on Github: https://github.com/crypto-chassis/ccapi 🎉. We specialize in market data collection, high speed trading system, infrastructure optimization, and proprietary market making.

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

--

--