Skip to content
Level 4 · Trading System ResearcherLessonPart 28 · page 4 of 832 min
32Minutes
23AFL functions
6Sources
StandardRequires
AFL functions taught here23

Stops with ApplyStop()

A stop is the one part of a trading system whose backtested behaviour is most likely to be fiction, and the reason is a single argument that most people set to the value that makes the results look best.

This lesson covers the four built-in stops and their exact syntax, and then spends most of its length on that argument — because getting the syntax right and the fill assumption wrong produces a system that works perfectly in simulation and fails in a way you did not model.

Fragment — not a complete formula

ApplyStop( type, mode, amount, exitatstop,
volatile = False, ReEntryDelay = 0, ValidFrom = 0, ValidTo = -1 );

Eight arguments, four of them with defaults. Write them all out while you are learning — the defaults are reasonable, but a stop whose behaviour is half implicit is a stop you cannot review.

Constant Value What it does
stopTypeLoss 0 Maximum-loss stop
stopTypeProfit 1 Profit-target stop
stopTypeTrailing 2 Trailing stop
stopTypeNBar 3 N-bar stop — exit after a fixed number of bars
Constant Value Meaning
stopModeDisable 0 Stop off
stopModePercent / stopModeBars 1 Amount in percent — or number of bars for the N-bar stop
stopModePoint 2 Amount in points (price units)
3 Amount as a percent of profit (risk)

stopModePercent and stopModeBars are the same value, 1, wearing two names. Use the name that matches the stop type, because ApplyStop( stopTypeNBar, stopModePercent, 40, … ) is legal, correct and unreadable.

A number for a static level, or an array for a dynamic one. That single fact is what makes volatility-based stops a one-liner.

  • exitatstop — the fill assumption. The whole next section.
  • volatileFalse (default) samples the amount at entry and holds it for the trade; True lets it vary during the trade. True is what makes a single-line Chandelier exit possible. Documented limitation: volatile stops work in backtestRegular mode only.
  • ReEntryDelay — how many bars to wait before re-entering the same symbol.
  • ValidFrom / ValidTo — the first and last bar since entry on which this stop may fire. 0 and -1 (infinite) are the defaults. These are per stop type, and they do not affect regular exits.

ExitAtStop: the argument that decides whether your backtest is fiction

Section titled “ExitAtStop: the argument that decides whether your backtest is fiction”

It is not a Boolean. It has three documented values, and they model three genuinely different things.

Value What is checked Where you exit
0 The trade price only At the regular trade price
1 The bar’s High–Low range Intraday, at exactly the stop level, on the triggering bar
2 The bar’s High–Low range On the next bar, at the regular trade price

Read value 1 again. It says: whenever the bar’s range touched your level, you are awarded a fill at precisely the price you nominated — every time, including the times the market gapped straight past it.

Here is the same gap through all three, with a stop at 92 set from a previous close of 100. Overnight the instrument gaps down, opening at 84, ranging 83 to 86, and closing at 85.

Setting What the engine sees Simulated exit
ExitAtStop = 0 Only the trade price. With a next-open system that is 84, which is below 92. 84. A trade-on-close system would instead sit through the day and exit at 85.
ExitAtStop = 1 The Low of 83 is below 92, so the stop triggered. 92 — a price no order book offered at any moment that day.
ExitAtStop = 2 The range is checked, the trigger is recognised, the exit is deferred. The next bar’s trade price, wherever that turns out to be.

Value 1 is not slightly optimistic on that bar. It is off by the entire size of the gap, and it is off in the same direction every time.

ExitAtStop = 1 is defensible only when your data can see inside the bar. On intraday bars short enough that a gap through your level is genuinely rare, it approximates a real stop order. On daily bars it does not: overnight gaps are exactly when stops matter, and value 1 assumes they never cost you anything.

ExitAtStop = 2 is the conservative default for daily systems. You are recognised as stopped out, and you leave at the next bar’s price, whatever it is.

ExitAtStop = 0 models a system that only ever acts on the trade price — a close-only stop, which is a real and defensible design, but a different one. It will sit through an intraday collapse that recovers by the next open.

There is one more documented wrinkle that catches people out: ExitAtStop = 0 uses the SellPrice/CoverPrice arrays in backtestRegular mode only. In other modes it takes the trade prices from the Settings dialog and your assigned arrays are ignored.

The ApplyStop page gives four scenarios, and it is worth having them in one table because getting the combination wrong produces silently wrong exits.

You trade on You want to exit ActivateStopsImmediately ExitAtStop Delays Trade price
Next bar’s open Intraday at the stop price ON 1 1 Open
Today’s close Intraday at the stop price OFF 1 0 Close
Next day’s open At the open, when the previous day’s range hit the stop see below 2 1 Open
Today’s close Only when the close itself hits the level OFF 0 0 Close

For the third scenario, ActivateStopsImmediately decides ordering rather than triggering: ON means stops execute after regular signals, so cash from a stopped-out position is not available to enter a new trade the same day; OFF means the reverse.

Every stop type, every argument written out, each one switchable.

Complete runnable AFL

stops-workbench.afl
// stops-workbench.afl
// Part 28 - Stops with ApplyStop()
//
// All four built-in stop types, each switchable, with the full documented
// argument list spelled out rather than relying on defaults. Run as a portfolio
// BACKTEST, then change one switch at a time and watch what moves.
//
// ApplyStop( type, mode, amount, exitatstop,
// volatile = False, ReEntryDelay = 0, ValidFrom = 0, ValidTo = -1 )
//
// ASSUMPTIONS - the stop assumptions are the point of this file:
// Fill price Entries and regular exits on the next bar's open.
// Stop fills ExitStyle below decides. Read section 3 before you choose,
// because one of the three choices quietly awards you a fill
// that no order book had to provide.
// Gaps A gap through a stop level is filled at the gap price, not at
// the stop, in reality. ExitStyle 1 does not model that.
// Costs Whatever Settings holds. Set them deliberately.
// Delays One bar on every signal.
//
// This is a simulation of stop behaviour, not a claim that any of these stops
// improves any system.
// ------------------------------------------------------------ 1. the account
SetOption( "InitialEquity", 100000 );
SetOption( "AllowPositionShrinking", True );
RoundLotSize = 1;
PosQty = 10;
SetOption( "MaxOpenPositions", PosQty );
SetPositionSize( 100 / PosQty, spsPercentOfEquity );
SetTradeDelays( 1, 1, 1, 1 );
BuyPrice = Open;
SellPrice = Open;
ShortPrice = Open;
CoverPrice = Open;
// ------------------------------------------------------------ 2. the signals
// A trend entry with no exit rule of its own, so that every exit you see in the
// trade list came from a stop. Turn UseRuleExit on to study how a rule exit and
// a stop compete for the same trade.
FastMa = MA( Close, 20 );
SlowMa = MA( Close, 100 );
Buy = Cross( FastMa, SlowMa );
UseRuleExit = 0;
if( UseRuleExit )
Sell = Cross( SlowMa, FastMa );
else
Sell = 0; // exits come only from the stops below
PositionScore = MA( Close * Volume, 50 );
// ------------------------------------------------- 3. how stops are filled
// ExitStyle is the 4th argument of ApplyStop, and it is NOT a boolean.
//
// 0 Check the trade price only, and exit at the regular trade price. With a
// next-bar-open system this means the stop is only ever tested against
// opens - it will sit through an intraday collapse that recovers by the
// next open.
// 1 Check the bar's High-Low range and exit INTRADAY at the exact stop
// level, on the bar that triggered it. This is the flattering one: it
// assumes a fill at a price you nominated, on every occasion, including
// the occasions where the market gapped straight past it.
// 2 Check the bar's High-Low range but exit on the NEXT bar at the regular
// trade price. Slower and worse, and much closer to what a stop order
// actually does to you on a gap.
//
// The documented pairing for style 1 with a trade-on-open system is
// ActivateStopsImmediately turned ON, ExitAtStop = 1, delays of one, and trade
// price set to open.
ExitStyle = 2;
// With style 2 this switch decides ordering, not whether stops fire: ON means
// stops are executed AFTER regular signals, so cash from a stopped-out position
// is not available to enter a new trade the same day. OFF means the reverse.
SetOption( "ActivateStopsImmediately", False );
// --------------------------------------------------------- 4. the stop levels
AtrPeriod = 14;
MaxLossPct = 8;
ProfitPct = 25;
TrailAtrMult = 3;
NBarLimit = 40;
// The ATR of the bar that is still forming is not known when the order rests in
// the book. Shifting it by one bar removes that dependence. The User's Guide's
// own one-line Chandelier example uses the unshifted ATR; this is the more
// conservative reading of the same idea.
TrailDistance = TrailAtrMult * Ref( ATR( AtrPeriod ), -1 );
UseMaxLoss = 1;
UseProfit = 0;
UseTrailing = 1;
UseNBar = 0;
// ------------------------------------------------------------- 5. the stops
// Every argument is written out, including the ones that would have defaulted,
// so that nothing about this behaviour is implicit.
if( UseMaxLoss )
{
// Amount is sampled at entry and held for the trade unless volatile = True.
ApplyStop( stopTypeLoss, stopModePercent, MaxLossPct, ExitStyle,
False, 0, 0, -1 );
}
if( UseProfit )
{
// ValidFrom = 5 makes the profit target inert for the first five bars of a
// trade. ValidFrom/ValidTo are per stop type and do not touch rule exits.
ApplyStop( stopTypeProfit, stopModePercent, ProfitPct, ExitStyle,
False, 0, 5, -1 );
}
if( UseTrailing )
{
// volatile = True lets the distance follow ATR during the trade, which is
// the documented single-line Chandelier exit. Volatile stops work in the
// default backtestRegular mode only.
ApplyStop( stopTypeTrailing, stopModePoint, TrailDistance, ExitStyle,
True, 0, 0, -1 );
}
if( UseNBar )
{
// stopModeBars, not stopModePercent. With ExitStyle 0 the N-bar stop has the
// LOWEST priority among simultaneous stops; with ExitStyle 1 it has the
// highest and is evaluated before all the others.
ApplyStop( stopTypeNBar, stopModeBars, NBarLimit, ExitStyle,
False, 0, 0, -1 );
}
// -------------------------------------------------------- 6. reading the run
// Turn the result list to "Detailed log" while you are learning: it prints the
// per-bar reasoning, including which stop fired. When two stops trigger on the
// same bar the documented order is ruin, max loss, profit target, trailing,
// n-bar - with the n-bar stop jumping to the front when ExitStyle is 1.

Download stops-workbench.afl140 lines

The signals are deliberately dull — a moving-average cross with no exit rule of its own — so that every exit in the trade list came from a stop. Turn UseRuleExit on when you want to study how a rule exit and a stop compete for the same trade.

The trailing stop, and why the ATR is shifted

Section titled “The trailing stop, and why the ATR is shifted”

Fragment — not a complete formula

TrailDistance = TrailAtrMult * Ref( ATR( AtrPeriod ), -1 );
ApplyStop( stopTypeTrailing, stopModePoint, TrailDistance, ExitStyle,
True, 0, 0, -1 );

volatile = True is what makes the distance follow ATR during the trade — the documented single-line Chandelier exit. The official example is ApplyStop( stopTypeTrailing, stopModePoint, 3 * ATR( 14 ), True, True ).

The shift is this course’s addition, and the reason is worth stating: the ATR of a bar that is still forming is not known while your order rests in the book. Using the unshifted value makes the stop distance depend on the very bar it is protecting you from. Ref( ATR( n ), -1 ) is the conservative reading of the same idea. The difference is usually small; the principle is not.

The official page documents a fixed evaluation order, given by AmiBroker’s author in the page’s comments:

  1. Ruin stop (losing 99.96% of starting capital)
  2. Maximum-loss stop
  3. Profit-target stop
  4. Trailing stop
  5. N-bar stop

With one exception, also documented there: when ExitAtStop = 1, the N-bar stop moves to the highest priority and is evaluated before all the others. The same effect is available from the “Has priority” checkbox in the Settings window.

A trade list tells you a trade ended. It does not tell you why, and with three stops active that is the question you actually have.

Complete runnable AFL

stop-exit-visualiser.afl
// stop-exit-visualiser.afl
// Part 28 - Stops with ApplyStop()
//
// A CHART formula (Analysis -> Formula Editor, then apply to a chart pane) that
// marks each exit with the reason the stop engine gave for it. Reading a trade
// list tells you a trade ended; this tells you which stop ended it, on the bar
// where it happened.
//
// HOW IT WORKS AND WHAT IT IS NOT. Equity( 1 ) runs AmiBroker's OLD,
// single-security backtester and, as a documented side effect, rewrites the Buy
// and Sell arrays: redundant signals removed, stop exits applied, and the exit
// reason written into Sell as a code - 1 regular, 2 max loss, 3 profit target,
// 4 trailing, 5 n-bar, 6 ruin.
//
// That engine knows nothing about portfolio equity, cash competition, the
// maximum-open-positions limit or skipped trades. The arrows below will
// therefore NOT match a portfolio backtest trade for trade, and they are not
// meant to. They are a picture of when each stop would have triggered on this
// one symbol.
//
// ASSUMPTIONS
// Delays Zero, deliberately, so an arrow sits on the bar whose
// condition produced it. The portfolio backtest you are
// auditing almost certainly uses a delay of one.
// Fill price Close, for the same reason.
// Stop fills ExitStyle below, matching stops-workbench.afl.
_SECTION_BEGIN( "Stop exits" );
SetTradeDelays( 0, 0, 0, 0 );
BuyPrice = Close;
SellPrice = Close;
ShortPrice = Close;
CoverPrice = Close;
// ------------------------------------------------------ 1. the rules and stops
FastMa = MA( Close, 20 );
SlowMa = MA( Close, 100 );
Buy = Cross( FastMa, SlowMa );
Sell = 0; // every exit below comes from a stop
MaxLossPct = Param( "Max loss %", 8, 1, 30, 1 );
TrailAtrMult = Param( "Trailing ATR mult", 3, 1, 10, 0.5 );
AtrPeriod = Param( "ATR period", 14, 5, 50, 1 );
ExitStyle = Param( "Exit style (0/1/2)", 2, 0, 2, 1 );
ApplyStop( stopTypeLoss, stopModePercent, MaxLossPct, ExitStyle );
ApplyStop( stopTypeTrailing, stopModePoint,
TrailAtrMult * Ref( ATR( AtrPeriod ), -1 ), ExitStyle, True );
// ------------------------------------------------------- 2. evaluate the stops
// This call is what fills Sell with exit-reason codes. It must come after every
// ApplyStop and before anything that reads Buy or Sell.
Equity( 1 );
// -------------------------------------------------------------- 3. the price
Plot( Close, "Close", colorDefault, styleCandle );
Plot( FastMa, "Fast MA", colorBlue, styleLine );
Plot( SlowMa, "Slow MA", colorBrown, styleLine );
// The maximum-loss level, drawn only while a position is open, so you can see
// how close the bar came without having to guess.
InTrade = Flip( Buy, Sell );
EntryPrice = ValueWhen( Buy, BuyPrice );
MaxLossLevel = IIf( InTrade, EntryPrice * ( 1 - MaxLossPct / 100 ), Null );
Plot( MaxLossLevel, "Max loss level", colorRed, styleDashed | styleNoRescale );
// ------------------------------------------------------- 4. the exit reasons
// Shape and colour both carry the meaning, so the chart is still readable if
// the colours are hard to tell apart.
PlotShapes( IIf( Buy, shapeUpArrow, shapeNone ), colorGreen, 0, Low, -18 );
PlotShapes( IIf( Sell == 1, shapeDownArrow, shapeNone ), colorBlue, 0, High, 18 );
PlotShapes( IIf( Sell == 2, shapeSquare, shapeNone ), colorRed, 0, High, 18 );
PlotShapes( IIf( Sell == 3, shapeCircle, shapeNone ), colorGreen, 0, High, 18 );
PlotShapes( IIf( Sell == 4, shapeStar, shapeNone ), colorOrange, 0, High, 18 );
PlotShapes( IIf( Sell == 5, shapeHollowCircle, shapeNone ), colorGrey40, 0, High, 18 );
// ------------------------------------------------------------- 5. the tally
// Counts to the right-hand end of the loaded range, so you can see at a glance
// whether the trailing stop or the max-loss stop is doing the work.
Title = "Exits on this symbol rule: " + NumToStr( LastValue( Cum( Sell == 1 ) ), 1.0 )
+ " max loss: " + NumToStr( LastValue( Cum( Sell == 2 ) ), 1.0 )
+ " profit: " + NumToStr( LastValue( Cum( Sell == 3 ) ), 1.0 )
+ " trailing: " + NumToStr( LastValue( Cum( Sell == 4 ) ), 1.0 )
+ " n-bar: " + NumToStr( LastValue( Cum( Sell == 5 ) ), 1.0 );
_SECTION_END();

Download stop-exit-visualiser.afl89 lines

The mechanism is Equity( 1 ). Its documented behaviour with flag 1 is to run the old single-security backtester and update the Buy/Sell/Short/Cover arrays so that redundant signals are removed exactly as the backtester does internally, “plus all exits by stops are applied so it is now possible to visualise ApplyStop() stops”.

As a side effect, the exit reason is written back into Sell as a code — 1 regular, 2 max loss, 3 profit target, 4 trailing, 5 n-bar, 6 ruin — and the chart draws a different shape for each.

Note the deliberate mismatch in the visualiser: it uses zero delays and fills at the close, so each arrow sits on the bar whose condition produced it. Your portfolio backtest almost certainly uses a delay of one. That is the right choice for a diagnostic — you want to see the cause on the bar it happened — but it is a difference you have to hold in mind.

Everything above is mechanics. This is the part that decides whether your stop results mean anything.

Complete runnable AFL

gap-through-stop.afl
// gap-through-stop.afl
// Part 28 - Stops with ApplyStop()
//
// How often would a stop order have been filled at the price you nominated?
// Run this as an EXPLORATION over the universe you intend to trade, with the
// Range set to the period you intend to test. It produces one row per symbol.
//
// WHAT IT MEASURES. For every bar it places a hypothetical stop StopPercent
// below the previous close - a level that was fully known before the bar opened
// - and then asks three questions about what the bar actually did:
//
// 1. Did the bar trade at or below that level at all?
// 2. If it did, had it ALREADY opened below it, so that no order resting at
// the level could have been filled there?
// 3. On those occasions, how far below the level did it open?
//
// Question 2 is the one that matters. A backtest with ExitAtStop = 1 assumes
// question 2 always answers "no" and awards the exact stop price every time.
// This exploration tells you the share of occasions on which that assumption is
// simply false for your data, and by how much.
//
// ASSUMPTIONS AND LIMITS
// - This is a property of the DATA, not of any system. It is measured on
// every bar, not only on bars where some strategy held a position.
// - Only the gap at the open is counted. Fast intraday moves through a level
// also cost you, and daily bars cannot see them at all.
// - Ref( Open, 1 ) below reads the next bar deliberately, because we are
// measuring what followed. The same call inside a trading rule would be a
// look-ahead bug. It must never reach Buy.
// - Dividends, splits, halts and bad prints in your database will all show up
// here as gaps. Look at the worst rows before you believe them.
// -2 forces AmiBroker to use all loaded bars rather than the QuickAFL subset,
// so the running totals below cover the whole range.
SetBarsRequired( -2, -2 );
StopPercent = Param( "Stop distance % below prior close", 8, 0.5, 30, 0.5 );
MinPrice = Param( "Minimum close", 1, 0, 100, 0.5 );
MinTurnover = Param( "Minimum 50-bar average turnover", 0, 0, 5000000, 100000 );
// ---------------------------------------------------- 1. usable bars only
Turnover = MA( Close * Volume, 50 );
Usable = Close >= MinPrice
AND Volume > 0
AND High > Low
AND Turnover >= MinTurnover
AND Status( "barinrange" );
LastBarIndex = LastValue( BarIndex() );
HasNextBar = BarIndex() < LastBarIndex;
// ------------------------------------------------------- 2. the stop level
PriorClose = Ref( Close, -1 );
StopLevel = PriorClose * ( 1 - StopPercent / 100 );
Measurable = Usable AND NOT IsNull( PriorClose );
// -------------------------------------------------------- 3. the questions
Touched = Measurable AND Low <= StopLevel;
GapThrough = Touched AND Open < StopLevel;
// How much worse than the nominated price the open actually was, in percent of
// the stop level. Zero on bars that did not gap through.
OpenShortfall = IIf( GapThrough,
100 * SafeDivide( StopLevel - Open, StopLevel, 0 ),
0 );
// The same question for a next-bar exit, which is what ExitAtStop = 2 models:
// the stop is recognised on the triggering bar, but you leave at the following
// open, wherever that happens to be.
NextOpen = Ref( Open, 1 );
NextMeasurable = Touched AND HasNextBar AND NOT IsNull( NextOpen );
NextShortfall = IIf( NextMeasurable,
100 * SafeDivide( StopLevel - NextOpen, StopLevel, 0 ),
0 );
// --------------------------------------------------------- 4. running totals
BarsMeasured = Cum( Measurable );
TouchCount = Cum( Touched );
GapCount = Cum( GapThrough );
GapShortfallSum = Cum( OpenShortfall );
NextCount = Cum( NextMeasurable );
NextShortfallSum = Cum( NextShortfall );
// The single worst gap seen so far, which is the number that decides whether a
// risk limit survives a bad week.
WorstGap = Highest( OpenShortfall );
// ---------------------------------------------------------- 5. the report
Filter = Status( "lastbarinrange" ) AND TouchCount > 0;
AddColumn( BarsMeasured, "Bars measured", 1.0 );
AddColumn( TouchCount, "Level touched", 1.0 );
AddColumn( GapCount, "Opened through", 1.0 );
AddColumn( 100 * SafeDivide( GapCount, TouchCount, Null ),
"Gapped through %", 1.1 );
AddColumn( SafeDivide( GapShortfallSum, GapCount, Null ),
"Avg gap shortfall %", 1.2 );
AddColumn( WorstGap, "Worst gap shortfall %", 1.2 );
AddColumn( SafeDivide( NextShortfallSum, NextCount, Null ),
"Avg next-open shortfall %", 1.2 );
// COUNT and AVERAGE rows. The average weights every symbol equally, so a symbol
// with four touches counts as much as one with four hundred. To pool properly,
// export the table and weight each symbol by its own touch count.
AddSummaryRows( 2 | 16, 1.2 );

Download gap-through-stop.afl111 lines

Run it as an Exploration over the universe and range you intend to test. For every bar it places a hypothetical stop a fixed percentage below the previous close — a level fully known before the bar opened — and asks three questions:

  1. Did the bar trade at or below that level at all?
  2. If it did, had it already opened below it, so that no resting order could have been filled there?
  3. On those occasions, how far below the level did it open?

Question 2 is the whole point. ExitAtStop = 1 assumes the answer is always “no”. This exploration tells you the share of occasions on which that is simply false for your data, and by how much.

Two implementation details in that formula are worth borrowing.

SetBarsRequired( -2, -2 ) uses the documented sbrAll constant to require all past and future bars, which turns QuickAFL off so the running totals cover the whole range rather than a subset.

And Ref( Open, 1 ) reads the next bar deliberately, to measure what a next-bar exit would have cost. Legitimate in a measurement; a look-ahead bug the moment it reaches Buy.

They coexist. Both are active; whichever fires first ends the trade. Three practical points:

A rule exit and a stop can fire on the same bar. The stop ordering above governs stops against each other; AllowSameBarExit and HoldMinBars govern how entries and exits share a bar.

ValidFrom lets a stop be inert early in a trade. ApplyStop( stopTypeProfit, …, 5, -1 ) makes a profit target ignore the first five bars — useful when you want to give a position room before taking a small win, and unavailable from a rule exit without extra code.

A stop is not a risk model. It caps the loss on one trade given that you were filled near your level. It says nothing about correlated positions all stopping out on the same day, and it does not survive a gap. Part 34 builds risk management on top of this, and the distinction between a stop and a risk budget is the reason that part exists.

ApplyStop takes eight arguments; write them all out. stopModePercent and stopModeBars are the same value with two names. volatile = True gives you a Chandelier exit in one line and works in backtestRegular mode only. The evaluation order for simultaneous stops is fixed, with the N-bar stop jumping to the front when ExitAtStop = 1.

And ExitAtStop is where backtests go wrong: value 1 awards you a fill at exactly the price you nominated on every triggering bar, including the ones that gapped past it. On daily data, use 2 unless you can defend 1 — and run the gap exploration on your own universe so that “unless you can defend it” is a measurement rather than an opinion.

Check your understanding

Question 1. What is the fourth argument of ApplyStop and what does it control?
ApplyStop( type, mode, amount, exitatstop, volatile = False, ReEntryDelay = 0, ValidFrom = 0, ValidTo = -1 );
Show the answer and why

Answer: exitatstop — a three-valued setting controlling what is checked (trade price or the bar range) and where you exit

It is the fill assumption, and it is not a Boolean: 0 checks the trade price only; 1 checks the High-Low range and exits intraday at exactly the stop level; 2 checks the range but exits on the next bar at the regular trade price.

Question 2. Your stop sits at 92. The instrument gaps overnight and opens at 84. With ExitAtStop = 1 on daily bars, what does the backtest record?
Show the answer and why

Answer: An exit at 92 — a price the market never offered that day

ExitAtStop = 1 checks the High-Low range and fills at the stop level on the triggering bar. The Low of the gap bar is below 92, so the stop "triggered" and the fill is awarded at 92. This is why ExitAtStop = 2 is the conservative choice for daily systems.

Question 3. Which statements about volatile = True are documented? Select all that apply.
Show the answer and why

Answer: It lets the stop distance vary during the trade instead of being sampled at entry, It enables a single-line Chandelier exit implementation, It works in backtestRegular mode only

The first three are all on the ApplyStop page, including the mode limitation. What is checked — trade price or bar range — is decided by ExitAtStop, a different argument entirely.

Question 4. What is the difference between ValidFrom/ValidTo and SetOption( "HoldMinBars", n )?
Show the answer and why

Answer: ValidFrom/ValidTo apply to one stop type and leave rule exits alone; HoldMinBars suppresses every exit, including stops, during its window

The documentation states that HoldMinBars affects BOTH regular exits and stops, and that any stop generated during the period is ignored — including new highs and drops below a trailing level. ValidFrom/ValidTo are per stop type and do not touch regular exits.

Question 5. Why will the arrows drawn by the Equity( 1 ) visualiser not match a portfolio backtest trade for trade?
Show the answer and why

Answer: Because Equity() uses the old single-security backtester, which knows nothing about portfolio cash competition, the maximum-open-positions limit or skipped trades

The Equity() page says it is kept for backward compatibility and uses the old single-security backtester. It shows when each stop would have triggered on that one symbol in isolation — useful for understanding stop behaviour, useless as a record of what your portfolio actually did.

Sources for this lesson

6 verified · checked 2026-08-31

  1. 01AFL Function Reference — ApplyStopamibroker.com/guide/afl/applystop.html2026-08-31
  2. 02AFL Function Reference — Equity§ Flags = 1amibroker.com/guide/afl/equity.html2026-08-31
  3. 03AFL Function Reference — SetOption§ ActivateStopsImmediately, HoldMinBarsamibroker.com/guide/afl/setoption.html2026-08-31
  4. 04AmiBroker User's Guide — Portfolio-level backtesting§ HOLDMINBARS and EARLY EXIT FEESamibroker.com/guide/h_portfolio.html2026-08-31
  5. 05AFL Function Reference — SetBarsRequiredamibroker.com/guide/afl/setbarsrequired.html2026-08-31
  6. 06AFL Function Reference — SafeDivideamibroker.com/guide/afl/safedivide.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.