Module C · Charting & Visualisation - Chapter 13

Arrows, Shapes & Text

Mark signals on the chart with PlotShapes and PlotText - up arrows, down arrows, stars and labels.

Chart
What you'll learn
  • ·PlotShapes & shapeUpArrow
  • ·Positioning with Offset
  • ·Colour by condition
  • ·PlotText for labels
  • ·Stacking shapes
  • ·Buy/Sell arrow conventions

A line on a chart is useful, but a trader's eye is drawn to events - the exact bar where something happened. An arrow under a candle saying "buy here," a star marking an exit, the word "Short" stamped above a high. These marks turn a busy chart into a story you can read in a glance, and they are how every strategy you build from here on will announce its signals. In this chapter you learn to place them with PlotShapes and PlotText.

The good news: you already have everything you need. A signal is a true/false array (Chapter 8), and PlotShapes simply draws a chosen shape on every bar where that array is true. The craft is all in which shape, what colour, and where on the price scale it lands.

Good to know

Did you know? AmiBroker's AFL engine is multi-threaded and uses every core of your CPU - a capability added back in 2010. That is why it can repaint a chart full of shapes, or scan a thousand symbols, without you ever waiting for it.

PlotShapes: a shape where a condition is true

The core call looks like this:

PlotShapes( shapeArray, color, layer, yPosition, offset );

The first argument decides which shape on which bar, and the standard idiom is an IIf that returns a real shape when your condition is true and shapeNone (draw nothing) when it is false:

_SECTION_BEGIN("Simple EMA Crossover with Signal Arrows");

fast = EMA(Close, 10);
slow = EMA(Close, 20);

Buy  = Cross(fast, slow);
Sell = Cross(slow, fast);

// up arrow under the low on Buy bars; nothing on other bars
PlotShapes(IIf(Buy,  shapeUpArrow,   shapeNone), colorGreen, 0, Low,  -20);
// down arrow over the high on Sell bars
PlotShapes(IIf(Sell, shapeDownArrow, shapeNone), colorRed,   0, High,  -20);

Plot( Close, "Price", colorDefault, styleCandle );

_SECTION_END();

The 0 is the drawing layer (leave it at 0 for now). The fourth argument, Low or High, is the y-array - the price level the shape attaches to on each bar. The last number is the offset.

Key idea

PlotShapes draws on every bar where the shape array is non-zero. Wrapping your condition in IIf(cond, someShape, shapeNone) is the universal pattern: a shape appears only where cond is true, because shapeNone means "draw nothing here."

See a shape chosen per bar

The shape array is built by IIf just like any other array - it holds shapeUpArrow on the bars where the signal is true and shapeNone everywhere else. PlotShapes then walks that array and draws each shape at the price you passed as the y-array, here the bar's Low:

Bar 1 2 3 4 5
Buy F T F T F
IIf(Buy, shapeUpArrow, shapeNone) none up none up none
Low (where it is drawn) 99 101 100 103 102

An up arrow appears only on the two Buy bars (2 and 4), each pinned just under that bar's low.

The shape menu

AmiBroker ships a set of named shape constants. The ones you will reach for constantly:

  • shapeUpArrow / shapeDownArrow - the classic buy/sell arrows
  • shapeSmallUpArrow / shapeSmallDownArrow - lighter weight versions
  • shapeSquare - a solid block, great as a coloured backdrop behind an arrow
  • shapeStar - a star, popular for exits and special events
  • shapeHollowCircle / shapeCircle - a ring or dot to flag a level
  • shapeDigit0 ... shapeDigit9 - little numerals, handy for labelling
  • shapeNone - the all-important "draw nothing"

Each is just a number under the hood, which leads to a neat shortcut you will see in the author's files: because true is 1 and false is 0, multiplying a condition by a shape constant gives you the shape on true bars and shapeNone (0) on false bars:

// these two lines do the same thing
PlotShapes(IIf(Sell, shapeStar, shapeNone), colorBrightGreen, 0, High, 12);
PlotShapes(Sell * shapeStar,                colorBrightGreen, 0, High, 12);

The cond * shape form is compact; the IIf form is clearer for beginners. Use whichever you find readable.

Positioning: the y-array and offset

Two arguments together decide where a shape sits. The y-array pins it to a price - pass Low to anchor shapes at the bottom of each candle, High to anchor them at the top. The offset then nudges it from that anchor, measured in screen pixels. Straight from the AmiBroker manual:

Offset is expressed in screen pixels. Negative offsets shift symbols down, positive offsets shift them up. (The default is -12.)

So the sign is about screen direction - down or up - not about "above or below the candle". This catches everyone out once, so here is the rule that actually works: in the author's files you almost always see a negative offset on both arrows, exactly like the -20 in the example above.

// BOTH offsets are negative - the sign just means "push downward"
PlotShapes(IIf(Buy,  shapeUpArrow,   shapeNone), colorGreen, 0, Low,  -20);
PlotShapes(IIf(Sell, shapeDownArrow, shapeNone), colorRed,   0, High, -20);
  • The buy up-arrow is anchored at the Low and pushed down, so it sits cleanly beneath the candle.
  • The sell/short down-arrow is anchored at the High and also pushed down, so it tucks just under the high, hugging the price it marks.

A larger magnitude (-30, -45) pushes the shape further down and away - which is exactly the lever the buy badge in the stacking convention uses to layer its shapes at -40, -45 and -50.

Up arrows pushed under the low and down arrows tucked under the high on crossover bars - both offsets negative
ChartUp arrows pushed under the low and down arrows tucked under the high on crossover bars - both offsets negative

Colour by condition

The colour argument can be a constant like colorGreen, or it can itself be an IIf so the same PlotShapes call changes colour depending on the bar:

// one shape call, two colours: lime when also above the 50-EMA, plain green otherwise
strong = Close > EMA(Close, 50);
arrowColor = IIf(strong, colorLime, colorGreen);

PlotShapes(IIf(Buy, shapeUpArrow, shapeNone), arrowColor, 0, Low, -30);

That lets a single arrow carry two pieces of information at once - that a signal fired, and whether a secondary filter agreed.

The signal convention: chunky entry arrows, star exits

Open any of the author's strategy files and the four trade signals are drawn with one consistent, polished convention - and it is worth adopting wholesale, because it stays crisp even on a crowded chart. The two entries (Buy and Short) get a chunky three-layer arrow badge; the two exits (Sell and Cover) get a star:

/* Plot Buy and Sell Signal Arrows */
PlotShapes(IIf(Buy, shapeSquare, shapeNone),   colorGreen,  0, L, Offset=-40);
PlotShapes(IIf(Buy, shapeSquare, shapeNone),   colorLime,   0, L, Offset=-50);
PlotShapes(IIf(Buy, shapeUpArrow, shapeNone),  colorWhite,  0, L, Offset=-45);

PlotShapes(IIf(Short, shapeSquare, shapeNone),    colorRed,    0, H, Offset=40);
PlotShapes(IIf(Short, shapeSquare, shapeNone),    colorOrange, 0, H, Offset=50);
PlotShapes(IIf(Short, shapeDownArrow, shapeNone), colorWhite,  0, H, Offset=-45);

PlotShapes(Sell  * shapeStar, colorBrightGreen, 0, High,  12);
PlotShapes(Cover * shapeStar, colorRed,         0, Low,  -12);

Why three calls per entry? Stacking two coloured squares just behind one another builds a chunky two-tone block, and the white arrow lands right on top of it. That layering is the whole trick: it makes the marker look far bigger and more elegant than a lone thin arrow, and it stays readable even when the chart is busy. The green/lime up-arrow says "go long" at the low; the red/orange down-arrow says "go short" at the high. Keeping the two squares about ten points apart is what gives each badge its two-tone depth.

The two exits use the compact cond * shapeStar shortcut from earlier: because Sell and Cover are 1/0 arrays, multiplying by shapeStar draws a star where the exit fires and nothing elsewhere. A bright-green star marks a long being closed (Sell, at the high); a red star marks a short being covered (Cover, at the low).

So each of the four reserved signals gets its own unmistakable mark - a green badge-arrow to enter long, a red badge-arrow to enter short, a green star to exit long, a red star to exit short. This is the exact set you will reuse in every strategy in the playbook.

Tip

Build each badge from back to front: plot the biggest, darkest square first, the brighter square slightly offset next, and the white arrow last. Because each PlotShapes call draws over the previous ones, whatever you plot last sits cleanly on top - which is why the arrow always ends up crowning the squares.

PlotText: words on the chart

Sometimes a symbol is not enough and you want an actual label. PlotText writes a string at a given bar and price:

PlotText( "Buy", barNumber, yPrice, textColor, backgroundColor );

Because it needs a specific bar number, PlotText is normally driven by a short for-loop that walks the bars and writes a label wherever the signal is true - the pattern straight from the author's Supertrend file:

for( i = 0; i < BarCount; i++ )
{
    if( Buy[i] )   PlotText("Buy",   i, Low[i],  colorWhite, colorLime);
    if( Sell[i] )  PlotText("Sell",  i, High[i], colorWhite, colorRed);
}

For each bar where Buy[i] is true, it stamps the word "Buy" at that bar's low with a green background; sells get "Sell" at the high in red. Labels like these make a printed chart self-explanatory.

Heads up

Text is heavier to draw than shapes. A handful of labels is fine, but writing text on hundreds of bars will slow a chart noticeably - and clutter it past the point of being readable. Prefer shapes for dense signals and save PlotText for the few marks that truly need words.

Try it yourself

  • Plot a 10/20 EMA crossover and mark buys with shapeUpArrow below the low, sells with shapeDownArrow above the high.
  • Adopt the four-signal convention - chunky badge-arrows for Buy/Short entries and shapeStar exits for Sell/Cover - and tune the offsets until the marks sit clearly off the candles.
  • Colour your buy arrow lime when price is also above its 50-EMA and plain green otherwise, using an IIf in the colour slot.
  • Add a for-loop that writes "Buy" / "Sell" labels with PlotText, then compare how it reads against arrows alone.

Recap

  • PlotShapes(IIf(cond, shape, shapeNone), color, 0, yArray, offset) draws a shape on every bar where cond is true.
  • Useful shapes include shapeUpArrow, shapeDownArrow, shapeSquare, shapeStar, shapeHollowCircle - and shapeNone to draw nothing.
  • The y-array (Low/High) pins a shape to a price; a negative offset drops it below the bar, a positive offset lifts it above.
  • The colour argument can be an IIf, so one call can change colour by condition.
  • The author's signal convention stacks two squares plus a white arrow for entries (Buy/Short) and uses a shapeStar for exits (Sell/Cover) - bold, readable marks for all four signals.
  • PlotText writes words at a specific bar and price, usually from a short for-loop - powerful but heavier, so use it sparingly.

Next we go beyond marks to full colour - painting candles by trend, laying ribbons along the bottom and shading the background for instant context.