I would really appreciate if any of you can clarify the following questions. I have been struggling to understand it on my own.

  1. $b_t=\begin{cases}b_{t-1}, & \text{if}\ \Delta p_t = 0 \\ \frac{|\Delta p_t|}{\Delta p_t} ,& \text{if} \Delta p_t \neq 0 \end{cases} $ (1)

where $p_t$ = price associated with tick t

Question 1: My tick data have both bid and ask side by side. Based on this equation, am I supposed to do bid and ask separately? i.e $b_{bid}$ and $b_{ask}$?

  1. $\theta_T = \sum_{t = 1}^{T}b_t$ (2)

T = tick index

$T^* = \underset{T}{Argmin} (|\theta_T| \geq E_0[T]|2P[b_t = 1] - 1)$ (3)

$E_0$ is estimated by the exponentially weighted moving average of T values from previous bars.

$2P[b_t = 1] -1$ is calculated as an exponentially weighted moving average of $b_t$ values from prior bars.

Question 2: T value for equation 2 is derived from equation 3?

Question 3: My interpretation from equation 2 and 3 is that substitute values into T, calculate $\theta_T$ until it is greater than $E_0[T]|2P[b_t = 1] - 1$. Is this interpretation correct?

I really appreciate anyone's input in this matter. Learning these on my own is tough, but with your help, I should be able to achieve it.

  • $\begingroup$ Check this article for a review of tick imbalance bars. You will also find the code to build them! medium.com/@savastamirko/… $\endgroup$
    – Charlie
    Commented Jan 18, 2020 at 9:50

3 Answers 3


Question 1. Actually, the assumption of trade data format is that you have timestamp, size and price (not bid/ask) of trade. Sometimes, trades(ticks) are included to Level 1 data (also called BBO) which assumes bid and ask information. However, bars are constructed on trades, not quotes.

Question 2. Yes, T value is derived from equation 3. The process is described in details below.

The whole process of imbalance bars calculation is not straightforward. First of all, you have to set 3 parameters:

  1. Initial guess for expected number of ticks in imbalance bars (𝐸0[𝑇]). When you start to calculate imbalance bars, you don't have any bars at all, so you don't have any information about expected number of ticks inside of an imbalance bar. That is why you need an initial guess. This parameter doesn't significantly impact further imbalance bars, but still it is needed for the first bar calculation

  2. Number of bars to use for expected number of ticks in bars. When you accumulate imbalance bars you can define 𝐸0[𝑇] as EWMA of number of ticks in previous bars that is why you need to set the window for EWMA calculation.

  3. Number of ticks to use for expected imbalance calculation. Expected tick imbalance (2𝑃[𝑏𝑑=1]βˆ’1) can be found by calculating EWMA from tick imbalances from previous trades/ticks. We need to set the window for EWMA.

The whole algorithm description:

  1. Set parameters
  2. On each trade calculate cumulative sum of tick imbalances (πœƒπ‘‡)
  3. Start accumulating ticks until number of tick imbalances reaches the window used to expected tick imbalance calculation.
  4. When number of tick imbalance reaches the EWMA window size - calculate expected tick imbalance (2𝑃[𝑏𝑑=1]βˆ’1)
  5. Check if |πœƒπ‘‡| β‰₯ 𝐸0[𝑇] * |(2𝑃[𝑏𝑑=1]βˆ’1)| (absolute values on left and right)

  6. If True - it means that we have the first bar. Set πœƒπ‘‡ to zero, set expected number of ticks(𝐸0[𝑇]) to EWMA of array of number of ticks in previous bars. As we have only one bar available you call EWMA with you window setting on array of generated bars, don't worry you can call EWMA with window = 3 to an array with less than 3 elements.

  7. If False continue, until |πœƒπ‘‡|β‰₯𝐸0[𝑇]|2𝑃[𝑏𝑑=1]βˆ’1|

I am contributor to https://github.com/hudson-and-thames/mlfinlab package where you can see the implementation of imbalance bars (dollar, tick, volume) described above

  • 1
    $\begingroup$ "where you can see the implementation"? All I see is class/function stubs and "We have created three premium python libraries..." with a link to purchase them. $\endgroup$ Commented Jan 3, 2022 at 13:34

Question 1: Only tick data works for this method of bar generation, which means you actually need to provide the successful transactions. Bid and ask won't work.

Question 2: T value is generated at each bar and its value will depend on the time it takes to meet the condition in equation (3) given the tick data.

Question 3:* E[T] is calculated using the EWMA of b and the EWMA of T from prior bars. The following code should clarify this procedure:

  • Keep in mind that at the start you need a guess for the EWMA of b and T, and you need to provide a parameter alpha to compute both of those EWMA.
import numpy as np

# Generate tick imbalanced bars id of every tick by computing: get_bars_ids(get_b(ticks))
class Tick_Imbalance():
  def __init__(self, alpha_T, alpha_imbalance, expected_imbalance, expected_ticks):
    self.T = 0
    self.bar_id = 0
    self.imbalance = 0
    self.expected_tick_imbalance = expected_imbalance * expected_ticks

    self.alpha_ticks = alpha_T
    self.alpha_imbalance = alpha_imbalance
    self.EWMA_ticks = expected_ticks
    self.EWMA_imbalance = expected_imbalance

  def __get_EWMA(self, alpha, rt, EWMA_0):
    return alpha * rt + (1 - alpha) * EWMA_0

  def __get_b(self, price_change):
    if price_change == 0: return self.last_b
    self.last_b = abs(price_change)/price_change

    return self.last_b

  def get_b(self, price_ticks: np.ndarray) -> np.ndarray:
    price_change = (price_ticks[1:] - np.roll(price_ticks, 1)[1:]) / np.roll(price_ticks, 1)[1:]
    b = [self.__get_b(x) for x in price_change]

    return np.asarray(b)

  def get_bar_ids(self, b: np.ndarray) -> np.ndarray:
    bars_ids = []    
    for _imbalance in b:
      self.T += 1
      self.imbalance += _imbalance

      self.EWMA_imbalance = self.__get_EWMA(self.alpha_imbalance, _imbalance, self.EWMA_imbalance)


      if abs(self.imbalance) >= self.expected_tick_imbalance:
        self.EWMA_ticks = self.__get_EWMA(self.alpha_ticks, self.T, self.EWMA_ticks)
        self.expected_tick_imbalance = self.EWMA_ticks * abs(self.EWMA_imbalance)

        self.T = 0
        self.imbalance = 0
        self.bar_id += 1
    return np.asarray(bars_ids)

Question 1

As I read it from your formula $b_t = \pm 1$, depending on whether the price has risen or fallen since the last evaluated $b_{t-1}$. If the price is unchanged the indicator rolls over from the previous timestep.

I can see two cases, either you calculate your price with actual trade data, i.e. which trades have executed between the timestep and determine a suitable model for determining $p_t$, or you determine $p_t$ based on the depth of the market, i.e. bid and offer. In either case it seems that $b_t$ is some indicator based on price. It is how you choose to model price that matters. Here is a link on that aspect definition of mid price in literature.

  • $\begingroup$ thanks for answering question 1. My tickdata has both bid and ask for the same timestamp. Does that mean my $b_t$ is the collection of spread or the $b_t$ is separate for bid and ask? $\endgroup$ Commented Mar 8, 2019 at 21:30
  • 2
    $\begingroup$ so to reiterate, $b_t$ is an indicator of your price. Your price, dependent upon on your model, may or may not be related to the bid/ask, you might ignore bid/ask and use last trade, or you might say it is average of bid/ask. This indicator model's description does not appear to suggest you should ever be calculating two indicators by considering bid/ask as two separate prices. At least, that's how I read it from your question. $\endgroup$
    – Attack68
    Commented Mar 8, 2019 at 21:39
  • $\begingroup$ That makes sense! Thanks for clarifying this~ $\endgroup$ Commented Mar 9, 2019 at 2:40

Your Answer

By clicking β€œPost Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.