0% found this document useful (0 votes)
314 views35 pages

Avellaneda-Stoikov Market Making Model

The document summarizes algorithmic market making models. It discusses the Avellaneda-Stoikov model, which is a simple but influential model that serves as the basis for other models. The Avellaneda-Stoikov model assumes the agent has no price drift opinion and focuses only on inventory risk. It derives the optimal bid and ask quotes by maximizing expected utility from inventory holding at terminal time, using a constant absolute risk aversion utility function. The model results in the optimal quotes being a spread above and below the reservation price, which measures inventory holding risk. The spread is a function of model parameters like volatility, risk aversion, and order arrival rates.

Uploaded by

RemoCPI
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
314 views35 pages

Avellaneda-Stoikov Market Making Model

The document summarizes algorithmic market making models. It discusses the Avellaneda-Stoikov model, which is a simple but influential model that serves as the basis for other models. The Avellaneda-Stoikov model assumes the agent has no price drift opinion and focuses only on inventory risk. It derives the optimal bid and ask quotes by maximizing expected utility from inventory holding at terminal time, using a constant absolute risk aversion utility function. The model results in the optimal quotes being a spread above and below the reservation price, which measures inventory holding risk. The spread is a function of model parameters like volatility, risk aversion, and order arrival rates.

Uploaded by

RemoCPI
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

MarketMaking Models - Summary [Link]

Algorithmic Market Making

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.)

Ave llane da Stoik ov (2008): [Link] w w .[Link]/f aculty/avellane/[Link]


([Link] w w .[Link]/f aculty/avellane/[Link] )

Gue ant, Le halle , and Fe rnande z-Tapia (2012): [Link] /[Link] ([Link]
/pdf /[Link] )

Carte a, Jaim ungal and Ricci (2012) [Link] [Link]


/325c/980a17bf a4af 18f d33f 7e38f 20cacf 01699f .pdf ([Link] [Link]
/325c/980a17bf a4af 18f d33f 7e38f 20cacf 01699f .pdf )

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.

Sophie Larue lle - [Link] [Link]/w p-content/uploads/2013/06/CAHIER_MICRO_1.pdf


([Link] [Link]/w p-content/uploads/2013/06/CAHIER_MICRO_1.pdf )

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]

In [2]: isin = 'ES0000012F43'


date = '2019-07-18'

df = pd.read_sql("""SELECT time, isin, bidqty0, bid0, ask0, ask


qty0 FROM [Link]
where isin='%s' and time::date='%s'"""%(isi
n, date),
con=get_engine(), index_col='time', parse_date
s=True)

[Link]()

Out[2]:
is in bidqty0 bid0 as k 0 as k qty0

tim e

2019-07-18 08:46:01.181317 ES0000012F43 0.0 0.00 0.00 0.0

2019-07-18 17:02:51.830379 ES0000012F43 2.0 101.87 102.13 11.0

2019-07-18 17:05:00.421313 ES0000012F43 14.0 101.87 102.14 11.0

2019-07-18 17:06:00.744654 ES0000012F43 12.0 101.89 102.14 11.0

2019-07-18 17:07:03.006506 ES0000012F43 17.0 101.89 102.14 11.0

In [3]: df = [Link]['%s 09:30:00'% date:'%s 17:00:00'% date]


df = [Link](rule='T').mean().fillna(method='ffill')
df['midprice'] = [Link](df[['bid0', 'ask0']], axis=1)
df = df.sort_index()

In [4]: df['midprice'].plot(figsize=(16,6), color='#ec0000', label='Mid


price')
[Link]('ISIN %s, best of book midprice at date %s'%(isin, da
te))
[Link]()
[Link]();

2 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

In [5]: print('Actual Best of Book Data for ISIN %s at date %s'%(isin,


date))

Actual Best of Book Data for ISIN ES0000012F43 at date 2019-0


7-18

In [6]: [Link]()

Out[6]:
bidqty0 bid0 as k 0 as k qty0 m idprice

tim e

2019-07-18 09:31:00 11.5 101.3525 101.9625 16.0 101.65750

2019-07-18 09:32:00 11.5 101.3525 101.9625 16.0 101.65750

2019-07-18 09:33:00 8.5 101.3700 101.9750 16.0 101.67250

2019-07-18 09:34:00 8.5 101.3800 101.9850 16.0 101.68250

2019-07-18 09:35:00 11.5 101.3925 101.9950 13.0 101.69375

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:

u(s, x, q, t) = max E t[ − exp( − γ(X T + q TS T))]


δa , δb

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δ

+ max λ a(δ a)[u(s, x + s + δ a, q − 1, t) − u(s, x, q, t)] = 0,


δa

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.

In the end, the model depends on the f ollow ing parameters:

σ : 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]

In [8]: import numpy as np


import pandas as pd
import plotly.graph_objects as go
from [Link] import make_subplots
from [Link] import get_engine
import [Link] as plt
%matplotlib inline

class AvellanedaStoikov(object):
def __init__(self, base_price, sigma, gamma, k, a, initial_
inventory=0, max_inventory=10):

# Save the input variables


self.base_price = base_price
[Link] = 1 / len(base_price)
[Link] = len(base_price)
[Link] = sigma
[Link] = gamma
self.a = a
self.k = k
self.ask_orders = 0
self.bid_orders = 0
self.orders_in_book = [0, 0]
self.initial_price = base_price[0]
self.single_model_results = None
self.simulation_results = {}
[Link] = None
self.running_time = 0
[Link] = 0
self.order_book = []
self.initial_inventory = initial_inventory
self.max_inventory = max_inventory

# 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]

3.1 - Running the mode l

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.

In [9]: model = AvellanedaStoikov(base_price=p, sigma=0.2, gamma=0.1,


k=5.04, a=100)
model.run_model()
orderbook = model.get_orderbook()

In [10]: print('Best of Book Mean Spread', round((df['ask0']-df['bid0


']).mean(), 3))
print('Model Mean Spread:', round((orderbook['ask']-orderbook['
bid']).mean(), 3))

Best of Book Mean Spread -1.509


Model Mean Spread: 0.395

The code w orks as f ollow s:

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:

In [11]: indexing = df.reset_index().reset_index()[['index', 'time']]


indexing['index'] = indexing['index']+1
orderbook = [Link](indexing, how='left', left_on='t',
right_on='index').set_index('time')
orderbook = [Link](['t', 'index'], axis=1)
[Link]()

Out[11]:
bidqty bid as k as k qty orde r type

tim e

2019-07-18 09:31:00 1.0 101.459034 101.855966 1.0 New LO

2019-07-18 09:31:00 1.0 101.459034 101.855966 1.0 Sell MO

2019-07-18 09:31:00 1.0 101.459034 101.855966 1.0 Partial Cancel

2019-07-18 09:32:00 1.0 101.470057 101.866979 1.0 New LO

2019-07-18 09:32:00 1.0 101.470057 101.866979 1.0 Complete Cancel

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]

In [12]: model_sim = AvellanedaStoikov(base_price=p, sigma=0.13, gamma=


0.6, k=10, a=0.5)
model_sim.run_simulation(n_simulations=100)
model_sim.plot_simulation(index = [Link], best_ask=df['ask0
'], best_bid=df['bid0'],
title='Avellaneda Stoikov Simulation
on ISIN %s for date %s'%(isin, date),
height=700, width=950, template='seab
orn')

Avellaneda Stoikov Simulation on ISIN ES0000012F43 for date

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

Ask Bid Best Ask Best Bid


ReservationPrice Spread Position Position

8 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

4 - Gueant Lehalle Fernandez-Tapia

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.

the optimal spread is given in the basic case by:

σ 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 / γ

GLT also show how to include a drif t μ on the computations:

σ 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]

In [13]: class GueantLehalleTapia(AvellanedaStoikov):


def __init__(self, base_price, sigma, gamma, k, a, xi=0.0,
initial_inventory=0, use_reservation_price=False,
max_inventory=10):
super(GueantLehalleTapia, self).__init__(base_price=bas
e_price, sigma=sigma, gamma=gamma, k=k, a=a,
initial_invent
ory=initial_inventory)
[Link] = xi
self.use_reservation_price = use_reservation_price
if max_inventory is None:
self.max_inventory = 9999
else:
self.max_inventory = max_inventory

def __calculate_spread__(self, t):

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]))

self.bid_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]))

self.bid_spread[t] = 1 / [Link] * [Link](1 + se


[Link] / self.k) + (
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]))

[Link][t] = [Link](self.ask_spread[t] + [Link]


_spread[t], 5)

def __quote_bid_ask__(self, t):


self.__reservation_price__(t)

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]

In [14]: glt = GueantLehalleTapia(base_price=p, sigma=0.13, gamma=0.3,


k=12, a=150, max_inventory=3)
glt.run_simulation(n_simulations=500)
glt.plot_simulation(index = [Link], best_ask=df['ask0'], best
_bid=df['bid0'],
title='Avellaneda Stoikov Simulation
on ISIN %s for date %s'%(isin, date),
height=700, width=950, template='seab
orn')

Avellaneda Stoikov Simulation on ISIN ES0000012F43 for date

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

Ask Bid Best Ask Best Bid


ReservationPrice Spread Position Position

13 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

4.2 - ξ ≠ 0 - Including Inv e ntory Risk and Side -e ffe ct

14 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

In [15]: glt = GueantLehalleTapia(base_price=p, sigma=0.13, gamma=0.3,


k=12, a=8, max_inventory=3, xi= 0.05)
glt.run_simulation(n_simulations=500)
glt.plot_simulation(index = [Link], best_ask=df['ask0'], best
_bid=df['bid0'],
title='Avellaneda Stoikov Simulation
on ISIN %s for date %s'%(isin, date),
height=700, width=950, template='seab
orn')

Avellaneda Stoikov Simulation on ISIN ES0000012F43 for date

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

Ask Bid Best Ask Best Bid


ReservationPrice Spread Position Position

15 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

4.3 - Using the Re se rv ation Price on GLT

16 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

In [16]: glt = GueantLehalleTapia(base_price=p, sigma=0.13, gamma=0.3,


k=12, a=8, max_inventory=3, xi= 0.05, use_reservation_price=Tru
e)
glt.run_simulation(n_simulations=20)
glt.plot_simulation(index = [Link], best_ask=df['ask0'], best
_bid=df['bid0'],
title='Avellaneda Stoikov Simulation
on ISIN %s for date %s'%(isin, date),
height=700, width=950, template='seab
orn')

Avellaneda Stoikov Simulation on ISIN ES0000012F43 for date

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

Ask Bid Best Ask Best Bid


ReservationPrice Spread Position Position

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.

E[ − exp( − γ(X T + q tS T − l( | q T | )))]

ξ = γ in the resulting model.

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]

ξ = 0 in the resulting model.

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]

In [17]: class Gueant(AvellanedaStoikov):


def __init__(self, base_price, sigma, gamma, k, a, delta=1,
use_xi=False, initial_inventory=0, use_reservation_price=Fals
e):
super(Gueant, self).__init__(base_price=base_price, sig
ma=sigma, gamma=gamma, k=k, a=a,
initial_inventory=
initial_inventory)
self.use_xi = use_xi
self.max_inv = delta
self.use_reservation_price = use_reservation_price

if use_xi:
[Link] = [Link]
else:
[Link] = 0
[Link] = delta

def __calculate_spread__(self, t):


if [Link] == 0:
self.bid_spread[t] = 1 / self.k + (2 * [Link]
ry[t - 1] - [Link]) / 2 * [Link](
([Link] * [Link] ** 2 * [Link](1)) / (2
* self.a * [Link] * self.k))
self.ask_spread[t] = 1 / self.k - (2 * [Link]
ry[t - 1] - [Link]) / 2 * [Link](
([Link] * [Link] ** 2 * [Link](1)) / (2
* self.a * [Link] * self.k))
elif [Link] > 0:
in_log = 1+[Link]*[Link]/self.k
inv_frac_bid = (2*[Link][t-1]+[Link])/2
inv_frac_ask = (2 * [Link][t - 1] - [Link]
lta) / 2
sqrt_1 = ([Link]*[Link]**2)/(2*self.a*self.
delta*self.k)
sqrt_2 = (1+[Link]*[Link]/self.k)**(self.k/(se
[Link]*[Link])+1)
frac = 1 / ([Link]*[Link])
self.bid_spread[t] = frac * [Link](in_log) + inv_fr
ac_bid * [Link](sqrt_1 * sqrt_2)
self.ask_spread[t] = frac * [Link](in_log) - inv_fr
ac_ask * [Link](sqrt_1 * sqrt_2)
else:
raise ValueError('Xi must be greater than or equal
to zero.')

[Link][t] = [Link](self.ask_spread[t] + [Link]


_spread[t], 5)

def __quote_bid_ask__(self, t):


self.__reservation_price__(t)
self.__calculate_spread__(t)

# Set the quotes:


if not self.use_reservation_price:
[Link][t] = self.base_price[t] + self.ask_spread

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]

In [18]: ft = Gueant(base_price=df['midprice'], sigma=0.3, gamma=0.1, k=


8, a=20, use_xi=False, delta=1)
ft.run_simulation(n_simulations=500)
ft.plot_simulation(index = [Link], best_ask=df['ask0'], best_
bid=df['bid0'],
title='Avellaneda Stoikov Simulation
on ISIN %s for date %s'%(isin, date),
height=700, width=950, template='seab
orn')

Avellaneda Stoikov Simulation on ISIN ES0000012F43 for date

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

Ask Bid Best Ask Best Bid


ReservationPrice Spread Position Position

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]

In [19]: ft2 = Gueant(base_price=df['midprice'], sigma=0.3, gamma=0.1,


k=8, a=20, use_xi=True, delta=2)
ft2.run_simulation(n_simulations=500)
ft2.plot_simulation(index = [Link], best_ask=df['ask0'], best
_bid=df['bid0'],
title='Avellaneda Stoikov Simulation
on ISIN %s for date %s'%(isin, date),
height=700, width=950, template='seab
orn')

Avellaneda Stoikov Simulation on ISIN ES0000012F43 for date

100
Price

50

0
10:00 11:00 12:00 13:00 14:00
Jul 18, 2019

1.5

1
Spread

0.5

10:00 11:00 12:00 13:00 14:00


Jul 18, 2019

300
80
Simulations

60 200
40
100
20
0 0
−5 0 5 −300 −200

Ask Bid Best Ask Best Bid


ReservationPrice Spread Position Position

23 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

6 - Simulating consecutive days in one run.

6.1 - One run:

In [20]: isin = 'ES0000012F43'

daydata = pd.read_sql("""SELECT time, isin, bidqty0, bid0, ask


0, askqty0 FROM [Link] where isin='%s'"""%(isin),
con=get_engine(), index_col='time', parse_date
s=True)
daydata['day'] = [Link]
[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

In [21]: def build_day_data(df):


df = df.sort_index()
df = [Link]("bid0>0 and ask0>0")
date = str([Link][0]['day'])
df = [Link]['%s 09:30:00'% date:'%s 17:00:00'% date]
df = [Link](rule='T').mean().fillna(method='ffill')
df['midprice'] = [Link](df[['bid0', 'ask0']], axis=1)
return df

In [22]: ddd = [Link]('day').apply(build_day_data)

In [23]: base_price = ddd['midprice'].ravel()


best_ask = ddd['ask0'].ravel()
best_bid = ddd['bid0'].ravel()

In [24]: plot_index = ddd.reset_index().set_index('time').index

24 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

In [25]: model = AvellanedaStoikov(base_price=base_price, sigma=1.7, gam


ma=0.1, k=6, a=200)
model.run_model()

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]

In [27]: 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('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))

Mean Spread: 0.475


Mean Inventory: 0.104
Final inventory: -2.0
Final Equity: -2.057
Final Wealth: 204.323

6.2 - Simulation

In [28]: model_sim = AvellanedaStoikov(base_price=base_price, sigma=1.7,


gamma=0.1, k=6, a=200)
model_sim.run_simulation(n_simulations=10)

26 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

In [29]: model_sim.plot_simulation(index = plot_index, best_ask=best_as


k, best_bid=best_bid,
title='Avellaneda Stoikov Simulation
on ISIN %s'%(isin),
height=700, width=950, template='seab
orn')

Avellaneda Stoikov Simulation on ISIN ES0000012F4

120

100
Price

80

Jun 23 Jul 7 Jul 21 Aug 4 Aug 18 Sep 1


2019

1.2
Spread

0.8

Jun 23 Jul 7 Jul 21 Aug 4 Aug 18 Sep 1


2019

2 2
Simulations

1.5 1.5
1 1
0.5 0.5
0 0
−4 −2 0 2 4 15 20

Ask Bid Best Ask Best Bid


ReservationPrice Spread Position Position

7 - Simulating consecutive days, one model per day.


In [30]: ddd = ddd.reset_index().set_index('time')

27 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

In [31]: dates = [str(d) for d in ddd['day'].unique().tolist()]

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]

__ Metrics last trading day __


Mean Spread: 0.101
Mean Inventory: -1.533
Final inventory: -4.0
Final Equity: -95.852
Final Wealth: 316.908
Mean End-day Inventory -1.097

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]

__ Metrics last trading day __


Mean Spread: 0.1
Mean Inventory: 0.202
Final inventory: -5.0
Final Equity: 109.737
Final Wealth: 625.687
Mean End-day Inventory 0.409

33 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

8 - Next Steps:

Case 1: Brownian price + Poisson Process + Avellaneda


Stoikov
In [36]: start = '2019-07-23 09:30:00'
end = '2019-07-23 18:00:00'
freq = 'T'

price = Brownian(start_price=100, sigma=0.05)


orders = PoissonProcess(a=150, k=30)
model = AvellanedaStoikov(gamma=0.8)

sim = Simulator(price_engine=price, order_engine=orders, model=


model).config(start, end, freq).run_model()
sim.matplotlib_plot()

NameErrorTraceback (most recent call last)


<ipython-input-36-74544fd41645> in <module>()
3 freq = 'T'
4
----> 5 price = Brownian(start_price=100, sigma=0.05)
6 orders = PoissonProcess(a=150, k=30)
7 model = AvellanedaStoikov(gamma=0.8)

NameError: name 'Brownian' is not defined

Case 2: Market Price + Poisson Process + Avellaneda


Stoikov
In [ ]: day = '2019-07-23'
freq = 'T'
isin = 'ES0000012F43'
start = '10:00:00'
end = '16:00:00'

price = DayPrice(day=day, isin=isin)


orders = PoissonProcess(a=50, k=60)
model = AvellanedaStoikov(gamma=0.8)

sim = Simulator(price_engine=price, order_engine=orders, model=


model).config(start, end, freq, sigma=0.13).run_model()
sim.matplotlib_plot()

34 de 35 25/10/2019 14:34
MarketMaking Models - Summary [Link]

Case 3: Historical Data + Poisson Process + Avellaneda


Stoikov
In [ ]: freq = 'T'
isin = 'ES0000012F43'
start = '2019-07-01 10:00:00'
end = '2019-07-23 17:00:00'

price = HistoricalPrice(isin=isin)
orders = PoissonProcess(a=50, k=60)
model = AvellanedaStoikov(gamma=0.3)

sim = Simulator(price_engine=price, order_engine=orders, model=


model).config(start, end, freq, sigma=0.13).run_model()
sim.matplotlib_plot()

In [ ]: from [Link] import HTML

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

Common questions

Powered by AI

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 .

You might also like