Skip to content
Level 3 · AFL DeveloperLessonPart 10 · page 1 of 830 min
30Minutes
9AFL functions
6Sources
StandardRequires
AFL functions taught here9

Plotting with Plot(): Styles, Colours and Panes

Everything you have ever seen on an AmiBroker chart arrived there through a handful of drawing functions, and Plot() is the one that does almost all the work. Learn its nine arguments properly and you stop guessing why a line is the wrong colour, in the wrong pane, or hidden behind something else.

This lesson is the reference you will come back to. It is deliberately dense in places, because the argument order and the constant names are exactly the things you cannot invent.

The documented signature is:

Fragment — not a complete formula

Plot( array, name, color, style = styleLine,
minvalue = {empty}, maxvalue = {empty},
XShift = 0, ZOrder = 0, width = 1 );

Three of the nine are required — array, name and color — and the official tutorial says so in those words. The rest have defaults, and the defaults are usually what you want.

# Argument What it does
1 array The series to draw. One value per bar.
2 name The label used for this plot in the chart title bar.
3 color A single colour number, or an array of colour numbers, one per bar.
4 style A combination of style* flags. Default styleLine.
5 minvalue Lower Y bound. Documented as used by styleOwnScale plots only.
6 maxvalue Upper Y bound. Same restriction.
7 XShift Shifts the drawing horizontally by N bars. Visual only.
8 ZOrder Front/back position. 0 is where the grid sits.
9 width Positive: pixels. Negative: percent of bar width.

Two of those deserve immediate attention.

color accepting an array is the single most useful fact in the whole drawing layer. It is why one Plot() call can produce a line that is green while an average is rising and red while it is falling — you build a colour array with IIf() and pass it in the third slot.

XShift is purely cosmetic. The official example is explicit that the shift happens during plotting and does not affect the source array. Shifting a plot to the right does not make the underlying calculation look into the future; it makes the picture lie about when the value existed, which is a different and arguably worse problem.

AFL has no named arguments. To reach the eighth slot you must fill the fourth through seventh, and the convention for “leave this alone” is Null:

Fragment — not a complete formula

// array, name, color, style, minvalue, maxvalue, XShift, ZOrder
Plot( Close, "Price", colorDefault, styleCandle, Null, Null, 0, 1 );

PlotOHLC() makes this worse by design: its name argument is the fifth and its color the sixth, because four arrays come first. Copying a Plot() argument list across to PlotOHLC() and hoping is a reliable way to lose an afternoon.

Styles are bit flags. Combine them with | (binary OR) or +.

Constant Value Effect
styleLine 1 Line chart — the default
styleHistogram 2 Histogram
styleThick 4 Thick
styleDots 8 Include dots
styleNoLine 16 No line
styleDashed 32 Dashed
styleCandle 64 Candlesticks
styleBar 128 Traditional bars
styleNoDraw 256 Not drawn, but still scales the axis
styleStaircase 512 Staircase
styleSwingDots 1024 Middle dots for staircase
styleNoRescale 2048 Excluded from Y-axis autoscaling
styleNoLabel 4096 No value label on the axis
stylePointAndFigure 8192 Point and figure
styleArea 16384 Area (wide histogram)
styleOwnScale 32768 Independent Y scale
styleLeftAxisScale 65536 Uses the left axis, independent of the right
styleNoTitle 131072 Keeps this plot out of the title string
styleCloud 262144 Filled cloud — intended for PlotOHLC()
styleClipMinMax 524288 Clips the painted area to the min/max levels
styleGradient Gradient area fill; pairs with SetGradientFill()
styleHidden Not drawn, but the values reach the Data window

The numeric values for styleGradient and styleHidden are not published in the official guide, which does not matter: the documentation tells you to use the names, and the names are what the course validator checks.

Not every combination is meaningful. The documentation’s own example is styleCandle | styleLine, which simply resolves to a candlestick. There is also a genuine historical trap: the value 32 is styleDashed in Plot(), but the same value is documented as styleLog in the obsolete graphNstyle table. For modern code, 32 means dashed; logarithmic scaling is a chart option, not a plot style.

These are frequently confused, and they do four different jobs:

  • styleNoDraw — nothing is drawn, but the series still takes part in Y-axis scaling. This is how you reserve room for a series you are about to shift into view.
  • styleHidden — nothing is drawn and the values appear in the Data window and the data tooltip. This is how you inspect an intermediate calculation without adding a line.
  • styleNoLabel — the line is drawn, but no value label appears on the axis.
  • styleNoTitle — the line is drawn, but its value is left out of the title string. You want this on every plot in a formula that writes its own Title.

AmiBroker’s colour argument accepts three different kinds of value, and they share one numeric space.

The named palette runs from index 0 to 55. Indexes 16 to 55 are fixed names — colorBlack, colorRed, colorBrightGreen, colorPaleTurquoise and so on. Indexes 0 to 15 are colorCustom1 through colorCustom16, and those are user-editable in Tools → Preferences → Colors.

For anything outside the palette there is ColorRGB( red, green, blue ) with each component in the range 0–255, and ColorHSB( hue, saturation, brightness ) with all three in the range 0–255. The hue range is the trap: it is 0–255, not 0–360, so passing degrees wraps in ways that look like a bug in your maths.

ColorBlend( colorFrom, colorTo, factor ) mixes two colours, with factor defaulting to 0.5. A factor of 0 gives you colorFrom only and a factor of 1 gives colorTo only, which is the opposite of what most people guess the first time. The official page publishes the two helpers everyone ends up writing anyway: lightening is a blend towards colorWhite, darkening a blend towards colorBlack.

GetChartBkColor() returns the pane’s current background colour, which is how you write a formula that stays legible in both a light and a dark theme. You can pass the returned value straight back into another colour argument. If you want the actual red, green and blue components you must first subtract 56, because the 24-bit values are offset by the number of predefined palette colours.

This is where the third argument earns its keep:

Fragment — not a complete formula

BarTint = IIf( Close >= Open, colorGreen, colorRed );
Plot( Close, "Price", BarTint, styleCandle );

A colour argument is just another array

One colour per bar, produced by exactly the same array arithmetic as everything else in AFL.
Bar12345
Close10.011.010.511.511.2
Open9.810.211.010.611.4
Close >= Open11010
BarTintgreengreenredgreenred
One colour per bar, produced by exactly the same array arithmetic as everything else in AFL. Prices in this diagram are invented for the illustration. They are not market data and nothing should be inferred from them.

SetBarFillColor() gives you a second, independent colour for the interior of candles, bars, areas and clouds, with the outline still coming from Plot(). It must be called before the Plot() it applies to, and it affects only those four styles — calling it before a styleLine plot does nothing at all.

By default every plot in a pane shares one Y scale, computed from all of them together. That is what you want for price and its moving averages, and completely wrong for price and volume, whose numbers differ by orders of magnitude.

styleOwnScale gives a plot its own independent Y range. On its own that range is chosen automatically; supplying minvalue and maxvalue is how you control it. This is the only documented situation in which those two arguments do anything — passing them to an ordinary plot does not rescale the pane.

Two useful consequences follow.

First, if you give two own-scale plots the same explicit minvalue and maxvalue, they share a scale with each other while remaining independent of everything else in the pane.

Second, choosing bounds deliberately lets you place a series in a slice of the pane. The official ribbon idiom does exactly this: plot the constant 2 with a scale running from −0.5 to 100, and the result is a band two per cent of the pane’s height sitting at its foot.

Fragment — not a complete formula

// The plotted constant is the ribbon's height as a percentage of pane height.
Plot( 2, "Ribbon", IIf( Trend, colorGreen, colorRed ),
styleOwnScale | styleArea | styleNoLabel, -0.5, 100 );

styleLeftAxisScale is the third option: the plot is scaled against the left axis, independently of the right. It is the natural home for a secondary series that deserves a readable axis of its own rather than an invisible one.

styleNoRescale is different again and often confused with styleNoLabel. It excludes a plot from the pane’s autoscaling, so a reference line that occasionally runs far away cannot squash everything else flat.

A “pane” is one horizontal strip of a chart window; a chart window is a stack of panes. Every Plot() in a formula draws into the pane that formula was applied to. There is no argument that moves a plot to a different pane, and no way for one formula to draw into two panes.

That gives you exactly two choices, and they are made in the user interface rather than in the code:

  • Overlay — drag the formula from the Charts window onto an existing pane. Its plots join whatever is already there and, unless you ask otherwise, share that pane’s Y scale.
  • Separate pane — drop the formula on empty chart space, or use Insert. It gets its own strip and its own scale.

An indicator whose values live on the price scale — moving averages, bands, pivots — belongs as an overlay. An indicator with its own natural range — RSI, ATR, a percentile — belongs in its own pane, where the 0–100 axis is readable. The one thing you should not do is force a 0–100 oscillator onto a price pane with styleOwnScale and then wonder why nobody can read the values.

Within a pane, ZOrder decides what is in front. The number to remember is that the grid sits at ZOrder 0, so a plot needs a negative ZOrder to be drawn behind the grid lines.

Within a single ZOrder layer, drawing order is the reverse of call order: the last Plot() in the file is drawn first, and therefore furthest back. This is the answer to “why is my second plot hidden behind my first one?”. Setting GraphZOrder = 1; flips the ordering back to call order, and an explicit ZOrder argument always wins over both.

Two more chart-level variables belong here. GraphXSpace = 5; adds five per cent of extra room above and below the graph — despite the X in the name, it controls vertical space, and its default when unset is 2%. And GraphLabelDecimals = 2; sets the number of decimal places in the axis value labels, which does not affect anything in your Title string.

A single price pane that demonstrates every mechanism above at once: conditional bar colour, a candle interior separate from its outline, a moving average layered in front, a volume band pinned to the bottom fifth of the pane on its own scale and behind the grid, an intermediate series that is calculated but never drawn, and a grid line at the average’s current level.

Complete runnable AFL

plot-styles-demo.afl
/*
* Plot styles, scales and layers - worked example for Part 10.
*
* Assumptions
* - Chart pane formula. Apply it to the price pane, or insert it as its own
* pane; the same code works in both places, it simply looks different.
* - Any interval, any symbol. Symbols with no volume data draw an empty
* volume band rather than failing.
* - Nothing here is a trading rule. The formula exists to make the drawing
* mechanics visible: argument slots, own scaling, layering and hiding.
*/
_SECTION_BEGIN( "Price and layers" );
// GraphXSpace is a percentage of extra room added ABOVE AND BELOW the graph.
// The default when it is not set is 2%.
GraphXSpace = 6;
// A per-bar colour array. Plot()'s third argument accepts either a single
// colour number or one colour per bar, which is where conditional colouring
// comes from.
BarTint = IIf( Close >= Open,
ParamColor( "Up bar", colorGreen ),
ParamColor( "Down bar", colorRed ) );
// SetBarFillColor must PRECEDE the Plot() it applies to, and it only affects
// styleCandle, styleBar, styleArea and styleCloud. Here it fills the candle
// bodies while the outline keeps the colour passed to Plot().
SetBarFillColor( ColorBlend( BarTint, GetChartBkColor(), 0.55 ) );
// Argument slots, in documented order:
// array, name, color, style, minvalue, maxvalue, XShift, ZOrder, width
Plot( Close, "Price", BarTint,
ParamStyle( "Price style", styleCandle, maskPrice ) | GetPriceStyle(),
Null, Null, 0, 1 );
MaPeriod = Param( "MA periods", 50, 2, 400, 1 );
// ZOrder 2 puts this line in front of the price plot. Within one ZOrder layer
// the LAST Plot() call is drawn FIRST, i.e. furthest back, so relying on call
// order alone is how plots end up hidden behind each other.
Plot( MA( Close, MaPeriod ), "MA " + NumToStr( MaPeriod, 1.0 ),
ParamColor( "MA colour", colorBlue ), styleLine | styleThick,
Null, Null, 0, 2 );
// A volume band pinned to the bottom of the pane. minvalue/maxvalue are
// documented as being used by styleOwnScale plots only; setting the ceiling to
// five times the tallest recent bar keeps volume inside the lowest fifth of the
// pane. ZOrder -1 draws it behind the grid, because ZOrder 0 is where the grid
// itself sits.
VolumeCeiling = Max( 5 * LastValue( HHV( Volume, 250 ) ), 1 );
Plot( Volume, "Volume",
ColorBlend( ParamColor( "Volume colour", colorBlueGrey ), GetChartBkColor(), 0.5 ),
styleArea | styleOwnScale | styleNoLabel | styleNoTitle,
0, VolumeCeiling, 0, -1 );
// Not drawn at all, but the values are available in the Data window and the
// data tooltip. This is how you inspect an intermediate series without adding
// another line to an already busy chart.
Distance = 100 * ( Close - MA( Close, MaPeriod ) ) / MA( Close, MaPeriod );
Plot( Distance, "Distance from MA (%)", colorDefault, styleHidden );
// A constant level is cheaper to draw with PlotGrid than with Plot, and the
// official guide recommends it explicitly. Pattern 10 is a solid line.
PlotGrid( LastValue( MA( Close, MaPeriod ) ), colorGrey40, 10, 1, False );
_SECTION_END();

Download plot-styles-demo.afl68 lines

The formula falls into four movements.

It begins by opening a section and setting GraphXSpace, so that nothing is drawn hard against the pane edges. Then it builds BarTint, a colour array, and hands it to SetBarFillColor() in a blended, lightened form — blending the bar colour halfway towards the pane’s own background is what keeps the interior legible whether the user runs a light or a dark theme.

The price plot combines ParamStyle() with GetPriceStyle(), which is the idiom AmiBroker’s own price formula uses: the Parameters-dialog choice and the View → Price chart style menu are OR-ed together, so both work rather than one overriding the other.

The moving average is plotted at ZOrder 2 while price sits at ZOrder 1, which puts the line in front of the candles regardless of the order the two calls appear in.

The volume band is the interesting part. It is an area plot with its own scale, running from 0 to five times the tallest volume bar of the last 250. Because the ceiling is five times the peak, the tallest bar reaches one fifth of the pane height. ZOrder -1 puts the whole band behind the grid so it reads as background rather than as data competing with price. The Max( ..., 1 ) guard exists because a symbol with no volume data would otherwise produce a scale whose minimum and maximum are both zero.

Finally, Distance is calculated and plotted with styleHidden, so it never appears on the chart but does appear in the Data window, and PlotGrid() draws a single horizontal line at the average’s most recent value.

  • SetBarFillColor( colorarray ) — sets the interior colour of candles, bars, areas and clouds. Must precede the Plot() it applies to.
  • GetChartBkColor() — returns the pane’s background colour, so a formula can adapt to the user’s theme.
  • ColorBlend( from, to, factor ) — mixes two colours; factor runs from 0 (all from) to 1 (all to).
  • GetPriceStyle() — returns the price style chosen in View → Price chart style, ready to be OR-ed into a style argument.
  • PlotGrid( level, color, pattern, width, Label ) — draws a horizontal reference line through the grid-drawing path. level must be a number.

A candle chart in which up bars and down bars have different outline colours and paler interiors, a thick moving-average line drawn on top of the candles, a soft volume histogram occupying roughly the bottom fifth of the pane and sitting behind the grid, and one solid horizontal line at the current value of the average. Hovering a bar and opening the Data window shows a “Distance from MA (%)” row that has no corresponding line on the chart.

  1. Change MA periods in the Parameters dialog and confirm the line, the grid line and the hidden Data-window row all move together. If the grid line lags, you have two different period values in the formula.
  2. Temporarily change the volume plot’s ZOrder from -1 to 1 and redraw. The volume band should jump in front of the grid lines. Put it back.
  3. Delete styleOwnScale from the volume plot. The pane’s Y axis should collapse, because volume in the millions is now sharing a scale with price in the tens. This is the failure the own scale exists to prevent.
  4. Apply the formula to a symbol with no volume — a currency cross, or an index. The volume band should be empty and everything else should still draw.
  • The volume band fills the whole pane. The minvalue/maxvalue arguments landed in the wrong slots. Count the commas: style is fourth, min is fifth, max is sixth.
  • Nothing changes when you edit a colour. You edited a colorCustom* slot rather than the formula, or the parameter you changed belongs to a different chart pane — parameter values are stored per chart, which the parameters lesson covers.
  • styleHidden still draws a line. Check you have not also passed a style that forces drawing, and remember that the Data-window row only appears when the Data window is open.
  • The moving average disappears at the left edge. That is MA() returning Null for its first periods - 1 bars, which is correct behaviour and not a plotting fault.

Add a second moving average on styleLeftAxisScale and give it a period far from the first — say 5 and 200. Watch what the left axis does, then decide whether a second axis genuinely helps a reader or simply makes two unrelated lines look comparable. That judgement is the real content of this lesson.

You now know what all nine Plot() arguments do, which two of them only work with an own scale, the documented names of the style and colour constants, and the three ways a plot can be present without being visible. You also know that panes are chosen in the interface rather than in code, and that layering is decided by ZOrder first and call order second — in reverse.

The next lesson takes the other half of the pane: the title bar, and the numbers you want it to report.

Check your understanding

Question 1. Which arguments of Plot() are required?
Show the answer and why

Answer: array, name and color

The official tutorial states Plot takes nine parameters, of which the first three are required. style defaults to styleLine.

Question 2. What does this call actually do?
Plot( RSI( 14 ), "RSI", colorBlue, styleLine, 0, 100 );
Show the answer and why

Answer: Nothing beyond drawing a normal line, because minvalue and maxvalue are documented for styleOwnScale plots only

minvalue and maxvalue are documented as used by styleOwnScale plots only. Without that style they are inert, which is why the pane does not rescale.

Question 3. A plot is drawn behind the chart grid. Which ZOrder value makes that happen?
Show the answer and why

Answer: Any negative value

The grid sits at ZOrder 0, so a plot must use a negative ZOrder to be drawn behind it.

Question 4. Which of these keep a series out of sight while still doing something useful? Select all that apply.
Show the answer and why

Answer: styleNoDraw, styleHidden

styleNoDraw suppresses drawing but still scales the axis; styleHidden suppresses drawing but feeds the Data window. styleNoLabel and styleNoTitle both still draw the line.

Question 5. ColorHSB expects hue in the range 0-360.
Show the answer and why

Answer: False

All three ColorHSB components run 0-255, where 255 corresponds to 360 degrees of hue. Passing degrees is the most common ColorHSB mistake.

Sources for this lesson

6 verified · checked 2026-08-31

  1. 01AFL Function Reference — Plotamibroker.com/guide/afl/plot.html2026-08-31
  2. 02AmiBroker User's Guide — Creating your own indicators, part 2 (styles, colours, titles)amibroker.com/guide/h_indbuilder2.html2026-08-31
  3. 03AFL Function Reference — PlotGridamibroker.com/guide/afl/plotgrid.html2026-08-31
  4. 04AFL Function Reference — SetBarFillColoramibroker.com/guide/afl/setbarfillcolor.html2026-08-31
  5. 05AFL Function Reference — ColorRGBamibroker.com/guide/afl/colorrgb.html2026-08-31
  6. 06AFL Function Reference — GetChartBkColoramibroker.com/guide/afl/getchartbkcolor.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.