Time & Sales: What It Does and Does Not Tell You
Of everything AmiBroker can put on a screen, the Time & Sales window is the display that looks most like unmediated fact. Row after row of prices and sizes, arriving in real time, each one a trade that actually happened. It is easy to believe you are watching the market itself rather than a representation of it.
You are watching a representation. A very good one — but one that has been stripped of the single piece of information that almost every popular interpretation of it requires. This lesson is about which claims the tape supports, which it does not, and how to tell the difference without either believing everything or dismissing the window as useless. It is not useless. It is just far narrower than its reputation.
What a print contains, and what it does not
Section titled “What a print contains, and what it does not”Part 2 established the raw material: every trade carries a timestamp, a price, a size, and usually an exchange identifier and condition codes describing what kind of trade it was. AmiBroker’s database format has supported timestamps down to one microsecond since version 5.30, so granularity is not the limitation here.
Here is the limitation. A public trade print says that a quantity changed hands at a price at a time. It does not say who wanted it more.
Every trade has a buyer and a seller. Both of them traded. Neither of them is, in any factual sense, “the volume” — and yet almost every claim you will read about reading the tape depends on assigning each print to one side or the other. That assignment is called the aggressor inference, and understanding that it is an inference rather than a field is the single most valuable thing on this page.
The Time & Sales window in AmiBroker
Section titled “The Time & Sales window in AmiBroker”Open it from the Real-Time Quote window’s right-click context menu, where “Time & Sales” is the first item. The documentation describes what it shows plainly: information about every bid, ask and trade streaming from the market, one row per event, where each row is either a new trade, a new bid or a new ask sent by the streaming data source.
That is worth reading twice. The window is not a trade tape. It is a merged stream of three kinds of event, and quote updates typically outnumber trades substantially in an active instrument.
The colour rules
Section titled “The colour rules”Rows and values are coloured to encode conditions. Colour is a poor sole carrier of meaning, so here they are as text.
| Row background | Meaning |
|---|---|
| Light green | The row is a new ask |
| Light red | The row is a new bid |
| Normal (white) | The row is a new trade |
| Text colour on a bid/ask price or size | Meaning |
|---|---|
| Red | The value is lower than the previous value of the same category |
| Green | The value is higher than the previous value of the same category |
| Black | The value is unchanged |
| Text colour on a last trade price | Meaning |
|---|---|
| Red | The trade occurred on or below the current bid |
| Green | The trade occurred on or above the current ask |
| Black | The trade occurred inside the bid-ask range |
The third table is the aggressor inference, implemented in the user interface. AmiBroker is not asserting that a green print was a buy; it is telling you where the print sat relative to the quote it holds. What you do with that is your problem, and the rest of this lesson is about doing it carefully.
Version 5.70 added user-definable filtering, user-definable colours and two selectable display modes, so the exact appearance on your screen may differ from a colleague’s while the underlying rules stay the same.
The recent statistics
Section titled “The recent statistics”Version 5.30 added a statistics block. The documented figures are the number of trades and the average number of trades per second; the number of trades and shares traded at the ask or above; the number of trades and shares traded at the bid or below; the ask-minus-bid difference expressed both in trades and in shares; and that difference as a percentage ratio of total trades and of total volume traded. A right-click menu item, “Reset Stats”, clears them.
The documentation marks one point as IMPORTANT, and it is the most consequential sentence on the whole page: these are temporary, short-term statistics covering only the trades displayed in that window since it was opened or since the statistics were last reset.
They are not a session total. They are not a database query. Two people watching the same symbol at the same instant will see different numbers if they opened their windows at different times. Any conclusion you draw from them is partly a conclusion about when you happened to press a button.
The aggressor inference problem
Section titled “The aggressor inference problem”The standard way to assign a print to a side is to compare its price with the quote prevailing at the time: a trade at or above the ask is treated as buyer-initiated, a trade at or below the bid as seller-initiated, and a trade strictly inside the spread as ambiguous. AmiBroker’s colour rules and its “at the ask or above” / “at the bid or below” statistics implement precisely this.
It is a reasonable rule. It is also a rule, not a measurement, and it fails in ways that are structural rather than accidental.
A synthetic tape, classified
Section titled “A synthetic tape, classified”The prints below are invented for this lesson. They are illustrative and synthetic — no real instrument, no real session, no real vendor. Their only purpose is to let you apply the rule and see what happens.
Take the prevailing quote as bid 24.50 for 400, ask 24.52 for 200.
| # | Time | Price | Size | Rule says | Actually |
|---|---|---|---|---|---|
| 1 | 10:31:02.114 | 24.52 | 200 | Buyer-initiated | Consistent with the rule |
| 2 | 10:31:02.118 | 24.52 | 100 | Buyer-initiated | The ask was just exhausted; is your quote current? |
| 3 | 10:31:02.140 | 24.51 | 50 | Ambiguous | Inside the spread — no classification available |
| 4 | 10:31:02.301 | 24.50 | 400 | Seller-initiated | Consistent with the rule |
| 5 | 10:31:03.002 | 24.49 | 1,200 | Seller-initiated | Below the bid: aggressive seller, or a late-reported trade |
| 6 | 10:31:03.870 | 24.52 | 17 | Buyer-initiated | An odd lot; contributes 17 shares to a “pressure” figure |
| 7 | 10:31:05.410 | 24.505 | 900 | Ambiguous | A midpoint execution; unclassifiable by construction |
| 8 | 10:31:06.900 | 24.44 | 5,000 | Seller-initiated | Far outside the quote: a block reported late, or a bad tick |
Now compute the statistics the window would report. At the ask or above: three trades, 317 shares. At the bid or below: three trades, 6,600 shares. Total: eight trades, 7,867 shares.
The ask-minus-bid difference is therefore zero trades and minus 6,283 shares — nil per cent by trade count, and about minus eighty per cent by volume.
The same eight rows, in the same window, at the same moment, support “perfectly balanced” and “overwhelmingly one-sided” simultaneously, depending on which of the two documented statistics you quote. And the volume figure is driven almost entirely by row 8, which on this evidence is as likely to be a bad tick or a late block report as an act of aggression. Two of the eight prints cannot be classified at all.
What AmiBroker’s own documentation says about it
Section titled “What AmiBroker’s own documentation says about it”This course is not the first to notice. The Time & Sales page volunteers the qualification itself, immediately after describing the statistics: positive ask-minus-bid values, it says, may in theory mean more buying than selling, but in practice things are largely dependent on the security traded. It then names a specific mechanism — dark liquidity pools do not show in order books and may report trades to the tape several seconds later, which invalidates the relationship between bid, ask and actual trade price.
That is a vendor documenting a limitation of its own feature, in the manual, unprompted. It deserves to be quoted more often than it is.
The consequence is worth spelling out. If a print can arrive several seconds after the trade occurred, then comparing its price to the quote now compares it to a quote that did not exist when it was agreed. Rows 5 and 8 of the synthetic table are exactly this case, and nothing in the data marks them as such unless the condition codes reach you and you can see them. AmiBroker’s documentation does not describe a condition-code column in this window, so do not assume you can.
Why this is structural
Section titled “Why this is structural”Where the aggressor information goes
- Two orders matchOne party crossed the spread; both parties know which
- The venue reports the tradePrice, size, time, condition codes — the initiating side is not a public field on the tape
- Consolidation and redistributionPrints from several venues merge; some arrive late by their own rules
- Your vendor and pluginNormalised, possibly filtered, delivered over one connection
- You compare price to quoteA reconstruction of information that was discarded three steps earlier
Note what the diagram is not saying. It is not saying the data is bad, or that vendors are hiding something. It is saying that the identity of the initiating side is not part of what a public tape publishes, so anyone who wants it must reconstruct it — and a reconstruction is only as good as the assumptions behind it and the alignment of the two streams it compares.
Where a venue does publish an aggressor flag directly, this problem largely disappears. But then you are reading a field rather than inferring one, and you are no longer working from this window.
Why “order flow” claims usually overreach
Section titled “Why “order flow” claims usually overreach”Not because the people making them are dishonest. Mostly because four steps get skipped.
The classification is presented as an observation. “Two million shares were bought” is a statement about a rule you applied, not about the market. The moment it is written down without the rule attached, it acquires a certainty it never had.
Displayed size is treated as available size. The previous lesson made this point about
BidSize and AskSize, and it applies with equal force here: reserve and iceberg orders
show a fraction of their quantity, and quantity that never rests in a public book leaves no
trace at all.
Aggression is treated as conviction. A patient institutional seller working a large order can spend hours resting offers at the ask. Every single print that lifts those offers classifies as buying. The tape, read naively, reports enthusiastic accumulation during a distribution.
The measurement window is not stated. Because the statistics reset when you open the window, “the tape has been heavily one-sided” is incomplete until you say since when. This is the same discipline the course applies to every backtest figure: a number without its universe, period and assumptions is not evidence.
What the tape is genuinely for
Section titled “What the tape is genuinely for”Being sceptical about the popular use does not make the window useless, and it would be lazy to leave it there. Here is what it is actually the right instrument for — five uses, all of them non-predictive, all of them things no other window in AmiBroker gives you.
Confirming that a feed is alive at the finest available granularity. Charts refresh on a timer and a bar can look plausible while nothing is arriving. The Time & Sales window shows events as they land. When you are diagnosing the frozen chart from Part 21, this is the fastest way to establish whether the problem is upstream of AmiBroker.
Calibrating what “liquid” means for your intended size. Watching typical print sizes for a few minutes tells you something a daily volume figure cannot: whether a thousand shares is unremarkable here or would be the largest print of the hour. That is a direct input to the position-sizing constraints in Part 34 and to whether a backtest on this instrument is answerable at all.
Sanity-checking your cost assumptions. How wide is the quote at the times of day your system would trade? How often does it widen? A backtest that assumes a one-tick spread on an instrument whose quote is routinely three ticks wide at the open is not modelling your market, and this window is where you find that out before Part 28 rather than after.
Reviewing your own executions. Comparing your fill price and time against the prints around it is post-trade analysis, and it is the one use here where the aggressor question is settled — you know which side you were on. This is how you learn what your orders actually cost, as opposed to what you assumed.
Spotting data defects at source. A print far outside the quote, visible as it happens, is exactly the bad tick that will show up tomorrow as an inexplicable spike in your bar high. AmiBroker’s own vendor directory annotates IQFeed as an unfiltered feed for this reason — nothing is suppressed on the way through — and Force backfill from the plugin context menu is the documented remedy once the vendor has cleaned the data.
Notice the common shape. Every one of these uses the tape to learn about your data, your costs and your executions. None of them uses it to forecast a price. That is not a coincidence; it is the boundary this course draws, and it is drawn where the evidence stops.
Demonstrating classification sensitivity on data you already have
Section titled “Demonstrating classification sensitivity on data you already have”Goal. To turn the argument above into a number you can look at. If a classification rule really does most of the work in an “order flow” figure, then changing only the tie-break — the treatment of the least informative cases — should change the answer substantially. This formula scores the same bars under three tie-break rules that are all equally defensible and shows how far apart they end up.
It applies the crudest form of the aggressor inference: classify a bar’s entire volume by whether its close rose or fell. That is deliberately cruder than the tape-level rule. The argument is a fortiori: if the coarse version is this unstable under an arbitrary convention, a finer-grained version built on the same kind of assumption inherits the same fragility with better presentation.
Complete formula.
Complete runnable AFL
// Tick-rule volume imbalance// Part 22 - "Time & Sales: What It Does and Does Not Tell You"//// What this is FOR: measuring how much of a published "buying versus selling// pressure" figure is produced by the classification rule rather than by the// market. It applies the crudest form of the aggressor inference - classify a// bar's whole volume by whether the close rose or fell - and then shows the same// bars scored three ways, under three tie-break rules that are all equally// defensible.//// What this is NOT: a signal, an indicator to trade, or a measurement of order// flow. If you find yourself looking for a level at which the line means "buy",// re-read the lesson.//// ASSUMPTIONS:// 1. Runs on any interval and any database. No real-time feed, no plugin and// no Professional edition are required.// 2. Bar-level classification is cruder than trade-level classification, and// deliberately so. The argument in the lesson is that if the crude version// is this unstable under an arbitrary tie-break, the finer-grained version// inherits the same instability with better presentation.// 3. Volume is taken at face value. Where your data source reports composite// or corrected volume, that is what is being classified (Part 2).
_SECTION_BEGIN( "Tick-rule volume imbalance" );
TieRule = ParamList( "Unchanged bars are", "Left unclassified|Counted as buying|Given the previous direction", 0 );
PriceChange = Close - Ref( Close, -1 );Direction = Sign( PriceChange ); // +1 up, -1 down, 0 unchanged
// The last direction that was not a tie, carried forward across the ties.CarriedDirection = ValueWhen( Direction != 0, Direction );
// The three rules, all computed, so the panel can compare them.RuleUnclassified = Direction;RuleCountedAsBuy = IIf( Direction == 0, 1, Direction );RuleCarryForward = IIf( Direction == 0, CarriedDirection, Direction );
if( TieRule == "Counted as buying" ) ActiveRule = RuleCountedAsBuy;else{ if( TieRule == "Given the previous direction" ) ActiveRule = RuleCarryForward; else ActiveRule = RuleUnclassified;}
// Signed cumulative volume under one classification rule.function CumulativeImbalance( Classification ){ BuySide = IIf( Classification > 0, Volume, 0 ); SellSide = IIf( Classification < 0, Volume, 0 );
return Cum( BuySide ) - Cum( SellSide );}
ActiveImbalance = CumulativeImbalance( ActiveRule );
FinalUnclassified = LastValue( CumulativeImbalance( RuleUnclassified ) );FinalCountedAsBuy = LastValue( CumulativeImbalance( RuleCountedAsBuy ) );FinalCarryForward = LastValue( CumulativeImbalance( RuleCarryForward ) );
// How much volume the strict rule refuses to attribute to anybody.TotalVolume = LastValue( Cum( Volume ) );StrictClassed = LastValue( Cum( IIf( Direction != 0, Volume, 0 ) ) );
if( TotalVolume > 0 ) UnattributedPct = 100 * ( TotalVolume - StrictClassed ) / TotalVolume;else UnattributedPct = 0;
// The spread between the most and least generous rule, as a share of all volume.HighestFinal = Max( FinalUnclassified, Max( FinalCountedAsBuy, FinalCarryForward ) );LowestFinal = Min( FinalUnclassified, Min( FinalCountedAsBuy, FinalCarryForward ) );
if( TotalVolume > 0 ) RuleSpreadPct = 100 * ( HighestFinal - LowestFinal ) / TotalVolume;else RuleSpreadPct = 0;
Plot( ActiveImbalance, "Cumulative classified imbalance", colorBlue, styleLine | styleThick );Plot( 0, "", colorDarkGrey, styleLine | styleNoLabel );
Title = "Tick-rule volume imbalance - " + Name() + " (" + Interval( 2 ) + " bars)\n" + "This is a demonstration of classification sensitivity, not a trading signal.\n" + "\n" + "Active rule: unchanged bars " + TieRule + "\n" + "\n" + "Same bars, final imbalance under each rule:\n" + " left unclassified : " + NumToStr( FinalUnclassified, 1.0 ) + "\n" + " counted as buying : " + NumToStr( FinalCountedAsBuy, 1.0 ) + "\n" + " given previous direction : " + NumToStr( FinalCarryForward, 1.0 ) + "\n" + "\n" + "Volume the strict rule attributes to nobody: " + NumToStr( UnattributedPct, 1.2 ) + "%\n" + "Disagreement between the rules, as a share of all volume: " + NumToStr( RuleSpreadPct, 1.2 ) + "%";
_SECTION_END();How it works. Sign( Close - Ref( Close, -1 ) ) gives plus one, minus one or zero per
bar. The zeros are the tie cases, and the formula resolves them three ways: leave them
unclassified, count them as buying — which is what you get from the very common lazy
comparison Close >= Ref( Close, -1 ) — or carry the previous non-zero direction forward
with ValueWhen(). Each rule produces a signed cumulative volume series, and the title
panel prints all three final values side by side along with the share of total volume the
strict rule refuses to attribute to anybody.
Key functions. Sign() reduces a change to its direction. ValueWhen( Direction != 0, Direction ) carries the most recent decisive direction across the ties, which is the
standard tick-rule convention. ParamList() puts the tie-break choice in the Parameters
dialog so you can switch it while watching the chart, and Cum() accumulates the signed
volume.
Expected result. A cumulative line that wanders away from zero, and a title panel whose three final figures differ. How much they differ depends entirely on the instrument: in a liquid share on daily bars, unchanged closes are rare and the three rules will be close together. On a thinly traded instrument, or on fine intraday bars where unchanged closes are common, the gap can be a large fraction of total volume — and the “volume attributed to nobody” figure tells you why.
Test it. Apply it to the most thinly traded symbol in your database and then to the most liquid, and compare the two “disagreement between the rules” percentages. Then switch the chart to a much shorter interval on the liquid symbol and watch that percentage rise as unchanged closes become common. If the figures do not move at all between those cases, check that your data actually contains unchanged closes — some sources round in a way that makes exact ties rare.
Common errors. Reading the plotted line as a signal. Comparing the absolute imbalance across symbols, which compares volumes rather than behaviour. Concluding that because the three rules agree on one liquid daily chart, the classification problem is not real — the lesson is about the conditions under which it bites, and fine intervals are where you will actually be doing this kind of work.
Extension. Add a fourth rule that classifies each bar by where the close sits within the bar’s own range, rather than against the previous close. It is another perfectly defensible convention, and adding it to the panel is the fastest way to convince yourself that the family of defensible conventions is larger than you assumed.
The Level A path
Section titled “The Level A path”The tape itself cannot be reproduced without a streaming feed, and this is one of the few places in the course where that is simply true. What can be learned without one is everything that matters about how to treat it.
Do the classification exercise by hand. Work through the synthetic table above with a pencil before reading the totals. Assign each print a side, note where you had to guess, and then compare your totals with the ones in the text. The experience of finding two rows unclassifiable and one row dominating the volume figure is the argument, and it costs nothing.
Run the imbalance formula on any database you have. It needs no plugin, no Professional edition and no subscription. The instability it demonstrates is the same instability that sits underneath every tape-derived statistic.
Use Bar Replay for the sequencing discipline. What tape reading genuinely trains — and this is a fair point in its favour — is the habit of forming a view with no knowledge of what comes next. Bar Replay gives you that directly: it plays back all symbols at once, hiding everything past the playback position from every part of AmiBroker, at a speed between 0.1 and 5 steps per second, with options to skip after-hours and weekends. Set the step interval to your database’s base interval, step forward one bar at a time, and write down what you would do before you press the button. That is the transferable half of tape reading, available on end-of-day data.
Read your own broker’s execution reports. If you trade at all, your fills are the one part of the tape where you know the aggressor with certainty. Comparing your executions against the quotes at the time is more instructive than any amount of watching other people’s prints, and your broker gives it to you.
The Time & Sales window shows every bid, ask and trade the feed sends, colour-coded by where each print sat relative to the quote, with a short-term statistics block that is scoped to the window rather than to the session. Standard edition allows one such window; Professional allows as many as you like. No AFL function reaches this stream.
The reason most tape claims overreach is not bad faith but a missing field: public prints do not carry the initiating side, so every “buying versus selling” figure is a reconstruction. AmiBroker’s own documentation says so, names late-reported off-book trades as a specific mechanism that breaks the reconstruction, and warns that the statistics depend heavily on the instrument.
The window remains the right tool for judging liquidity against your intended size, calibrating cost assumptions, reviewing your own fills, spotting bad ticks as they happen and proving a feed is alive. Those are all questions about your data and your trading rather than about the market’s intentions, and they are the questions the data can answer.
Part 23 takes the fields from the previous lesson into AFL properly: GetRTData(),
execution context, controlled refresh, and a dashboard that behaves correctly when the feed
is not there.
Check your understanding
Sources for this lesson
8 verified · checked 2026-08-31
- 01AmiBroker User's Guide — Time & Sales windowamibroker.com/guide/w_timesales.html2026-08-31
- 02AmiBroker User's Guide — Real-time quote windowamibroker.com/guide/w_rtquote.html2026-08-31
- 03AmiBroker User's Guide — About AmiBroker Editionsamibroker.com/guide/versions.html2026-08-31
- 04AmiBroker User's Guide — What's new§ Highlights of versions 5.30 and 5.70amibroker.com/guide/whatsnew.html2026-08-31
- 05AmiBroker User's Guide — How to work with Real-Time data pluginsamibroker.com/guide/h_rtsource.html2026-08-31
- 06AmiBroker AFL Function Reference — GetRTDataamibroker.com/guide/afl/getrtdata.html2026-08-31
- 07AmiBroker User's Guide — How to get quotes from various markets§ Page is self-dated 2 March 2023amibroker.com/guide/h_quotes.html2026-08-31
- 08AmiBroker User's Guide — Bar Replay windowamibroker.com/guide/w_barreplay.html2026-08-31
Every technical claim on this page was checked against the official AmiBroker documentation on the date shown. Where the course disagrees with folklore, the source is how you can tell which one to trust.