Limit Order Book Slope¶
Introduction¶
Some various slope measures of the limit order book (LOB). A steeply sloping order book indicates a lack of depth beyond the best bid or ask quote, a sign of illiquidity.
Note
The notations below largely follow the respective papers. Therefore, the same symbol may have different meanings.
Certain measures are estimated by (typically hourly) snapshots or intervals, where quantities supplied at each level are aggregated. If so, the functions here should be applied on data by intervals.
Næs and Skjeltorp (2006)¶
Næs and Skjeltorp (2006) introduce the slope measure of bid, ask and the LOB. They measure the slope at each price level by the ratio of the increase in depth over the increase in price, then average across all price levels.
where N^B and N^A are the total number of bid and ask prices (tick levels) of stock i,1 and \tau is the tick level. The best bid (ask) price is denoted by p_1^B (p_1^A), i.e. \tau=1. p_0 is the bid-ask midpoint. v_{\tau}^B and v_{\tau}^A denote the natural logarithms of the accumulated volume at each tick level \tau on the bid and ask sides.
Note
If we define V^B_\tau as the total volume demanded at p^B_\tau, then v_{\tau}^B = \ln \left(\sum_{j=1}^\tau V_j^B\right).
The LOB slope for stock i at time t is then computed as
Intuitively, the bid (ask) slope measures the percentage change in the bid (ask) volume relative to the percentage change in the corresponding bid (ask) price, which is averaged across all limit price levels in the bid (ask) order book, and the LOB slope is an equally weighted average of the bid and ask slopes.
Ghysels and Nguyen (2019)¶
Ghysels and Nguyen (2019) take a more direct approach. The slope is estimated from regressing the percentage of cumulative depth on the price distance at each level.
where QP_{it\tau} is the percentage of cumulative depth at level \tau at time t and |P_{i\tau} - m_{it}| is the distance between the price at level \tau and the best bid-ask midpoint. \beta_i is the estimated slope. The bid and ask slope are estimated separately.
Because the cumulative depths are standardized by the total depth across all K levels, QP_{itK} is always 100%. As such, the slope measure \beta depends only on how wide the K-level pricing grid is (the intercept \alpha takes care of QP_{it1}). Accordingly, this slope measure captures the tightness of prices in the book. A steeper slope indicates that limit orders are priced closer to the market. Another way of interpreting the slope is that it measures the elasticity of depth with respect to price. A steeper slope implies that for one unit increase in price, there is a greater increase in the quantity bid or offered, implying a greater willingness of liquidity providers to facilitate trading demand on each side of the market.
Hasbrouck and Seppi (2001)¶
Let B_k and A_k denote the per share bid and ask for quote record k, and let N_k^B and N_k^A denote the respective number of shares posted at these quotes. Thus, a prospective purchaser knows that, if hers is the first market buy order to arrive, she can buy at least N_k^A shares at the ask price A_k.
Note
These slope measures below are for the whole LOB, not specific to bid or ask.
Quote Slope¶
Log Quote Slope¶
Della Vedova, Gao, Grant and Westerholm (working paper)¶
Slope is measured by the cumulative volume at level K divided by the distance from the K-th level price to the bid-ask midpoint.
where \text{Depth}_{itx} is the sum of the quantity of available at bid/ask depth level x in stock i at time t, \text{Price}_{it}^K is the bid/ask price at the K-th level, and m_{it} is the bid-ask midpoint at time t.
Scaled Depth Difference¶
In addition, the relative asymmetry of depth is also informative about the relative demand and supply of the stock. Scaled Depth Difference (SDD) is constructed to capture the relative asymmetry in the LOB at a particular time at a particular level x.
where \text{Ask}_{itx} is the ask price of stock i at level x at time t and \text{Bid}_{itx} is the bid price of stock i at level x. SDD represents a scaled level of asymmetry at the prevailing quote to level x, ranging from -1 and 1. A value of SDD greater than zero indicates asymmetry in the direction of the ask side of the book.
API¶
DGGW_ask_slope(ask_size, ask_price_highest_level, mid_point)
¶
Ask Slope from Della Vedova, Gao, Grant and Westerholm (working paper)
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ask_size | np.ndarray | (n,k) array of LOB ask sizes, where | required |
ask_price_highest_level | np.ndarray | (n,) array of ask prices at the highest level | required |
mid_point | np.ndarray | (n,) array of bid-ask midpoints. | required |
Returns:
Name | Type | Description |
---|---|---|
float | float | ask slope |
Examples:
>>> from frds.measures.lob_slope import DGGW_ask_slope
>>> import numpy as np
>>> rng = np.random.RandomState(42)
>>> mid_point = np.ones((1000,)) # (1)
>>> lv1 = rng.uniform(low=100, high=120, size=(1000,)) # (2)
>>> lv2 = rng.uniform(low=90, high=110, size=(1000,)) # (3)
>>> lv3 = rng.uniform(low=80, high=100, size=(1000,))
>>> lv4 = rng.uniform(low=70, high=90, size=(1000,))
>>> lv5 = rng.uniform(low=50, high=80, size=(1000,))
>>> ask_size = np.array([lv1, lv2, lv3, lv4, lv5]).T
>>> ask_size.shape
(1000, 5)
>>> ask_price_highest_level = rng.uniform(low=1.1, high=2.0, size=(1000,)) # (4)
>>> DGGW_ask_slope(ask_size, ask_price_highest_level, mid_point)
1136.8718207500128
- Assume bid-ask midpoint stays at 1
- Simulated level 1 ask sizes
- Simulated level 2 ask sizes
- Simulated ask price at level 5
Source code in src/frds/measures/lob_slope.py
def DGGW_ask_slope(
ask_size: np.ndarray, ask_price_highest_level: np.ndarray, mid_point: np.ndarray
) -> float:
"""Ask Slope from Della Vedova, Gao, Grant and Westerholm (working paper)
Args:
ask_size (np.ndarray): (n,k) array of LOB ask sizes, where `n` is number of quotes and `k` is number of levels.
ask_price_highest_level (np.ndarray): (n,) array of ask prices at the highest level `k`.
mid_point (np.ndarray): (n,) array of bid-ask midpoints.
Returns:
float: ask slope
Examples:
>>> from frds.measures.lob_slope import DGGW_ask_slope
>>> import numpy as np
>>> rng = np.random.RandomState(42)
>>> mid_point = np.ones((1000,)) # (1)
>>> lv1 = rng.uniform(low=100, high=120, size=(1000,)) # (2)
>>> lv2 = rng.uniform(low=90, high=110, size=(1000,)) # (3)
>>> lv3 = rng.uniform(low=80, high=100, size=(1000,))
>>> lv4 = rng.uniform(low=70, high=90, size=(1000,))
>>> lv5 = rng.uniform(low=50, high=80, size=(1000,))
>>> ask_size = np.array([lv1, lv2, lv3, lv4, lv5]).T
>>> ask_size.shape
(1000, 5)
>>> ask_price_highest_level = rng.uniform(low=1.1, high=2.0, size=(1000,)) # (4)
>>> DGGW_ask_slope(ask_size, ask_price_highest_level, mid_point)
1136.8718207500128
1. Assume bid-ask midpoint stays at 1
2. Simulated level 1 ask sizes
3. Simulated level 2 ask sizes
4. Simulated ask price at level 5
"""
assert len(ask_size.shape) > 1
n, k = ask_size.shape
assert k > 1 # at least two levels
assert len(mid_point.shape) == 1 and n == mid_point.shape[0]
slope = np.sum(ask_size, axis=1) / (ask_price_highest_level - mid_point)
return np.mean(slope)
DGGW_bid_slope(bid_size, bid_price_highest_level, mid_point)
¶
Bid Slope from Della Vedova, Gao, Grant and Westerholm (working paper)
Parameters:
Name | Type | Description | Default |
---|---|---|---|
bid_size | np.ndarray | (n,k) array of LOB bid sizes, where | required |
bid_price_highest_level | np.ndarray | (n,) array of bid prices at the highest level | required |
mid_point | np.ndarray | (n,) array of bid-ask midpoints. | required |
Returns:
Name | Type | Description |
---|---|---|
float | float | bid slope |
Examples:
>>> from frds.measures.lob_slope import DGGW_bid_slope
>>> import numpy as np
>>> rng = np.random.RandomState(42)
>>> mid_point = np.ones((1000,)) # (1)
>>> lv1 = rng.uniform(low=100, high=120, size=(1000,)) # (2)
>>> lv2 = rng.uniform(low=90, high=110, size=(1000,)) # (3)
>>> lv3 = rng.uniform(low=80, high=100, size=(1000,))
>>> lv4 = rng.uniform(low=70, high=90, size=(1000,))
>>> lv5 = rng.uniform(low=50, high=80, size=(1000,))
>>> bid_size = np.array([lv1, lv2, lv3, lv4, lv5]).T
>>> bid_size.shape
(1000, 5)
>>> bid_price_highest_level = rng.uniform(low=.7, high=.99, size=(1000,)) # (4)
>>> DGGW_bid_slope(bid_size, bid_price_highest_level, mid_point)
5316.539811566988
- Assume bid-ask midpoint stays at 1
- Simulated level 1 bid sizes
- Simulated level 2 bid sizes
- Simulated bid price at level 5
Source code in src/frds/measures/lob_slope.py
def DGGW_bid_slope(
bid_size: np.ndarray, bid_price_highest_level: np.ndarray, mid_point: np.ndarray
) -> float:
"""Bid Slope from Della Vedova, Gao, Grant and Westerholm (working paper)
Args:
bid_size (np.ndarray): (n,k) array of LOB bid sizes, where `n` is number of quotes and `k` is number of levels.
bid_price_highest_level (np.ndarray): (n,) array of bid prices at the highest level `k`.
mid_point (np.ndarray): (n,) array of bid-ask midpoints.
Returns:
float: bid slope
Examples:
>>> from frds.measures.lob_slope import DGGW_bid_slope
>>> import numpy as np
>>> rng = np.random.RandomState(42)
>>> mid_point = np.ones((1000,)) # (1)
>>> lv1 = rng.uniform(low=100, high=120, size=(1000,)) # (2)
>>> lv2 = rng.uniform(low=90, high=110, size=(1000,)) # (3)
>>> lv3 = rng.uniform(low=80, high=100, size=(1000,))
>>> lv4 = rng.uniform(low=70, high=90, size=(1000,))
>>> lv5 = rng.uniform(low=50, high=80, size=(1000,))
>>> bid_size = np.array([lv1, lv2, lv3, lv4, lv5]).T
>>> bid_size.shape
(1000, 5)
>>> bid_price_highest_level = rng.uniform(low=.7, high=.99, size=(1000,)) # (4)
>>> DGGW_bid_slope(bid_size, bid_price_highest_level, mid_point)
5316.539811566988
1. Assume bid-ask midpoint stays at 1
2. Simulated level 1 bid sizes
3. Simulated level 2 bid sizes
4. Simulated bid price at level 5
"""
assert len(bid_size.shape) > 1
n, k = bid_size.shape
assert k > 1 # at least two levels
assert len(mid_point.shape) == 1 and n == mid_point.shape[0]
slope = np.sum(bid_size, axis=1) / np.abs((bid_price_highest_level - mid_point))
return np.mean(slope)
DGGW_scaled_depth_difference(bid_price, ask_price)
¶
SDD from Della Vedova, Gao, Grant and Westerholm (working paper)
Parameters:
Name | Type | Description | Default |
---|---|---|---|
bid_price | np.ndarray | (n,) array of bid prices | required |
ask_price | np.ndarray | (n,) array of ask prices | required |
Note
The bid and ask prices must be at the same LOB level
Returns:
Name | Type | Description |
---|---|---|
float | float | bid slope |
Examples:
>>> from frds.measures.lob_slope import DGGW_scaled_depth_difference
>>> import numpy as np
>>> rng = np.random.RandomState(42)
>>> bid_price = rng.uniform(low=.7, high=.99, size=(1000,)) # (1)
>>> ask_price = bid_price + rng.uniform(low=.01, high=.03, size=(1000,)) # (2)
>>> DGGW_scaled_depth_difference(bid_price, ask_price)
0.01191263921211267
- Simulated bid price at a particular level
- Simulated ask price at the same level
Source code in src/frds/measures/lob_slope.py
def DGGW_scaled_depth_difference(bid_price: np.ndarray, ask_price: np.ndarray) -> float:
"""SDD from Della Vedova, Gao, Grant and Westerholm (working paper)
Args:
bid_price (np.ndarray): (n,) array of bid prices
ask_price (np.ndarray): (n,) array of ask prices
Note:
The bid and ask prices must be at the same LOB level
Returns:
float: bid slope
Examples:
>>> from frds.measures.lob_slope import DGGW_scaled_depth_difference
>>> import numpy as np
>>> rng = np.random.RandomState(42)
>>> bid_price = rng.uniform(low=.7, high=.99, size=(1000,)) # (1)
>>> ask_price = bid_price + rng.uniform(low=.01, high=.03, size=(1000,)) # (2)
>>> DGGW_scaled_depth_difference(bid_price, ask_price)
0.01191263921211267
1. Simulated bid price at a particular level
2. Simulated ask price at the same level
"""
assert bid_price.shape == ask_price.shape
return np.mean((ask_price - bid_price) / (ask_price + bid_price))
TODO¶
- Implementation of all other slope measures.
References¶
- Næs and Skjeltorp (2006), Order Book Characteristics and the Volume–Volatility Relation: Empirical Evidence from a Limit Order Market, Journal of Financial Markets 9(4), 408–32.
- Valenzuela, Zer, Fryzlewicz and Rheinländer (2015), Relative liquidity and future volatility, Journal of Financial Markets, 24, 25–48.
- Ghysels and Nguyen (2019), Price discovery of a speculative asset: Evidence from a bitcoin exchange, Journal of Risk and Financial Management, 12(4), 164.
- Duong and Kalev (2007), Order Book Slope and Price Volatility, SSRN.
See Also¶
-
For example, if we examine 5 levels of LOB, N^B and N^A will be 5. ↩