This seems like a very basic question, but the Internet does not seem to know, so:

If you have a stream of bid, ask, and last trade prices, how do you convert those into periodic open/high/low/close bars? Is there a standard definition?

The main question is whether the summaries incorporate the bid and ask or just last trade prices. The other question is whether the previous close is equal to the next open (talking intraday bars here, excluding the session open and close).

My guess is that bars just use last trade prices and the previous close generally equals the next open, but that seems to drop a lot of information, especially for intraday bars.

• By the way, an unfiltered trade stream contains numerous anomalous trades that occur at prices that are quite far from the current price. These outlier trades dramatically distort the bars. As such, especially if volume data is not needed, for an individual trader's purposes, it is worthwhile to consider creating HLC bars over the best quote stream instead. Aug 19, 2023 at 14:11
• An unfiltered trade stream contains numerous anomalous trades that occur at prices that are quite far from the current price. These outlier trades dramatically distort the bars. As such, it is worthwhile to filter the trades by conditions before using them to create bars. Aug 19, 2023 at 17:25

Bars represent an summarized view of what happened during a given period of time of a single given value.

Therefore, you need to first pick the value you want to summarize: Bid, Ask or Last Trade and use values of only these measures to build your bars.

Pick a time range of traded prices, open is the first value in the range, high is the max of the range, low is the min and close is last value in the range.

I made the next C++ conversion function.

int Convert( Bar *bars, const Tick *tick, int tickCount, int lookback  )
{
#define Normalize(a)    double((int((a)*m_scale+0.5))*m_point)
unsigned int m_end = 0;
int count = 0, i = 0;
Bar *pBars = bars[count++];

// lookback - bar's timeframe in seconds
const Tick &t = tick[i++];
pBars->Time = t.Time / lookback;
pBars->Time *= m_lookback;
pBars->Open = pBars->Close = pBars->High = pBars->Low = Normalize(t.Bid);
pBars->Volume = t.Volume();
m_end = pBars->Time + lookback;
while (i < tickCount)
{
const Tick &t = tick[i++];

if (t.Time < m_end)
{
double price = t.Bid;

if (pBars->Low > price)
pBars->Low = price;
else if (pBars->High < price)
pBars->High = price;

pBars->Close = price;
pBars->Volume += t.Volume();
}
else
{
pBars->Close = Normalize(pBars->Close);
pBars->High = Normalize(pBars->High);
pBars->Low = Normalize(pBars->Low);

pBars = bars[count++]
pBars->Time = t.Time / lookback;
pBars->Time *= m_lookback;
pBars->Open = pBars->Close = pBars->High = pBars->Low = Normalize(t.Bid);
pBars->Volume = t.Volume();
m_end = pBars->Time + lookback;
}
}
return barIdx;
}


My understanding is that most OHLC data is made from Bid ticks

• That's about the source but that doesn't answer the whole question. Jan 6, 2017 at 13:23