GetRTData() and the Live Quote Fields
Every AFL variable you have written so far has been a series: Close has one value per
bar, MA( Close, 50 ) has one value per bar, and a comparison between them produces one
Boolean per bar. GetRTData() does not work like that. It hands you a single number
describing this instant, it has no history whatsoever, and on most installations it hands
you nothing at all.
By the end of this lesson you should be able to name every field the function documents, write the guard that distinguishes “the feed says zero” from “there is no feed”, read a quote for a symbol other than the one on screen, and convert the feed’s own timestamp into something you can compare with a bar timestamp.
One call, one number
Section titled “One call, one number”The reference page gives the signature as GetRTData( "fieldname" ), filed under
Miscellaneous functions, added in AmiBroker 4.60, returning a NUMBER. It retrieves the
last — that is, the most recent — value of a named field reported by the streaming
real-time data source for the currently selected symbol.
The word to underline in that sentence is last. Not the value at each bar. There is exactly one, and it belongs to the moment your formula ran.
An array function and a scalar function on the same five bars
| Bar | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
Close | 10.0 | 11.0 | 12.0 | 11.0 | 13.0 |
MA(Close, 3) | Null | Null | 11.0 | 11.3 | 12.0 |
GetRTData("Bid") | b | b | b | b | b |
Three consequences follow, and they are the reason this function surprises people:
- You cannot backtest against it. A backtest walks history;
GetRTData()has none. If you writeBuy = Close > GetRTData( "Ask" );and run a backtest over five years, the one value read at the moment the backtest started is compared against every bar of 2019. The result is arithmetic, not a simulation. - You cannot recover a past bid. Nothing in AFL exposes yesterday’s spread. If you want a history of quote fields you have to sample them yourself on each refresh and store them, which is what static variables are for.
- Arithmetic with an array silently broadcasts.
Close - GetRTData( "Last" )compiles, runs, and produces an array — every bar’s close minus one number. That is occasionally what you want and frequently a bug.
The complete documented field list
Section titled “The complete documented field list”Field names are strings. The reference page prints exactly these, in this order, with these
descriptions. GetRTDataForeign() reprints the same list verbatim on its own page.
| Field name | Documented meaning |
|---|---|
"Ask" |
current best ask price |
"AskSize " |
current ask size |
"Bid" |
current best bid price |
"BidSize " |
current bid size |
"52WeekHigh" |
52 week high value |
"52WeekHighDate" |
52 week high date (in datenum format) |
"52WeekLow" |
52 week low value |
"52WeekLowDate" |
52 week low date (in datenum format) |
"Change" |
change since yesterdays close |
"Dividend" |
last dividend value |
"DivYield" |
dividend yield |
"EPS" |
earnings per share |
"High" |
current day’s high price |
"Low" |
current day’s low price |
"Open" |
current day’s open price |
"Last" |
last trade price |
"OpenInt" |
current open interest |
"Prev" |
previous day close |
"TotalVolume" |
total today’s volume |
"TradeVolume" |
last trade volume |
"ChangeDate" |
datenum (YYYMMDD) of last data change |
"ChangeTime" |
timenum (HHMMSS) of last data change |
"UpdateDate" |
datenum (YYYMMDD) of last data update |
"UpdateTime" |
timenum (HHMMSS) of last data update |
"Shares" |
total number of shares |
Twenty-five names. Four details about that table are worth having in your head before you start typing.
Two names are printed with a trailing space. The page shows "AskSize " and
"BidSize " with a space inside the closing quote, while every other entry has none. That
is a typesetting artefact of the reference page, not a field whose name contains a space.
The names to write are "AskSize" and "BidSize".
The date fields are described as YYYMMDD. Three Ys. The other date fields on the same
page are described as “in datenum format”, and datenum is a four-digit-year encoding
everywhere else in AFL. This is a typo in the documentation. The point of mentioning it is
not pedantry: when a reference page has visible slips in it, treat the shape of a value
as something to verify empirically rather than as something you have been told.
Field names are matched without regard to case. The list prints "52WeekHigh", and the
page’s own example then calls GetRTData( "52weekhigh" ). Both work. Pick one convention
and keep it — the list’s own capitalisation is the sensible choice.
Some obvious names are not there. There is no "Volume": today’s cumulative figure is
"TotalVolume" and the size of the most recent trade is "TradeVolume". There is no
"Close": the current price is "Last" and yesterday’s is "Prev". And there is nothing
at all for market depth. Level 2 order-book data has no AFL access point, and neither does
the Time and Sales tape — Part 22 covers both windows, and the absence of an AFL route to
them is documented by omission rather than by any statement.
What comes back when nothing is listening
Section titled “What comes back when nothing is listening”This is where formulas go wrong, so read the two notes on the reference page as carefully as they were written.
Note 1: the function is available only in the Professional edition, and calling it from the Standard edition “will give you NULL values for all fields”. Note 2: it “works only if data source uses real time data source (plugin)”.
Between them those sentences cover almost everyone reading this. An end-of-day database, an ASCII import, an AmiQuote database, a Norgate database, a real-time database whose plugin has disconnected, or any database at all under the Standard edition: in every one of those cases there is no streaming source to report a field, and there is nothing for the function to return.
The correct shape is to test for absence explicitly and to keep the absent case absent:
Fragment — not a complete formula
BidValue = GetRTData( "Bid" );
// Wrong: turns "no feed" into a price of zero, and every calculation// downstream now works on a number that was never quoted by anybody.BidValue = Nz( GetRTData( "Bid" ) );
// Right: absence stays absence, and the display says so.if( IsNull( BidValue ) ) BidText = "n/a";else BidText = NumToStr( BidValue, 1.4 );Nz() is a fine function and this course uses it constantly. It is the wrong function here,
because the whole information content of a missing quote is that it is missing.
There is a second trap hiding behind the first. Zero is a legitimate value for several of
these fields — "OpenInt" on an equity, "TradeVolume" before the first print of the day,
"Dividend" on a company that pays none. A guard written as if( BidValue == 0 ) conflates
“the feed told me zero” with “the feed told me nothing”, and those need different handling.
IsNull() is the test that distinguishes them.
Editions, and a documentation inconsistency worth knowing about
Section titled “Editions, and a documentation inconsistency worth knowing about”The edition requirement for these two functions is unambiguous and appears on both function pages: Professional only, Null for every field otherwise. Nothing in this lesson works around that.
The wider claim is less tidy, and you will meet the untidiness. The User’s Guide chapter on getting quotes heads its real-time table “REAL-TIME DATA (Professional Edition only)” — and that same page carries the line “THIS PAGE IS CURRENT AS OF March 2, 2023”, along with entries for data vendors that have since changed hands or shut down. AmiBroker’s own ordering page, meanwhile, describes real-time capability in the Standard edition with a restricted symbol count in the Real-Time Quote window.
Both statements are official. They are not obviously reconcilable, and this course is not going to pretend one of them away.
Reading a symbol you are not looking at
Section titled “Reading a symbol you are not looking at”GetRTDataForeign( "fieldname", "symbol" ) does the same job for a ticker other than the
selected one. It was added in AmiBroker 4.80, takes exactly the same field-name list, and
carries the same two notes about editions and plugins.
Its reference page makes one performance claim explicitly: it is “much faster than
SetForeign/GetRTData combo”. That sentence is the whole design rule for multi-symbol
real-time panels. Switching the evaluation context with SetForeign() in a loop, purely so
that GetRTData() reads a different symbol, is the slow way to build a heat map. Pass the
ticker as an argument instead.
Fragment — not a complete formula
// A three-symbol quote strip, without changing the evaluation context.IndexBid = GetRTDataForeign( "Bid", "^GSPC" );IndexAsk = GetRTDataForeign( "Ask", "^GSPC" );
// The same guard applies to every one of these calls. A foreign symbol that// is not currently streaming is exactly as absent as no feed at all.if( IsNull( IndexBid ) OR IsNull( IndexAsk ) ) StripText = "index quote unavailable";else StripText = "index spread " + NumToStr( IndexAsk - IndexBid, 1.4 );There is a hard practical ceiling on this too. The real-time data chapter states that
AmiBroker will rotate active symbols in the Data Manager when you list more tickers than
your subscription allows, and that this rotation does not apply to the Real-Time Quote
window, which cannot hold more symbols than the subscription limit. A dashboard that calls
GetRTDataForeign() across five hundred tickers on a fifty-symbol subscription is not
reading five hundred live quotes. Part 18 has the documented symbol ceilings by vendor.
The feed’s own clock
Section titled “The feed’s own clock”Four of the fields are timestamps, and they are not in the format the rest of AFL uses.
"ChangeDate" and "UpdateDate" are DATENUMs; "ChangeTime" and "UpdateTime" are
TIMENUMs. Neither encoding is a DateTime, and a DateTime is what DateTime(), Now(5) and
DateTimeDiff() expect.
DateTimeConvert() is the bridge. Format 2 converts a datenum plus an optional timenum into
a DateTime:
Fragment — not a complete formula
// Two encodings in, one DateTime out.FeedStamp = DateTimeConvert( 2, GetRTData( "UpdateDate" ), GetRTData( "UpdateTime" ) );
// DateTime values are a bitset. Only == and != are reliable on them, so elapsed// time comes from DateTimeDiff(), never from subtraction or a > comparison.AgeSeconds = DateTimeDiff( Now( 5 ), FeedStamp );Two warnings come attached to that second line, both documented. Now() reads your PC
clock, which has no relationship to the timestamps in your database unless the database
timeshift happens to align them; Status( "timeshift" ) reports that shift in seconds so
you can see whether the comparison means anything. And Status( "lastrtupdate" ) gives the
DateTime of the last update sent by the plugin, which is often the better reference for
“how stale is my feed” — with the caveat, stated on the Status() page, that it depends on
the plugin sending correct update stamps, that most sources send odd timestamps at
weekends, and that the IQFeed plugin sends them only inside regular trading hours.
Why your formula lags the quote window
Section titled “Why your formula lags the quote window”Note 4 on the reference page is the answer to the most common complaint about this function. The value it returns is the current value at the moment of the call — and your formula is only called when the chart or commentary is re-executed, at the refresh interval set in Preferences. The built-in Real-Time Quote window refreshes far more often: the page puts it at “at least 10 times per second”.
So the number in the quote window and the number in your panel will disagree, routinely, and
neither is wrong. Part 21 separates feed updates from chart refreshes properly; the next
lesson but one shows how to take control of the refresh side with
RequestTimedRefresh().
Finding out what your feed actually supplies
Section titled “Finding out what your feed actually supplies”Note 3 says that availability of data depends on the underlying data source, and directs you to the Real-Time Quote window to see whether a given field is available. That is the right first check, and it is a Part 22 subject. The second check is to ask from AFL, symbol by symbol, and record the answer.
Complete runnable AFL
// rt-field-probe.afl// Part 23 - GetRTData() and the Live Quote Fields//// An Exploration that asks your data source, field by field, which of the// documented GetRTData() fields it actually supplies for each symbol.//// Why it exists: the AFL Function Reference lists the field names but states// that "availability of data depends on underlying data source". It does not// say which vendor supplies which field, because it cannot. The only reliable// answer is empirical, and this is how you get it.//// How to run it:// Send to Analysis, set "Apply to" to a small watch list, set Range to// 1 recent bar, press Explore.//// Assumptions and honest limits:// - GetRTData() is documented as PROFESSIONAL edition only. In the Standard// edition every field is documented to return Null.// - It is also documented to work "only if data source uses real time data// source (plugin)". On an end-of-day, ASCII or AmiQuote database there is// no feed to ask, so every quote column will be empty. That is the correct// output of this formula, not a fault in it.// - Empty is not the same as zero. The documentation promises no numeric// sentinel for the no-feed case, so nothing here converts an absent field// into a number.// - The field names below are copied from the GetRTData() reference page in// the order that page prints them. Two of them are printed on that page// with a stray trailing space inside the quotes ("AskSize " and// "BidSize "); the names used here are the ones without it.
_SECTION_BEGIN("Real-time field probe");
// One row per symbol rather than one row per bar: these are scalar snapshots// of "now", so repeating them down a column of bars would be misleading.Filter = Status( "lastbarinrange" );
// Probes five core fields and counts how many answered. Any one of them// answering is enough to prove a live source is attached; none answering is// the expected result on a database with no real-time plugin.function FieldAnswered( FieldName ){ return IIf( IsNull( GetRTData( FieldName ) ), 0, 1 );}
AnsweredCount = FieldAnswered( "Last" ) + FieldAnswered( "Bid" ) + FieldAnswered( "Ask" ) + FieldAnswered( "Prev" ) + FieldAnswered( "TotalVolume" );
if( AnsweredCount == 0 ) FeedVerdict = "no real-time field answered - offline database, Standard edition, or this vendor supplies none of the probed fields";else FeedVerdict = NumToStr( AnsweredCount, 1.0 ) + " of 5 probed core fields answered";
AddTextColumn( FeedVerdict, "Feed status", 1.0, colorDefault, colorDefault, 380 );
// The complete documented field list, in the order the reference page gives it.// An empty cell means the field returned Null: either no feed, or this vendor// does not supply that field.AddColumn( GetRTData( "Ask" ), "Ask", 1.4 );AddColumn( GetRTData( "AskSize" ), "AskSize", 1.0 );AddColumn( GetRTData( "Bid" ), "Bid", 1.4 );AddColumn( GetRTData( "BidSize" ), "BidSize", 1.0 );AddColumn( GetRTData( "52WeekHigh" ), "52WeekHigh", 1.4 );AddColumn( GetRTData( "52WeekHighDate" ), "52WeekHighDate", 1.0 );AddColumn( GetRTData( "52WeekLow" ), "52WeekLow", 1.4 );AddColumn( GetRTData( "52WeekLowDate" ), "52WeekLowDate", 1.0 );AddColumn( GetRTData( "Change" ), "Change", 1.4 );AddColumn( GetRTData( "Dividend" ), "Dividend", 1.4 );AddColumn( GetRTData( "DivYield" ), "DivYield", 1.4 );AddColumn( GetRTData( "EPS" ), "EPS", 1.4 );AddColumn( GetRTData( "High" ), "High", 1.4 );AddColumn( GetRTData( "Low" ), "Low", 1.4 );AddColumn( GetRTData( "Open" ), "Open", 1.4 );AddColumn( GetRTData( "Last" ), "Last", 1.4 );AddColumn( GetRTData( "OpenInt" ), "OpenInt", 1.0 );AddColumn( GetRTData( "Prev" ), "Prev", 1.4 );AddColumn( GetRTData( "TotalVolume" ), "TotalVolume", 1.0 );AddColumn( GetRTData( "TradeVolume" ), "TradeVolume", 1.0 );AddColumn( GetRTData( "ChangeDate" ), "ChangeDate", 1.0 );AddColumn( GetRTData( "ChangeTime" ), "ChangeTime", 1.0 );AddColumn( GetRTData( "UpdateDate" ), "UpdateDate", 1.0 );AddColumn( GetRTData( "UpdateTime" ), "UpdateTime", 1.0 );AddColumn( GetRTData( "Shares" ), "Shares", 1.0 );
// UpdateDate is a DATENUM and UpdateTime is a TIMENUM. They are two different// encodings, and neither of them is a DateTime, so they have to be converted// before they can be displayed or compared as one value.FeedStamp = DateTimeConvert( 2, GetRTData( "UpdateDate" ), GetRTData( "UpdateTime" ) );AddColumn( FeedStamp, "Feed update stamp", formatDateTime );
// The bar timestamp comes from the database, which is a different clock from// the feed's own update stamp. Showing both makes the difference visible.AddColumn( DateTime(), "Last bar in range", formatDateTime );
_SECTION_END();Send it to the Analysis window, set Apply to to a small watch list, set Range to one recent bar, and press Explore. You get one row per symbol, a verdict column, and one column per documented field.
Doing this without a subscription
Section titled “Doing this without a subscription”Nothing makes GetRTData() return a bid on a database with no streaming plugin. What can be
done — and what this part is actually assessing — is everything around that call.
Run the probe on the database you already have. The empty result is not a failure to work around; it is the contract. Most published real-time AFL fails on exactly this case, and having seen it once you will recognise the failure mode in other people’s code.
Write and test the absence handling now. IsNull() guards, “n/a” display strings, and
derived values that stay absent when their inputs are absent are all testable on an
end-of-day database, because that database produces the absent case on every run. The
project at the end of this part is built that way on purpose.
Use historical intraday bars for the array-shaped work. Session high, session low, session range, cumulative volume and the position of the last price inside the day’s range are all computable from stored bars. Only bid, ask and their sizes genuinely require a feed. Part 18 covers the free and low-cost intraday sources; Part 19 covers building the database.
Use Bar Replay for the time-ordered practice. Replay truncates every symbol at a
playback position, which reproduces the one thing an end-of-day chart cannot: not knowing
what happens next. GetPlaybackDateTime() returns that position, or zero when replay is not
running, so a formula can tell the difference and label itself accordingly. Part 26 is the
full treatment; the project in this part uses it as a display mode.
GetRTData() returns one number, for one field, for the currently selected symbol, at the
moment the formula ran. Twenty-five field names are documented, two of them printed with a
stray trailing space, none of them exposing depth or the tape. The function is
Professional-only and needs a streaming plugin, and where either condition fails the
documented result is Null — not zero, and not any other number you might be tempted to
substitute. GetRTDataForeign() reads another ticker and is documented as much faster than
switching context with SetForeign(). The four timestamp fields arrive as DATENUM and
TIMENUM and need DateTimeConvert() before they can be compared with anything.
The next lesson answers a different question about the same formula: not what the data is, but who is asking for it.
Check your understanding
Sources for this lesson
8 verified · checked 2026-08-31
- 01AmiBroker AFL Function Reference — GetRTDataamibroker.com/guide/afl/getrtdata.html2026-08-31
- 02AmiBroker AFL Function Reference — GetRTDataForeignamibroker.com/guide/afl/getrtdataforeign.html2026-08-31
- 03AmiBroker AFL Function Reference — DateTimeConvertamibroker.com/guide/afl/datetimeconvert.html2026-08-31
- 04AmiBroker AFL Function Reference — DateTimeDiffamibroker.com/guide/afl/datetimediff.html2026-08-31
- 05AmiBroker AFL Function Reference — Statusamibroker.com/guide/afl/status.html2026-08-31
- 06AmiBroker User's Guide — Real-time data sourcesamibroker.com/guide/h_rtsource.html2026-08-31
- 07AmiBroker User's Guide — Real-Time Quote windowamibroker.com/guide/w_rtquote.html2026-08-31
- 08AmiBroker User's Guide — How to get quotes from various markets§ Real-time dataamibroker.com/guide/h_quotes.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.