The problem of the weighted price

Mid price is so noisy so I try to use the weighted price, which is much better. However I want to define a better price called weighted price

My questions are:

1. How can I get a good weighted price

2. How to judge the weighted-price. How can I define an indicator to judge whether the weighted price is good or bad?

• The best approach depends on your objective. What are you trying to use the microprice for? – Jase Apr 27 at 7:50
• There's nothing wrong with being discrete.. You gotta be very careful to concretely distinguish what is actually important in practice vs what is theory/unnecessary niceties for your particular application. – Jase Apr 28 at 1:08
• why did the original question change from micro price to weighted price? – develarist Apr 28 at 14:59
• Please don’t edit your questions in such a way they become about something else. It’s confusing for others and invalidates existing answers and comments. – Bob Jansen May 13 at 10:43

Arriving at a good microprice is one of the main preoccupations in the HFT and short-term quantitative trading industry. No answer here will be competitive with these more sophisticated micropricing models.

However if you want something that gives decent predictions (but won't make money after slippage and fees), use either of these two, or some weighted combination of the two:

P = (best_bid_volume * best_ask_price + best_ask_volume * best_bid_price) / (best_bid_volume + best_ask_volume)

I know you mentioned bid-ask bounce as a significant problem, but for most applications (and certainly for prediction), it isn't. The last traded price is highly suggestive of future deltas. If you actually measure this, you will see. Either of these will be significantly better than midprice.

Note that these predict deltas in the midprice, $$ln(mid_{t+1}/mid_{t})$$, which is often the most directly actionable in terms of market taking and therefore the most relevant for market taking strategies.

• i think you should be more interested in predicting "micro-returns" not micro-price. would the return simply be calculated as log(micro-price$_t$ / micro-price$_{t-1}$) -1 ? – develarist Apr 27 at 11:33
• you said earlier that "I use the mid price to calculate the return". so, once you do get a good micro price, would you calculate its return how i asked? – develarist Apr 27 at 17:34
• @develarist I've updated my answer to briefly address the question of what it is that we're predicting – Jase Apr 28 at 1:07
• @jimmychou123 To get a more predictive microprice than this requires significant R&D. Often the micropricing model will have several indicators that contribute. You won't find these in the academic literature. Join a HFT firm as a quantitative researcher in a delta one trading team if you want to find out. – Jase Apr 28 at 1:11
• @jimmychou123 I can't give more info but you are on the right track. – Jase Apr 28 at 4:38

I found Lee & Mykland (2012, https://econpapers.repec.org/article/eeeeconom/v_3a168_3ay_3a2012_3ai_3a2_3ap_3a396-406.htm) to be a nice application for pre-averaging a noisy price process.

I will try and explain it not too rigorously so please correct me if I'm sloppy.

For the sake of completeness, fix a complete probability space $$(\Omega , \mathcal{F}_t, \mathcal{P})$$ where $$\Omega$$ is the set of events in a financial market, $$\left \{ \mathcal{F}_t:t\in\left [ 0,T \right ] \right \}$$ is the right-continuous information filtration for market participants, and $$\mathcal{P}$$ is a data-generating measure. So much for the theoretical foundation.

Now, assume that our price process is a random variable, e.g. \begin{align}dX_{t} = \sigma dW_{t}\end{align}

We have $$X_t\in \mathbb {R}$$ as our unobserved, true log price, $$\sigma\in \mathbb {R}^+$$ the volatility, and $$W_t\in \mathbb {R}$$ some $$\mathcal{F}_t$$-adapted Brownian Motion. This equation simply tells us that our observed price is moving randomly up and down within some corridor. In the original paper, this equation also includes a jump component, but since OP did not specifically ask about this, I omitted it.

Now, denote the observed, noisy price with noise component $$\epsilon_t \in \mathbb {R}$$ as \begin{align}P_{t} = X_{t} + \epsilon_t\end{align}

I.e., the true price is blurred by market microstructure noise, and we can't observe the true price. When dealing with tick data, all of our observations in the recorded time horizon $$[0,T]$$ lie in the grid $$\mathcal{G}_n$$: \begin{align}\mathcal{G}_n = \left \{0 = t_{n,0} < t_{n,1} < \cdots < t_{n,n} = T \right \} \end{align}

This may seem a little extensive, but it allows us to say that we observe trades whenever they occur, not only in regular periods like 1 second, 5 seconds, etc.

To approximate the true price, we need to make some assumptions about the noise process. In this case we assume that noise is stationary and with mean zero. We also assume that noise is $$(k-1)$$-dependent. In other words, only every $$k$$th observation is independent. Therefore, we subsample our observations on a reduced grid $$\mathcal{G}_n^{k}$$: \begin{align} \mathcal{G}_n^{k} = \left \{ t_n < t_{n,k} < t_{n,2k} < \cdots \right \} \end{align}

Denote the prices that were subsampled according to this grid as $$\tilde{P}(t_{ik})$$. To denoise this price, we take averages of $$\tilde{P}(t_{ik})$$ over some blocks of size $$M \in N^+$$. This part is a bit tricky, the authors of the paper gave data-driven recommendations based on simulations, so I will skip this part, but will include an example. The preprocessed price with (hopefully less microstructure noise) is \begin{align} \hat{P}(t_j) = \frac{1}{M}\sum_{i = \left \lfloor j/k\right \rfloor}^{\left \lfloor j/k\right \rfloor + M-1}\tilde{P}(t_{ik})\end{align}

Since this is all very theoretical, here's an example in R:

## install and load packages ##
libraries = c("data.table") # needed for shift function
lapply(libraries, function(x) if (!(x %in% installed.packages())) {install.packages(x)} )
invisible(lapply(libraries, library, quietly = TRUE, character.only = TRUE))
## ##

## Get data ##
set.seed(234)
P <- rnorm(100000, 100, 3)
P_tilde <- log(P)
## ##

## Determine k ##
# acf #
bacf <- acf(diff(P_tilde), plot = FALSE) # get ACF data
bacfdf <- with(bacf, data.frame(lag, acf))

# CI # https://stackoverflow.com/questions/42753017/adding-confidence-intervals-to-plotted-acf-in-ggplot2 #
alpha <- 0.90
conf.lims <- c(-1,1)*qnorm((1 + alpha)/2)/sqrt(bacf$$n.used) ## lag_outside_conf.lim <- sort(c(which(bacfdf$$acf < conf.lims[1]), which(bacfdf\$acf > conf.lims[2])))

# Specify k = maximum lag value + 1 #
k <- max(lag_outside_conf.lim) + 1
# note that the choice of k is very important, so if you aren't dealing with a lot of data you may to this visually,
# also, there may be more advanced approaches for handling this #
## ##

# Get n #
n <- length(P_tilde)

# Get n-k #
n_diff <- n - k

# Vector with P_tilde_m+k values
P_tilde_shift <- shift(P_tilde, n = k, type = "lead")

## Calculate block size M ##
# Calculate q hat (this is from the paper, see table 5) #
q_hat <- sqrt(1/(2 * n_diff) * sum((P_tilde[1:n_diff] - P_tilde_shift[1:n_diff])^2)) # *100 for percentage value (but this will cause trouble in further calculations)

# Determine C w.r.t. q_hat #
C <- NaN
if (q_hat * 100 > 1) {C <- 1/8; print("Q_Hat is larger than 1. C defaulted to 1/8. Choose better value.")}
if (q_hat * 100 <= 1) C <- 1/8
if (q_hat * 100 <= (0.9 + 0.8)/2) C <- 1/9
if (q_hat * 100 <= (0.3 + 0.4)/2) C <- 1/16
if (q_hat * 100 <= (0.1 + 0.2)/2) C <- 1/18
if (q_hat * 100 <= (0.05 + 0.07)/2) C <- 1/19
if (q_hat * 100 < 0.01) print("Q_Hat is smaller than 0.01. C defaulted to 1/19. Choose better value.")

# Calculate M #
if (floor(C*sqrt(n/k)) == 0) {M <- 1
} else  {
M <- floor(C*sqrt(n/k))}

# if testing different values for C #
C_vector <- c(1/8, 1/9, 1/16, 1/18, 1/19)
M_vector <- floor(C_vector*sqrt(n/k))
M_vector <- unique(M_vector)

# Calculate P_hat_tj (first iteration) #
G_n_k <- seq(from = 0, to = n, by = k) # Grid for first subsampling
G_n_k[1] <- 1
P_tilde_t_ik <- P_tilde[G_n_k]
P_hat_tj <- array(dim = (length(G_n_k) - 1)) # preallocate

# Calculate P_hat_tj (second iteration) #
for (i in G_n_k) P_hat_tj[[i]] <- mean(P_tilde_t_ik[(floor(i/k)):(floor(i/k)+M-1)])
G_n_kM <- seq(from = 0, to = n, by = k*M) # Grid for second subsampling
G_n_kM[1] <- 1
P_hat_tj <- P_hat_tj[G_n_kM] # keep only those times t_j where they lie in the grid G_n_kM


I hope this kind of advertisement is allowed, but I implemented the full methodology of this paper here: https://github.com/QuantLet/SFM_Class_2019WS/tree/master/SFM_JumpsInCryptocurrencies/JumpDetectR

For fixed income trading I acquired a single days worth of iSwap's bid-ask swap data, which is high quality constantly streamed prices of discrete book sizes.

I derived the three standard prices as measures for the mid: