Chart Titles and Dynamic Text
The strip of text at the top of a chart pane is the highest-value real estate in AmiBroker. It is always visible, it updates as you move the selection line, and it is the only place a formula can put a number that is too precise to read off an axis. Most people leave it as AmiBroker generated it. That is a waste.
By the end of this lesson you will be able to replace it with a title that reports exactly the numbers your analysis depends on, colour-coded, at the bar you are pointing at, without it turning into an unreadable wall of text on a laptop screen.
Title is a variable, not a function
Section titled “Title is a variable, not a function”There is no Title() function in AFL. Title is a reserved variable: you assign a string
to it and AmiBroker draws that string instead of the automatically generated one.
Fragment — not a complete formula
Title = "Hello from the title bar";The word “instead” matters. As soon as you assign Title, the automatic
“plot name: value, plot name: value” listing disappears. Any plot value you still want has to
be put back by hand — which is exactly why styleNoTitle exists, so you can suppress the
automatic entries on plots you have already covered yourself.
Because Title is a reserved variable rather than a function, it behaves like any other
assignment. You can build it in stages, branch on a parameter, or assemble it from several
named pieces, and most readable title code does exactly that rather than writing one enormous
concatenation.
Wrap the assignment in _N()
Section titled “Wrap the assignment in _N()”AmiBroker’s own example formulas write the assignment like this:
Fragment — not a complete formula
_N( Title = "Symbol: " + Name() );_N() means “no text output”. In a Commentary or Interpretation context, a bare string
expression at global level gets printed to the output window; _N() stops the title text
being dumped there as a side effect. It costs four characters and prevents a confusing
symptom, so make it a habit.
Getting numbers into a string
Section titled “Getting numbers into a string”Three functions do the conversion, and they are not interchangeable.
NumToStr() — the modern one
Section titled “NumToStr() — the modern one”Fragment — not a complete formula
NumToStr( NUMBER, format = 1.3, separator = True, roundAndPad = False )The format argument is a single number that encodes two things. Its integer part is the
minimum field width, space-padded; its fractional part is the number of decimal places. So
1.2 gives two decimals with no padding, 1.0 gives no decimals at all, and 8.4 gives four
decimals right-aligned in an eight-character field — which is how you get columns of numbers
to line up.
separator controls the thousands separator, on by default and configurable under
Tools → Preferences → Misc. There is also the special format constant formatDateTime,
which converts the value returned by DateTime() using the user’s Windows regional settings,
and formatDateTimeISO for the international YYYY-MM-DD HH:MM:SS form.
WriteVal() — the old name for the same thing
Section titled “WriteVal() — the old name for the same thing”WriteVal() has the identical signature and the identical behaviour. The official page says
plainly that it is obsolete and that NumToStr() is preferred in new code; the name survives
only because people migrating from MetaStock expected it. You will meet it constantly in
formulas found online, so recognise it — and do not write it.
Both functions share a behaviour that surprises people: given an array, they return one value, not a series of them. In an indicator that value is the selected one, the bar under the vertical selection line. Everywhere else it is effectively the last value.
WriteIf() — a conditional string
Section titled “WriteIf() — a conditional string”Fragment — not a complete formula
WriteIf( EXPRESSION, "TRUE TEXT", "FALSE TEXT" )WriteIf() does not write anything either. It returns one of two strings depending on the
selected value of the condition. Think of it as IIf() for text. It nests, so a
three-way classification reads reasonably well:
Fragment — not a complete formula
Word = WriteIf( Regime == 1, "UP", WriteIf( Regime == -1, "DOWN", "RANGE" ) );StrFormat() — when you have several numbers
Section titled “StrFormat() — when you have several numbers”StrFormat( formatstr, ... ) is printf that returns a string instead of printing it, and it
is by far the tidiest way to assemble a line with several values in it. The rules that matter:
- Use
%f,%eor%gfor numbers.%dand%xdo not work, because AFL has no integer type.%gis the usual choice: it drops trailing zeros. %sfor strings, supported from AmiBroker 6.20 onwards.- A literal percent sign is
%%. Since version 6.10 AmiBroker checks that the number of specifiers matches the number of arguments and reports “Error 61. The number of % formatting specifier(s) does not match the number of arguments passed” when it does not — which is almost always a single%that should have been%%. - Given an array,
StrFormat()uses its selected value, exactly likeNumToStr().
Which bar are you reporting?
Section titled “Which bar are you reporting?”A title is a single line of text, but every array in your formula has one value per bar. Some
function has to choose, and the choice is SelectedValue().
Fragment — not a complete formula
Reading = SelectedValue( RSI( 14 ) );SelectedValue() returns a number, which is why it is safe inside if() where an array
condition would not be. Its meaning, however, depends on where the formula is running:
- In an Indicator, Commentary or Interpretation, it is the bar marked by the chart’s vertical selection line — so the title follows your mouse.
- In an Analysis window there is no selection line, so it means the last bar of the selected analysis range. With the range set to “all quotes” that is the final bar of the data.
Its siblings are LastValue() (always the final bar), BeginValue() and EndValue() (the
ends of a From–To range). For a chart title you want SelectedValue(); for a fixed summary
line you want LastValue().
Colour and line breaks
Section titled “Colour and line breaks”EncodeColor( colorIndex ) returns a string — the escape sequence that switches text
colour from that point in the title onwards. It is concatenated with +; it is never passed
to Plot() as a colour.
Fragment — not a complete formula
Line = EncodeColor( colorGreen ) + "up" + EncodeColor( colorDefault ) + " today";The colour applies to the rest of the string until the next EncodeColor(). The underlying
escape is \cXX with a two-digit colour index, and \c-1 resets to the default axis colour,
but the official guide describes writing those by hand as hard to read and recommends
EncodeColor() instead. Take the advice.
\n embeds a line break. A long title is clipped rather than wrapped unless you turn
wrapping on:
Fragment — not a complete formula
SetChartOptions( 2, chartWrapTitle );Mode 2 means “set this flag on the existing chart”. Mode 0 only applies defaults the first time a chart is inserted into a pane, which is why editing a mode-0 call in a chart you already have open appears to do nothing at all.
Keeping it readable
Section titled “Keeping it readable”A title is read at a glance while you are looking at something else. Three habits help.
Ration the fields. Three or four numbers per line is the limit before the eye stops parsing and starts scanning. If you find yourself wanting eight, you want a table, and Part 12 will give you Explorations for that.
Give the reader a switch. A ParamToggle that collapses the title to one short line is
two lines of code, and it is the difference between a formula that works on a 13-inch laptop
and one that does not. Titles do not reflow: on a narrow pane a three-line title eats the
chart.
Put the units in the text, not in the reader’s head. “ATR(14) 1.83% of price” is unambiguous; “ATR 1.83” is not, because the reader cannot tell whether that is points, percent, or something you normalised earlier.
Putting it together
Section titled “Putting it together”What we are building
Section titled “What we are building”A replacement title bar that reports, for the bar under the selection line: the symbol, the bar interval, the date and time, the four price fields, the percentage change from the previous close in green or red, average true range as a percentage of price, and whether the close sits above or below a long moving average. Plus a compact mode for small screens.
The formula
Section titled “The formula”Complete runnable AFL
/* * An informative chart title - worked example for Part 10. * * Assumptions * - Chart pane only. Title is an Indicator-context reserved variable; it has * no effect in a Scan, an Exploration or a Backtest. * - Any interval. The date line switches itself between date-only and * date-and-time because DateTimeToStr mode 3 omits the time part on * end-of-day records. * - Every number reported is taken at the SELECTED bar - the bar under the * vertical selection line - which is not necessarily the last bar. */
_SECTION_BEGIN( "Informative title" );
AtrPeriod = Param( "ATR periods", 14, 2, 200, 1 );TrendSpan = Param( "Trend reference periods", 200, 20, 500, 5 );Compact = ParamToggle( "Compact (small screens)", "No|Yes", 0 );
// styleNoTitle keeps the price plot's own value out of the title, because this// formula writes the whole title itself.Plot( Close, "Price", colorDefault, styleCandle | styleNoTitle );
// Mode 2 sets a flag on an existing pane. Mode 0 would only apply the first// time the chart was inserted, which is why editing a mode-0 call looks broken.SetChartOptions( 2, chartWrapTitle );
ChangePercent = 100 * ( Close - Ref( Close, -1 ) ) / Ref( Close, -1 );AtrPercent = 100 * ATR( AtrPeriod ) / Close;
// SelectedValue returns a single NUMBER, so it is safe inside if().// An array condition would need IIf() instead.SelectedChange = SelectedValue( ChangePercent );if( SelectedChange >= 0 ) ChangeTint = colorGreen; else ChangeTint = colorRed;
// EncodeColor returns a STRING. It is concatenated into the title with "+",// never passed to Plot() as a colour.HeadLine = EncodeColor( colorDefault ) + Name() + " " + Interval( 2 ) + " " + DateTimeToStr( SelectedValue( DateTime() ), 3 );
// StrFormat takes %f, %e and %g for numbers and %s for strings. %d does not// work, because AFL has no integers. A literal percent sign must be written %%.PriceLine = StrFormat( "O %g H %g L %g C %g", SelectedValue( Open ), SelectedValue( High ), SelectedValue( Low ), SelectedValue( Close ) );
ChangeLine = EncodeColor( ChangeTint ) + NumToStr( SelectedChange, 1.2 ) + "%" + EncodeColor( colorDefault );
VolatilityLine = "ATR(" + NumToStr( AtrPeriod, 1.0 ) + ") " + NumToStr( SelectedValue( AtrPercent ), 1.2 ) + "% of price";
// WriteIf returns a string chosen by the SELECTED value of the condition,// so it reads as one word rather than as an array.TrendLine = "Close is " + WriteIf( Close > MA( Close, TrendSpan ), "above", "below" ) + " the " + NumToStr( TrendSpan, 1.0 ) + "-bar average";
// _N() stops the assembled string from also being echoed into the Commentary// or Interpretation window.if( Compact ) _N( Title = HeadLine + "\n" + "C " + NumToStr( SelectedValue( Close ), 1.2 ) + " " + ChangeLine );else _N( Title = HeadLine + "\n" + PriceLine + " " + ChangeLine + "\n" + VolatilityLine + " | " + TrendLine );
_SECTION_END();How it works
Section titled “How it works”The price plot carries styleNoTitle, because the formula writes the whole title itself and
does not want AmiBroker adding “Price: 143.20” to the end of it.
SetChartOptions( 2, chartWrapTitle ) turns on wrapping for the existing pane, so a long
line degrades into two lines rather than being cut off.
The formula then samples the values it needs once, at the selected bar. SelectedChange is a
number, which is what allows the ordinary if statement on the next line to choose a colour;
had it been left as an array, that if would have been a type error and IIf() would have
been needed instead.
The title is assembled from four named pieces rather than one long expression. HeadLine
carries the symbol, the interval name from Interval( 2 ), and the timestamp.
DateTimeToStr() with mode 3 gives an ISO date and appends the time only on non-end-of-day
records, so the same line is correct on a daily chart and on a five-minute chart without any
branching.
PriceLine uses StrFormat() with four %g specifiers, which is where that function pays
for itself. ChangeLine switches to a colour, writes the number with two decimals via
NumToStr(), and switches back. TrendLine uses WriteIf() to turn a comparison into a
word.
The final if chooses between a two-line compact title and the full three-line version, and
both are wrapped in _N().
Key functions
Section titled “Key functions”Interval( 2 )— returns the bar interval as a string such as"Daily"or"15-minute". Fine for display; the official page warns you never to compare against these strings, because they are translated in localised builds. CompareInterval()againstinDailyinstead.DateTimeToStr( number, mode )— mode 3 is ISO date and time, with the time part omitted on end-of-day records; mode 4 is ISO date only.EncodeColor( colorIndex )— returns a colour-escape string for concatenation._N( string )— suppresses text output into the Commentary window.
What you should see
Section titled “What you should see”A title of two or three lines. Moving the vertical selection line along the chart changes every number in it. The change figure is green on up bars and red on down bars. Switching the Compact parameter to “Yes” collapses it to symbol, date and one price.
Test it
Section titled “Test it”- Click on a bar in the middle of the chart. Every number in the title should correspond to that bar, not the last one. Compare the O/H/L/C against the Data window.
- Switch the chart from daily to a 15-minute interval, if you have intraday data. The interval name should change and the timestamp should gain a time part.
- Set the ATR period to 1. The reported ATR percentage should become the current bar’s own true range as a percentage of its close — verify one bar by hand.
- Deliberately break it: change
%gto%dinPriceLineand refresh. You should get an error rather than a silently wrong number. Put it back.
Common errors
Section titled “Common errors”- Error 61 about formatting specifiers. A stray single
%in aStrFormat()string. Double it. - The title shows the last bar no matter where you click. You used
LastValue()where you meantSelectedValue(). - The plot’s own value has vanished from the title. That is correct: assigning
Titlereplaces the automatic listing. Add the value back yourself, or removestyleNoTitlefrom a plot whose automatic entry you want. - The title text appears in the Interpretation window too. The assignment is not wrapped
in
_N(). - Colours leak.
EncodeColor()applies until the next one; reset withEncodeColor( colorDefault )after a coloured field.
Extension
Section titled “Extension”Add a field that reports how many bars have passed since the last time the close crossed the
long moving average, using BarsSince(). Then ask yourself what that number is actually
evidence of — it is a description of the recent past, and a small one, and the title should
not imply otherwise.
What changed
Section titled “What changed”You can now take over the chart’s title bar completely: assign the reserved Title variable,
convert numbers with NumToStr() and several at once with StrFormat(), turn conditions into
words with WriteIf(), colour any part of the string with EncodeColor(), and sample every
value at the bar the reader has selected rather than at the end of the data.
Next: putting marks on the chart itself, where the events happened.
Check your understanding
Sources for this lesson
9 verified · checked 2026-08-31
- 01AmiBroker User's Guide — Creating your own indicators, part 2 (styles, colours, titles)amibroker.com/guide/h_indbuilder2.html2026-08-31
- 02AmiBroker User's Guide — AFL language reference (reserved variables)amibroker.com/guide/a_language.html2026-08-31
- 03AFL Function Reference — StrFormatamibroker.com/guide/afl/strformat.html2026-08-31
- 04AFL Function Reference — NumToStramibroker.com/guide/afl/numtostr.html2026-08-31
- 05AFL Function Reference — WriteValamibroker.com/guide/afl/writeval.html2026-08-31
- 06AFL Function Reference — WriteIfamibroker.com/guide/afl/writeif.html2026-08-31
- 07AFL Function Reference — SelectedValueamibroker.com/guide/afl/selectedvalue.html2026-08-31
- 08AFL Function Reference — EncodeColoramibroker.com/guide/afl/encodecolor.html2026-08-31
- 09AFL Function Reference — DateTimeToStramibroker.com/guide/afl/datetimetostr.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.