- Agent-based model simulation of Order Book liquidity dynamics
- Focus on procedural fairness of various matching systems
- Written in C++20
This helps clangd (in emacs/eglot) find system and lib includes.
nix-shell
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1
Build with cmake:
cmake --preset release
cmake --build --preset release
build/release/leyval
Or build with nix:
nix-build default.nix
./result/bin/leyval
An Order Book is a collection of Bid and Ask limit orders:
Bid | Ask | ||
Price | Quantity | Price | Quantity |
---|---|---|---|
25 | 8 | 31 | 4 |
27 | 7 | 32 | 55 |
29 | 5 | 34 | 7 |
If an Agent places a Buy Market Order with Q=2, this gets matched with the current best Ask Price P=31. The Order Book after this transaction looks like:
Ask | |
Price | Quantity |
---|---|
31 | 2 |
32 | 55 |
34 | 7 |
Now, consider multiple Ask Limit Orders from multiple Agents (1, 2, 3) sitting at the current best price level. Agent 1 placed their order before 2, and 2 before 3. The Matching System is responsible for determining how an incoming Market Order is allocated to these Limit Orders.
Q_1 | 20 |
Q_2 | 50 |
Q_3 | 30 |
These Limit Orders have a total Q = 100. The incoming Market Order has Q_m=25.
FIFO | Pro-Rata | Random | |
---|---|---|---|
Q_1 | 20-20 = 0 | 20-5 = 15 | (0,20,20) |
Q_2 | 50-5 = 45 | 50-13 = 37 | (50,25,50) |
Q_3 | 30-0 = 30 | 30-7 = 23 | (30,30,5) |
Under FIFO (First-in Last-Out), the Market Order gets allocated to the earliest order (Agent 1). This first Limit Order is not enough to fulfill the Market Order, so the remaining Q=5 overflow to the next-earliest order.
Under Pro-Rata, the Market Order gets split up proportional to the quantity weight of each agent. E.g Agent 1’s order constitutes w_1 = Q_1/Q = 20/100 = 20% of the total quantity at this price level. Agent 1’s allocation is thus Q_m * w_1 = 5. However Pro-Rata can never be pure, as seen in Agent 2 and 3; their allocations are 12.5 and 7.5, respectively. Quantities must always be integer values, so Agent 2 is rounded up due to FIFO priority.
Under Random, the Market Order is allocated similarly to Pro-Rata, but instead w_1 is interpreted as the probability of 1 share of Q_m being allocated to Agent 1. The two extreme results of this is all of Q_1 being fulfilled or none of Q_1 being fulfilled. The remaining (5 or 25) gets split across Q_2 and Q_3.
https://sci-hub.ru/10.1111/j.1540-6261.1995.tb05192.x
Investors can submit limit orders at any price…
The orders are stored and executed in the sequence that they are received by the market. Transactions occur when a trader on the opposite side of the market hits the quote. The limit orders for a specified quantity and price are stored and executed using time priority at a given price and price priority across prices.
“Market orders” in the Paris Bourse are not handled in the same way as in the NYSE. They are executed against the best price on the opposite side of the limit order book, but any excess that cannot be executed at that price is converted into a limit order at that price rather than being executed at less favorable prices by walking up (down) the book.
https://open.uct.ac.za/items/574390a1-2466-4128-8920-6261505220e0
Julia ABM Logic: https://github.com/IvanJericevich/IJPCTG-ABMCoinTossX/blob/main/Scripts/ABMVolatilityAuctionProxy.jl ./.notes/ABMVolatilityAuctionProxy.jl
The matching algorithms decide the efficiency and robustness of the order matching system. Exchanges aim to prioritize trades in a way that benefits buyers and sellers equally so as to maximize order volume — the lifeblood of the exchange.
We define limit order imbalance ρ(t){…}
The market consists of N_LT Liquidity Takers (LT) that only submit market orders and N_LP Liquidity Providers (LP) that only submit limit orders.
liquidity providers will on average provide liquidity to the side with less liquidity and thus stabilise the order book.
Calibration for values of \(N, δ, κ, ν, σ\)
Agent-based model implementation
Sample from power law distribution: https://stats.stackexchange.com/questions/173242/random-sample-from-power-law-distribution
\[ \text{Volume: } f(x) = \begin{cases}
\frac{α x_m^α}{xα+1} & x \geq x_m
0 & x < x_m
\end{cases} \]
\(x_m := \text{lower bound of the volume size}\) For LTs, is from a function. For LPs, is fixed at 10.
\[ \text{Shape Parameter: } α = \begin{cases}
1 - ρ/ν & \text{Sell MO, Ask LO}
1 + ρ/ν & \text{Buy MO, Bid LO}
\end{cases} \]
\(ρ := \text{OrderBook Imbalance [(bid size - ask size) / total]} \)
TBD: \(ν := \text{ABM parameter — scaling factor for power-law volume order size}\)
NOT EXACTLY SURE \(m := \text{mid-price}\) \(m = (\text{best ask} + \text{best bid})/2 \)
\[ \text{Decision: } D^F = \begin{cases}
\text{sell} & f < m
\text{buy} & f > m
\end{cases} \]
\(f := \text{fundamental value for agent} \) \(f = m_0exp{x}, x ∼ \mathcal{N}(0, σ^2) \)
\(m_0 := \text{mid-price at start of day}\) \(σ := \text{fundamentalists’ value perception uncertainty for the trading day}\)
\[ x_m^F = \begin{cases}
20 & |f - m| \leq δ m
50 & |f - m| > δ m
\end{cases} \]
\[ \text{Decision: } D^C = \begin{cases}
\text{sell} & m < \bar{m}
\text{buy} & m > \bar{m}
\end{cases} \]
\(\bar{m}(t) := \text{Exponential Moving Average (EMA) of mid-price} \) \(\bar{m}(t) = \bar{m}(t’) + λ(m(t) - \bar{m}(t’)) \) \(t’ := \text{time point of when agent made last decision} \) \(λ = 1 - exp{(-Δ t / τ)} \) \(Δ t = t - t’\) \( τ := \text{time constant for agent’s mean inter-arrival time of decision time} \)
\[ x_m^C = \begin{cases}
20 & |m - \bar{m}| \leq δ m
50 & |m - \bar{m}| > δ m
\end{cases} \]
\(θ := \text{Probability of Placing Ask}\) \(θ = \frac{1}{2}(ρ + 1)\)
\(p := \text{placement of limit order}\)
\[ p = \begin{cases}
\text{best bid} + 1 + ⌊\eta⌋ & \text{asks}
\text{best ask} - 1 - ⌊\eta⌋ & \text{bids}
\end{cases} \]
\(p := \text{placement of limit order}\)
https://www.investopedia.com/terms/m/market-price.asp
Since $30.02 was the last traded price, this is the market price.
https://www.investopedia.com/ask/answers/042215/what-do-bid-and-ask-prices-represent-stock-quote.asp
That’s because they can sell shares at the higher ask price and buy them at the lower bid price, profiting from the difference.
https://www.pnas.org/doi/pdf/10.1073/pnas.1205013109
- technical trader
- seeking arbitrage and making decisions from price patterns
- fundamentalist
- attempt to determine the fundamental value of stocks
We consider here only technical traders, assuming that fundamentalists contribute only to market noise.
- demand
- number of buy trades
- supply
- number of sell traddes
On the other hand, the individual strategies used by different technical traders differ in their parameterizations of the buy/sell time, amount of risk tolerated, or portfolio composition (15). So when the input signal—the previous price change rt−1—is small, every agent acts independently. When the input signal is large, the agents act more in concert, irrespective of their differences in trading strategies
https://www.youtube.com/watch?v=I5UY3yb0128
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
clang-tidy -checks='bugprone*, cppcoreguidelines* ,clang-analyzer* ,modernize* ,readability* ,\
-modernize-use-trailing-return-type, -readability-avoid-const-params-in-decls, \
-readability-identifier-length' \
src/*
clang-format -i --style=mozilla src/*
python run-clang-tidy.py \
-p . \
-checks='bugprone*, cppcoreguidelines* ,clang-analyzer* ,modernize* ,readability* ,\
-modernize-use-trailing-return-type, -readability-avoid-const-params-in-decls, \
-readability-identifier-length' \
-j 4 \
-style "Mozilla" -format