Avellaneda-Stoikov Market Making Model
Avellaneda-Stoikov Market Making Model
1 - Intro
Various pricing models f or market making have been proposed in the academic literature. Ho and Stoll
(1981) is one of the early studies that analyze the market making problem under a stochastic control
f ramew ork. Ave llane da and Stoik ov (2008) extends the model proposed by Ho and Stoll, derives the
optimal bid and ask quotes using asymptotic expansion and applies it to high-f requency market making.
Furthermore, Gue ant, Le halle , and Fe rnande z-Tapia (2013) develop the model f urther by deriving the
closed f orm solution of the optimal bid and ask spread w ith boundary conditions on inventory size.
Several other contribution have been made (Carte a e t al.)
Gue ant, Le halle , and Fe rnande z-Tapia (2012): [Link] /[Link] ([Link]
/pdf /[Link] )
Since then more advances have been made in the search of approximated analytical solutions of the
problem (Alvaro Carte a), and currently other approaches are being explored, mainly reinf orcement
learning (Fe rnande z Tapia 2015) and extended versions of the stochastic control problem (Olivie r
Gué ant 2017)
Note: In every simulation it is considered that the arrival rate of market orders f ollow s a Poisson process
w ith intensity λ, as w as f irst suggested by Avellaneda and Stoikov.
2 - Data
In [1]: import pandas as pd
from [Link] import get_engine
import numpy as np
import [Link] as plt
%matplotlib inline
1 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
[Link]()
Out[2]:
is in bidqty0 bid0 as k 0 as k qty0
tim e
2 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
In [6]: [Link]()
Out[6]:
bidqty0 bid0 as k 0 as k qty0 m idprice
tim e
In [7]: p = df['midprice']
3 - Avellaneda Stoikov
3 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
The Ave llane da Stoik ov model is a simple model that has served as the bas e for e ve ry othe r
m ark e t m ak ing algorithm . The model assumes that our agent has no opinion on the drift or any
autocorre lation s tructure for the as s e t, it only f ocuses on inventory risk, and not about asymmetric
inf ormtion risk. It also assumes that the agent is the only one player in the market. The problem or
de ale rs unde r com pe tition w as analysed by Ho and Stoll (1981) and it can be show n that this
problem is related to the re s e rvation or indiffe re nce price (w hich Avellaneda and Stoikov use in the
model, althought they are not modelling other market players in an explicit w ay).
In the Avellaneda and Stoikov model, it is assumed that the mid price f ollow s a brow nian motion:
dS u = σdW u
The objective is to maximize the P&L prof ile at a terminal time T, taking into account that there exists a risk
if a non-zero inventory is held. To this end w e f ocus on CARA utility f unctions (Constant Absolute Risk
Aversion) and try to f ind the f unction u that satisf ies:
One of the key steps in the solution is to use the dynamic programming principle to show that the f unction
u solves the f ollow ing Hamilton–Jacobi–Bellman equation:
1 2
ut + σ u SS + max λ b(δ b)[u(s, x − s + δ b, q + 1, t) − u(s, x, q, t)]
2 bδ
u(s, x, q, T) = − e − γ ( x + qs )
The result of the analysis is that the optimal bid and ask prices are given by quoting a spread given by:
2 γ
δ a + δ b = γσ 2(T − t) + log(1 + )
γ k
around the reservation price, w hich 'measures' the risk of holding an inventory. The reservation bid price
is the price that w ould make the agent indif f erent betw een his current portf olio and his current portf olio
plus one asset. The reservation ask price is def ined similarly.
σ : Volatility
γ : Risk Aversion
k and A: Parameters f or arrival rates of market orders.
4 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
class AvellanedaStoikov(object):
def __init__(self, base_price, sigma, gamma, k, a, initial_
inventory=0, max_inventory=10):
# Initialize variables
[Link] = None
[Link] = None
[Link] = None
[Link] = None
[Link] = None
[Link] = None
[Link] = None
[Link] = None
[Link] = None
self.prob_a = None
self.prob_b = None
def __initialize_state_vectors__(self):
[Link] = [Link](([Link],))
[Link][0] = self.initial_price
[Link] = [Link](([Link],))
[Link][0] = self.initial_inventory
[Link] = [Link](([Link],))
[Link][0] = self.initial_price
5 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
To start of f , w e w ill calibrate the parameters k and A so as to get a mean spread similar to the mean
spread of the best of book and a relatively inactive market. The volatility σ w ill be calculated just as the
standard deviation of the mid price. The risk aversion parameter γ w ill have a standard value of 0.1.
At time t w e calculate the reservation price based of the current inventory and quote bid and ask
prices given the optimal spread.
Calculate the arrival of market orders and act according to the result (hits/lif ts).
Af ter the MO's have been processed, w e cancel the LO and prepare to give new quotes at time
t+1.
The result of the process can be seen in the f ollow ing table:
Out[11]:
bidqty bid as k as k qty orde r type
tim e
6 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
3.2 - Simulation
We can also run several simulations to get some statistics about the f inal inventory and the f inal P&L.
7 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
100
Price
50
0
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
0.405
Spread
0.4
0.395
0.39
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
100
Simulations
60
40 50
20
0 0
−1 0 1 2 −100 −50
8 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
9 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
The f ollow ing model by Gue ant, Le halle and Fe rnande z-Tapia (GLT), improves the results of
Avellaneda and Stoikov. They show that under a change of variables, the HJB equations simplif y to a
system of linear ordinary dif f erential equations, w hich simplif ies the computation of a solution and allow s
to study the asymptotic behavior of the optimal quotes. In addition they provide an approximation of the
optimal quotes in closed-f orm.
σ 2γ
√
1 γ 2q + 1 γ 1+k/γ
b∗
δ ∞ (q) ≈ log 1 +
γ k
+ ( 2 ) 2kA (
1+
k )
σ 2γ
√
1 γ 2q − 1 γ 1+k/γ
a∗
δ ∞ (q) ≈ log 1 +
γ k
− ( 2 ) 2kA (
1+
k )
σ 2γ
√
∗ 2 γ γ
ψ∞ ≈ log (1 + ) +
γ k 2kA
(1 + k ) 1 + k / γ
σ 2γ
b∗
δ ∞ (q) ≈
1
γ
log 1 +(γ
k
+ ) [ −
γσ 2
μ
+
2q + 1
2 ]√ ( 2kA
1+
k )
γ 1+k/γ
σ 2γ
]√ 2kA (1 + k )
1 γ μ 2q − 1 γ 1+k/γ
a∗
δ ∞ (q) ≈
γ
log 1 + ( k) [
+
γσ 2
−
2
σ 2γ
√
∗ 2 γ γ
ψ∞ ≈ log (1 + ) + ( 1 + )1 + k / γ
γ k 2kA k
The impact of market orders can also be taken into account to f ind:
σ 2γ
( ) √ ( )
b∗ 1 γ ξ 2q + 1 kξ / 4 γ 1k / γ
δ ∞ (q) ≈ log 1 + + + e 1+
γ k 2 2 2kA k
σ 2γ
( ) √ ( )
a∗ 1 γ ξ 2q − 1 kξ / 4 γ 1k / γ
δ ∞ (q) ≈ log 1 + + − e 1+
γ k 2 2 2kA k
σ 2γ
√
∗ 2 γ γ
ψ∞ ≈ log (1 + ) + ξ + e kξ / 4 ( 1 + )1 + k / γ
γ k 2kA k
Which considers adverse selection (ξ / 2) and side ef f ect of market orders (e kξ / 4).
To include inventory limit, w e set a maximum (minimum) inventory Q (-Q). If the maximum inventory limit is
reached, no new Limit orders are placed at that size of the LOB.
10 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
if [Link] != 0:
self.ask_spread[t] = 1 / [Link] * [Link](1 + se
[Link] / self.k) + [Link] / 2 - (
2 * [Link][t - 1] - 1) / 2 * [Link]
(self.k * [Link] / 4) * [Link](
([Link] * [Link] ** 2) / (2 * self.k *
self.a) * (1 + [Link] / self.k) ** (
1 + self.k / [Link]))
else:
self.ask_spread[t] = 1 / [Link] * [Link](1 + se
[Link] / self.k) - (
2 * [Link][t - 1] - 1) / 2 * n
[Link](
([Link] * [Link] ** 2) / (2 * self.k *
self.a) * (1 + [Link] / self.k) ** (
1 + self.k / [Link]))
11 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
4.1 - ξ = 0
The f ollow ing simulation is meant to show case the use of maximum_inventory limit, in it w e can see that
even if the trading activity increases, w e never go above (or below ) the maximum (or minimum) inventory
limits.
12 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
100
Price
50
0
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
1
Spread
0.5
−0.5
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
80
80
Simulations
60
60
40 40
20 20
0 0
−2 0 2 −200
13 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
14 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
100
Price
50
0
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
1
Spread
0.5
−0.5
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
400
Simulations
100 300
200
50
100
0 0
−2 0 2 −100
15 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
16 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
100
Price
50
0
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
1
Spread
0.5
−0.5
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
4
Simulations
3 10
2
5
1
0 0
−3 −2 −1 0 1 2 −100 −50
17 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
5 - Gueant
M ode l A: The market maker maximizes the expected value of a CARA utility f unction w ith risk aversion
parameter γ > 0, applied to the MtM value of the portf olio at a given date T, w hich is equal to
X T + q tS T − l( | q T | ) if a liquidity premium f or the remaining inventory is considered.
Here, the market maker is not only averse to price ris k , but also to the risk of not f inding a counterparty
to trade w ith – this is the so-called non-e xe cution ris k .
M ode l B: The market maker maximizes the expected value of the MtM value of the porf olio at time T, but
holding an inventory is penalized over the time interval [0, T]. The goal is then to maximize:
1 2 T 2
2 ∫0 t
E[X T + q tS T − l( | q T | ) − γσ q dt]
Here, the market maker is only averse to the risk associated w ith price changes
To sum up, the parameter ξ can be regarded as some f orm of risk aversion parameter applying to non-
execution risk only.
Note : For Δ = 1 and ξ = γ w e recover the model of Gueant, Lehalle and Fernandez-Tapia
18 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
if use_xi:
[Link] = [Link]
else:
[Link] = 0
[Link] = delta
19 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
5.1 - ξ = 0
20 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
100
Price
50
0
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
1.5
1
Spread
0.5
−0.5
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
80 300
Simulations
60
200
40
100
20
0 0
−5 0 5 −200 −100
21 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
5.1 - ξ > 0
22 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
100
Price
50
0
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019
1.5
1
Spread
0.5
300
80
Simulations
60 200
40
100
20
0 0
−5 0 5 −300 −200
23 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
Out[20]:
is in bidqty0 bid0 as k 0 as k qty0 day
tim e
2019-06-13
ES0000012F43 0.0 0.00 0.00 0.0 2019-06-13
08:46:01.664625
2019-06-13
ES0000012F43 12.0 98.69 100.69 12.0 2019-06-13
08:58:07.223714
2019-06-13
ES0000012F43 12.0 98.70 100.70 12.0 2019-06-13
08:59:17.594913
2019-06-13
ES0000012F43 12.0 98.69 100.69 12.0 2019-06-13
09:03:34.778938
2019-06-13
ES0000012F43 1.0 99.33 99.93 1.0 2019-06-13
09:04:39.073466
24 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
In [26]: [Link](figsize=(16,6))
[Link]([Link], label='Model Ask')
[Link](model.base_price, label='Base Price (Mid)')
[Link]([Link], label='Model Bid')
[Link](loc='lower right')
[Link]('Avellaneda Stoikov result in historical data');
[Link](figsize=(16,4))
[Link]([Link])
[Link]('Avellaneda Stoikov Inventory (Historical Data)');
[Link](figsize=(16,4))
[Link]([Link])
[Link]('Avellaneda Stoikov Equity Curve');
25 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
6.2 - Simulation
26 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
120
100
Price
80
1.2
Spread
0.8
2 2
Simulations
1.5 1.5
1 1
0.5 0.5
0 0
−4 −2 0 2 4 15 20
27 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
In [32]: input_inventory = 0
full_bid = []
full_ask = []
full_mid = []
full_inv = []
full_eq = []
end_day_invs = []
full_spread = []
for d in dates:
day_data = ddd[ddd['day']== pd.to_datetime(d).date()]
base_price = day_data['midprice'].ravel()
std = base_price.std()
model = AvellanedaStoikov(base_price=base_price, sigma=0.1
5, gamma=0.1, k=20, a=200, initial_inventory=input_inventory)
model.run_model()
full_bid.append([Link])
full_ask.append([Link])
full_inv.append([Link])
full_mid.append(model.base_price)
full_spread.append([Link])
full_eq.append([Link])
input_inventory = [Link][-1]
end_day_invs.append(input_inventory)
28 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
In [33]: [Link](figsize=(16,6))
[Link]([Link]([Link](full_bid)), label='Bid')
[Link]([Link]([Link](full_ask)), label='Ask')
[Link]([Link]([Link](full_mid)), label='Mid')
[Link]();
spread_daily = [Link]([Link](full_spread))
spread_daily = spread_daily[[Link](spread_daily>0)]
[Link](figsize=(16,4))
[Link](spread_daily, label='Spread')
[Link]('Spread over Time');
inv_daily = [Link]([Link](full_inv))
[Link](figsize=(16,4))
[Link](inv_daily, label='Inventory')
[Link]('Inventory over time for historial data');
[Link](figsize=(16,4))
[Link]([Link]([Link](full_eq)), label='P&L')
[Link]('P&L over time for historial data');
mean_spread = round(float([Link]()), 3)
mean_inventory = round(float([Link]()), 3)
end_inv = [Link][-1]
end_eq = [Link][-1]
end_w = [Link][-1]
print('__ Metrics last trading day __')
print('Mean Spread:', mean_spread)
print('Mean Inventory:', mean_inventory)
print('Final inventory:', end_inv)
print('Final Equity:', round(end_eq,3))
print('Final Wealth:', round(end_w,3))
print('Mean End-day Inventory', round([Link](end_day_invs),3))
# Pintar Equity
29 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
30 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
In [34]: input_inventory = 0
full_bid = []
full_ask = []
full_mid = []
full_inv = []
full_eq = []
end_day_invs = []
full_spread = []
for d in dates:
day_data = ddd[ddd['day']== pd.to_datetime(d).date()]
base_price = day_data['midprice'].ravel()
std = base_price.std()
model = Gueant(base_price=base_price, sigma=0.15, gamma=0.
1, k=20, a=200, initial_inventory=input_inventory)
model.run_model()
full_bid.append([Link])
full_ask.append([Link])
full_inv.append([Link])
full_mid.append(model.base_price)
full_eq.append([Link])
full_spread.append([Link])
input_inventory = [Link][-1]
end_day_invs.append(input_inventory)
31 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
In [35]: [Link](figsize=(16,6))
[Link]([Link]([Link](full_bid)), label='Bid')
[Link]([Link]([Link](full_ask)), label='Ask')
[Link]([Link]([Link](full_mid)), label='Mid')
[Link]();
spread_daily = [Link]([Link](full_spread))
spread_daily = spread_daily[[Link](spread_daily>0)]
[Link](figsize=(16,4))
[Link](spread_daily, label='Spread')
[Link]('Spread over Time');
inv_daily = [Link]([Link](full_inv))
[Link](figsize=(16,4))
[Link](inv_daily, label='Inventory')
[Link]('Inventory over time for historial data');
equity_daily = [Link]([Link](full_eq))
[Link](figsize=(16,4))
[Link](equity_daily, label='P&L')
[Link]('P&L over time for historial data');
mean_spread = round(float([Link]()), 3)
mean_inventory = round(float([Link]()), 3)
end_inv = [Link][-1]
end_eq = [Link][-1]
end_w = [Link][-1]
print('__ Metrics last trading day __')
print('Mean Spread:', mean_spread)
print('Mean Inventory:', mean_inventory)
print('Final inventory:', end_inv)
print('Final Equity:', round(end_eq,3))
print('Final Wealth:', round(end_w,3))
print('Mean End-day Inventory', round([Link](end_day_invs),3))
# Pintar Equity
32 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
33 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
8 - Next Steps:
34 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]
price = HistoricalPrice(isin=isin)
orders = PoissonProcess(a=50, k=60)
model = AvellanedaStoikov(gamma=0.3)
HTML('''<script>
code_show=true;
function code_toggle() {
if (code_show){
$('[Link]').hide();
} else {
$('[Link]').show();
}
code_show = !code_show
}
$( document ).ready(code_toggle);
</script>
<form action="javascript:code_toggle()"><input type="submit" va
lue="Click here to toggle on/off the raw code."></form>''')
35 de 35 25/10/2019 14:34
Subsequent studies improved the Avellaneda Stoikov model by addressing inventory control and incorporating more detailed market dynamics. Gueant, Lehalle, and Fernandez-Tapia (2013) extended the model by deriving the closed-form solution for the optimal bid and ask spread with inventory constraints. Cartea et al. further refined this by providing approximated analytical solutions and exploring models incorporating reinforcement learning techniques .
Using a CARA (Constant Absolute Risk Aversion) utility function in market making models allows for the management of inventory and price risks under a risk aversion scenario. It models the market maker's decision-making with consistent risk attitude, enabling optimization against portfolio variance and potential profits, while considering the liquidity premium for remaining inventory .
In the Avellaneda Stoikov model, the risk aversion parameter gamma ( b) affects trading strategy by influencing the spread calculation. Higher b values reflect increased reluctance to hold inventory, prompting tighter spreads and thus more conservative trading responses to limit potential losses. Consequently, gamma helps balance between earning profits from the spread and managing risk exposure .
Reinforcement learning offers potential improvements over traditional market making models by providing adaptive strategies that learn from real-time data. This method allows for dynamic adjustment to environmental changes, capturing complex market dynamics and optimizing decision-making processes beyond the capabilities of static analytical models. This flexibility positions reinforcement learning as a forward-looking approach to handling stochastic control problems .
Challenges in using real market data for calibrating market making models include handling the variance in bid-ask spreads, adapting to changing market conditions, and ensuring the model accurately represents supply-demand dynamics. Calibration requires careful selection of parameters such as order arrival rates and volatility, which require ongoing adjustments to reflect evolving market conditions accurately .
Setting xi to zero in market making algorithms simplifies risk management by solely addressing price risk, excluding non-execution risk factors. This adjustment leads to a focus on minimizing adverse effects from price fluctuations while ignoring potential cash flow disruptions from trading counterparties, as illustrated in the resulting behavior of the spread and inventory levels when xi equals zero .
Including inventory limits in the GueantLehalleTapia model ensures that trading activity remains within predefined boundaries by preventing new limit orders once the maximum inventory is reached. This creates a buffer against excessive risk from large inventory positions, as demonstrated by the model's constraint not to exceed set inventory limits even in high trading activity scenarios .
The Gueant model addresses non-execution risk by incorporating it into the utility function via the parameter xi, which represents risk aversion towards non-execution scenarios. This inclusion modifies the spread calculations to account for the market maker's expectation of finding a counterparty, thus directly impacting inventory and pricing strategies .
In simulations, the sigma parameter, representing volatility, influences market making strategies by affecting the calculation of bid-ask spreads. Higher sigma values lead to wider spreads to compensate for anticipated risk from potential large price movements. Consequently, sigma determines the aggressiveness of trading strategies, balancing risk and opportunity .
The Avellaneda Stoikov model simulates high-frequency trading environments by using stochastic control frameworks and assuming that market order arrival rates follow a Poisson process. By adjusting parameters like volatility and the risk aversion factor, the model dynamically responds to market data to replicate the conditions and behavior typical of high-frequency trading .