Skip to content
Level 4 · Trading System ResearcherLessonPart 35 · page 3 of 430 min
30Minutes
14AFL functions
8Sources
StandardRequires
AFL functions taught here14

From Candidate to Decision

An exploration finishes and eleven rows appear. Everything up to this point was arithmetic applied uniformly; everything after it involves a person who is tired, who has an opinion about one of the tickers, and who would quite like something to happen tonight. This is where a research process either holds or quietly stops being one.

By the end of this lesson you should be able to treat a candidate list as a question rather than an instruction, run a chart review that takes a fixed time and produces a written outcome, assess risk before deciding size rather than after, state exactly where the automated part of your process ends, and explain — with reasons rather than assertion — why this course stops at the human decision and does not teach automated live order execution.

A candidate list says: these instruments met the conditions you defined, ranked in the order you specified, with these numbers attached. It does not say that any of them should be traded. The distinction sounds pedantic until you notice how the list behaves psychologically. A list of eleven rows, produced by a formula, sorted, formatted and dated, carries an authority that its contents have not earned. Every row is the same statement — “conditions met” — and the ranking is an ordering of attention, not a measure of merit.

Three properties make a candidate list usable:

It is short. Short enough that the review budget from Lesson 1 covers all of it. If the list routinely exceeds the budget, the fix is upstream in the screen or the rank, not in reviewing faster.

It carries the numbers, not just the tickers. This is why the screen is an Exploration and not a Scan. The stop level, the stop distance as a percentage of price, the planned size, the participation and the binding constraint all need to be visible before the chart is opened, or the review will silently become a search for reasons.

It is exported before it is reviewed. The list as generated, not the list as edited. This single habit is what makes Lesson 4’s measurement possible: without the original list you cannot later tell the difference between a signal you rejected and a signal that never fired.

The purpose of chart review is narrow: to catch things the formula cannot see. It is not a second opinion on the strategy, and it is not an opportunity to find a better trade.

Give it a fixed order, a fixed set of questions and a hard time cap — two to three minutes per candidate — and require a written outcome for every row. Five checks are enough:

  1. Is the recent price history intelligible? Not “does it look good”. Does it look like trading? A chart whose last three months consist of a gap, a flat line and another gap has passed every numeric filter and should not be traded.
  2. Where is the signal relative to structure? The trigger fired at a level. Is that level in open space, or immediately beneath an obvious multi-year high, or in the middle of a range it has crossed eleven times? Part 5’s material is what you are applying here.
  3. Is the stop level survivable? Look at where the stop sits on the chart, not at the number. A stop inside the last three bars’ noise is a stop that will be hit by noise.
  4. What am I already carrying? Is this the fourth position in the same sector, or a symbol whose price has moved with two of your open positions for the last year? Concentration is a portfolio property and no per-symbol formula can see it.
  5. Is there something on the calendar? End-of-day OHLCV carries no corporate actions, no earnings dates, no index rebalances. If your process does not include a calendar check, it has a blind spot you should at least name.

The outcome of the review is one of three words — take, skip, defer — plus a short reason. Not a paragraph. The reason exists to be counted later, so it wants to be a category: structure, stop too wide, concentration, event, data, size not viable.

Risk assessment comes before sizing, and the order is not negotiable, because the size is derived from the risk and not the other way round.

Stop level first. A volatility-based stop — some multiple of ATR() below the signal close — has the property that it adapts to the instrument rather than to your preferences. Whatever method you use, the stop is a price, decided before entry, written into the journal. Part 34 covers the alternatives properly.

Then the risk budget. A fixed fraction of account equity per trade. This is where the arithmetic lives.

Then the caps. The size implied by the risk budget is a maximum, not a target, and at least two other constraints can bind before it: a cap on position value as a fraction of equity, and a cap on participation as a fraction of the instrument’s average daily turnover.

Here is the arithmetic with clearly labelled illustrative figures. These are not results, and they are not from any market — they are chosen to make the caps bind so you can see them work.

Quantity Value Where it came from
Account equity 100,000 Policy
Risk budget per trade 0.5% = 500 Policy
Signal close 42.00 The signal bar
ATR(20) 1.05 Computed
Stop distance, 2.5 × ATR 2.63 Policy × computed
Stop level 39.37 Close − stop distance
Shares implied by the risk budget 500 / 2.63 = 190 Risk arithmetic
Position value at 190 shares 7,980 Shares × close
Position value cap, 10% of equity 10,000 → 238 shares Policy
Average daily turnover 600,000 Computed
Participation cap, 1% of turnover 6,000 → 142 shares Policy
Planned size 142 shares The smallest of the three
Risk actually taken 142 × 2.63 = 373, or 0.37% of equity Consequence

The last two rows are the point of the whole table. The participation cap binds, the position is smaller than the risk budget would allow, and the trade therefore risks less than the policy maximum. That is the correct outcome. The instinct to “use up” the risk budget by widening the stop or ignoring the cap is the instinct that turns a sizing rule into a decoration.

There is one more assessment that no formula performs: whether you can actually get out. The participation cap is computed from a normal day’s turnover. The day you want to exit may not be a normal day, and the instruments whose turnover collapses under stress are disproportionately the smaller ones your screen finds attractive. This is not a number in the sheet; it is a reason to keep the cap tighter than the arithmetic strictly requires.

The boundary: alert, human review, decision

Section titled “The boundary: alert, human review, decision”

Where the machine stops

  1. ExplorationRules applied to today’s data
  2. AlertAlertIf, e-mail, sound - notification only
  3. Human reviewProtocol, time-capped, written outcome
  4. DecisionTake, skip or defer, with a reason
  5. Order entryBy hand, at your broker. Not taught here.
The course teaches everything to the left of the last step, and describes the last step without teaching it.

On the left of the boundary, AmiBroker does a great deal. AlertIf() fires an action when a condition is true on one of the most recent bars: it writes to the Alert Output window, beeps, plays a WAV file, sends an e-mail, or launches an external program or URL. Its flags argument defaults to 1+2+4+8, where 4 and 8 suppress repeated alerts of the same type and the same date-time respectively, and its type argument is the de-duplication key rather than a label — giving Buy and Sell the same type makes the state machine suppress legitimate alternating signals. The state, the type of the last alert, is kept per symbol. The Analysis window’s Settings drop-down carries Auto repeat Scan/Explore with an interval, which is the sanctioned way to run a screener repeatedly rather than building one by hand.

Alerts only reach you if the relevant subsystem is enabled under Tools → Preferences → Alerts, which has separate checkboxes for Automatic Analysis, Commentary and custom indicators. If you have ever wondered why an alert in a chart formula produced nothing, that is usually why. Where you do not want the de-duplication state machine at all, SendEmail(), PlaySound() and ShellExecute() do the same jobs unconditionally and leave you to manage your own state.

To the right of the boundary, this course stops. Not because the topic is uninteresting, and not because AmiBroker cannot do it.

Why the course stops short of automated execution

Section titled “Why the course stops short of automated execution”

Five reasons, in descending order of how much they should worry you.

Every defect changes category. A wrong array reference in an exploration produces a wrong row and you notice it, or you do not and nothing happens. The same defect behind an order interface produces a position. All the things this course has taught you to catch — look-ahead, a Null that propagated, a regime gate that failed open when the benchmark went missing, an off-by-one in a Ref() — become financial events instead of research errors. The skill that catches them is the same; the cost of missing one is not.

Repeated execution places repeated orders. AmiBroker’s own automatic-trading documentation is explicit about this: run order-placing code from an indicator and every chart refresh that satisfies the condition fires another order. The documented remedies are one-shot triggers, static variables holding order identifiers, or checking the pending list before acting. That is an entire discipline — idempotency, state reconciliation, error handling — and it is not the discipline this course teaches.

The operational surface is large and unglamorous. A live order path has to cope with a disconnected broker session, a partial fill, a rejected order, a stale position list, a symbol that maps differently at the broker than in your database, and a machine that goes to sleep. None of that is technical analysis and all of it decides whether the system works.

Your obligations are specific to you. What you are permitted to automate, what records you must keep and what your broker’s agreement says about unattended order entry vary by country, by account type and by broker. A course cannot answer that for you, and a course that pretends the question does not exist is doing something worse than omitting it.

The last thing you should automate is the judgement you have not yet measured. Lesson 4 shows how to measure the gap between what your rules said and what you did. Until you have that measurement, you do not know whether removing the human improves anything. Automating first and measuring afterwards gets the order exactly backwards.

What exists, so you can go and study it properly

Section titled “What exists, so you can go and study it properly”

AmiBroker has an automatic trading interface for Interactive Brokers, called IBController. It is a separate application, BrokerIB.EXE, which sits between AmiBroker and Trader Workstation and is launched automatically by the first GetTradingInterface("IB") call. It exposes order placement, modification and cancellation plus account and position queries; it provides no price data. Its documented version is 1.3.8 and it is permanently labelled beta, explicitly because the broker’s API changes without notice and fixes can only follow. By default it does not transmit orders at all — transmission requires entering an unlock code after accepting an agreement. Its own readme is intended for the Analysis window rather than for indicators, and advises testing first on a demo connection, then on a paper-trading account, and not leaving automated trading unattended.

Those are the facts, and they are the right facts to have before you decide to go further. If you do, treat it as a new subject with its own literature and its own testing regime, not as the last line of an AFL formula.

One exploration that ends the mechanical half of the evening: apply the regime gate, the universe, the setup and the trigger, then print every number the reviewer needs — stop level, stop distance, planned size, which cap is binding, participation — so that the review can be a review rather than a research session.

Complete runnable AFL

candidate-review-sheet.afl
// candidate-review-sheet.afl
// Part 35 - From Candidate to Decision
//
// The daily hand-off. This Exploration applies the regime gate, the universe
// definition, the setup and the trigger, and then prints every number a human
// needs in order to review a candidate and decide. It stops there. It places
// no orders, and it deliberately produces no column that says "buy".
//
// The sizing arithmetic below is the arithmetic of the plan, not a
// recommendation. It answers "if I were to take this trade at the risk budget
// I have already committed to, how many shares would that be, and would that
// position be too large for this instrument's turnover?"
//
// Run settings this formula assumes:
// Apply to: the watch list that defines your universe
// Range: All quotations, with Filter restricting output to the last bar
//
// Execution assumptions this sheet is built on. Change them here, not in your
// head, and record the change in the journal:
// - Signals are evaluated on the closed daily bar. Nothing is acted on
// during the session that produced the bar.
// - The planned entry is the NEXT bar's open. The sheet therefore never
// shows a fill price, because on the evening you read it that price does
// not exist yet.
// - The stop is a price level, not an order type. Whether it rests at the
// broker or is checked manually changes the fill you get and is a
// decision recorded in the journal, not something this formula knows.
// - Commission and slippage are not modelled here. They belong in the
// backtest, where they change the answer; here they would only decorate
// a number the human is about to read.
_SECTION_BEGIN("Candidate review sheet");
// ---- Account and risk policy --------------------------------------------
AccountEquity = Param( "Account equity", 100000, 1000, 100000000, 1000 );
RiskPercent = Param( "Risk budget per trade (% of equity)", 0.5, 0.05, 5, 0.05 );
MaxPositionPct = Param( "Position value cap (% of equity)", 10, 1, 100, 1 );
MaxParticipation = Param( "Participation cap (% of daily turnover)", 1, 0.05, 25, 0.05 );
// ---- Universe --------------------------------------------------------------
MinTurnover = Param( "Liquidity floor: average daily turnover", 2000000, 0, 100000000, 100000 );
TurnoverWindow = Param( "Turnover averaging window (bars)", 50, 5, 250, 5 );
MinPrice = Param( "Price floor (per share)", 5, 0, 500, 0.5 );
// ---- Regime gate ----------------------------------------------------------
BenchmarkTicker = ParamStr( "Benchmark symbol", "^GSPC" );
RegimePeriod = Param( "Benchmark trend period (bars)", 200, 20, 500, 10 );
// ---- Setup and trigger -----------------------------------------------------
TrendPeriod = Param( "Instrument trend period (bars)", 200, 20, 500, 10 );
BreakoutPeriod = Param( "Breakout lookback (bars)", 50, 5, 250, 5 );
AtrPeriod = Param( "ATR period", 20, 2, 200, 1 );
StopAtrMult = Param( "Stop distance (ATR multiples)", 2.5, 0.5, 10, 0.25 );
RaiseAlerts = ParamToggle( "Raise an alert for each candidate", "No|Yes", 0 );
// ---- Universe membership --------------------------------------------------
Turnover = Close * Volume;
AvgTurnover = MA( Turnover, TurnoverWindow );
SafeTurnover = Max( Nz( AvgTurnover ), 1 );
InUniverse = Nz( AvgTurnover ) >= MinTurnover AND Close >= MinPrice;
// ---- Regime gate ----------------------------------------------------------
// The gate does not claim to know what the market will do next. It says that
// while the benchmark is below its own long average, this particular set of
// rules is switched off, because that is the condition under which it was
// tested and the condition under which its results were measured.
BenchClose = Foreign( BenchmarkTicker, "C" );
BenchTrend = MA( BenchClose, RegimePeriod );
RegimeOn = NOT IsNull( BenchClose ) AND BenchClose > BenchTrend;
// ---- Setup (a state) and trigger (an event) --------------------------------
InstrumentTrend = MA( Close, TrendPeriod );
Setup = Close > InstrumentTrend;
// Ref( High, -1 ) excludes the current bar from its own breakout level, so the
// level being crossed was established before today rather than by today.
PriorHigh = HHV( Ref( High, -1 ), BreakoutPeriod );
Trigger = Cross( Close, PriorHigh );
IsCandidate = InUniverse AND RegimeOn AND Setup AND Trigger;
// ---- Risk arithmetic -------------------------------------------------------
// Stop first, then size. Sizing before the stop is known is how a position
// ends up risking whatever happens to be left over.
Volatility = ATR( AtrPeriod );
StopDistance = StopAtrMult * Volatility;
StopLevel = Close - StopDistance;
// A floor on the denominator: an instrument whose ATR is reported as zero,
// which happens on a symbol with a long run of identical closes, would
// otherwise produce an infinite share count.
SafeDistance = Max( Nz( StopDistance ), 0.000001 );
RiskBudget = AccountEquity * RiskPercent / 100;
RiskShares = floor( RiskBudget / SafeDistance );
// Three caps, applied in the order that makes the binding one visible. The
// smallest of them is what you would actually be able to trade.
ValueCapShares = floor( ( AccountEquity * MaxPositionPct / 100 ) / Max( Close, 0.000001 ) );
LiquidityCapShares = floor( ( SafeTurnover * MaxParticipation / 100 ) / Max( Close, 0.000001 ) );
PlannedShares = Min( RiskShares, Min( ValueCapShares, LiquidityCapShares ) );
PlannedValue = PlannedShares * Close;
PlannedRisk = PlannedShares * StopDistance;
// 0 risk budget, 1 position value cap, 2 turnover participation cap.
BindingCap = IIf( PlannedShares == RiskShares, 0,
IIf( PlannedShares == ValueCapShares, 1, 2 ) );
BindingList = "risk budget\nposition value cap\nparticipation cap";
// What the position actually risks as a percentage of equity once the caps
// have cut it down. When a cap binds, this is smaller than the risk budget -
// which is the correct outcome, not a bug to be tuned away.
RiskOfEquity = 100 * PlannedRisk / Max( AccountEquity, 1 );
Participation = 100 * PlannedValue / SafeTurnover;
// ---- Output ----------------------------------------------------------------
Filter = IsCandidate AND Status( "lastbarinrange" );
AddTextColumn( FullName(), "Name", 40 );
AddColumn( Close, "Signal close", 1.2 );
AddColumn( StopLevel, "Stop level", 1.2 );
AddColumn( StopDistance, "Stop distance", 1.2 );
AddColumn( 100 * StopDistance / Max( Close, 0.000001 ), "Stop as % of price", 1.2 );
AddColumn( PlannedShares, "Planned shares", 1.0 );
AddColumn( PlannedValue, "Planned value", 1.0 );
AddColumn( RiskOfEquity, "Risk % of equity", 1.2 );
AddMultiTextColumn( BindingCap, BindingList, "Binding constraint", 22 );
AddColumn( Participation, "Participation %", 1.2 );
AddColumn( AvgTurnover, "Avg turnover", 1.0 );
AddColumn( 100 * Volatility / Max( Close, 0.000001 ), "ATR as % of price", 1.2 );
// Counting the two default columns, "Participation %" is column 12. Reviewing
// the least liquid candidates first is deliberate: they are the ones where the
// plan is most likely to be undeliverable, and finding that out early saves
// the review time for candidates you could actually trade.
SetSortColumns( -12 );
AddRankColumn();
if ( RaiseAlerts )
{
// On end-of-day data run after the close, the last bar is complete and this
// is the alert you want. On intraday data the forming bar can turn a
// condition on and off within the bar; Part 25 covers the completed-bar
// guard and the de-duplication state machine behind AlertIf.
AlertIf( IsCandidate, "", "Candidate for review: " + Name(), 1 );
}
_SECTION_END();

Download candidate-review-sheet.afl160 lines

The header comment is the most important part of the file, and it is deliberately long. It states the execution assumptions — signals evaluated on the closed daily bar, planned entry at the next bar’s open, the stop as a price level rather than an order type, costs modelled in the backtest rather than here — because those assumptions are what make the sheet’s numbers mean anything.

The rules are assembled in the pipeline’s order: universe, then regime gate, then setup, then trigger. The setup is a state (Close > MA( Close, TrendPeriod )) and the trigger is an event (Cross() above a breakout level computed from Ref( High, -1 ), so the level being crossed was established before today rather than by today). Part 8’s state-versus-event distinction is doing real work here.

The risk section derives the stop first and the size second, then applies three caps and reports which one bound. The Risk % of equity column shows what the position actually risks after the caps have cut it, which is often less than the policy maximum — and seeing that in a column is what stops people from quietly restoring the difference.

The output is sorted by participation descending, so the candidates whose plans are least deliverable are reviewed first. That ordering is a small thing that changes the evening: finding out early that three of eleven candidates cannot be traded at a sensible size leaves time to review the rest properly.

  • ATR( period ) — average true range, used as the volatility unit that the stop distance is expressed in. It takes only a period; the price arrays are implicit.
  • Cross( array1, array2 ) — true on the bar where the first crosses above the second. An event, not a state.
  • HHV( Ref( High, -1 ), n ) — the highest high of the previous n bars. Dropping the Ref() is the classic way to build a breakout level that includes the bar breaking it.
  • floor() and Min() — share counts are integers, and the binding cap is the smallest of several candidate sizes. Min() takes two arguments, so three caps need it twice.
  • AddRankColumn() — adds a rank column based on the sort established by the preceding SetSortColumns(). Call order matters: with no prior sort it degenerates into a line-number column.
  • AlertIf( condition, command, text, type, flags, lookback ) — notification only. Note that the default lookback is 1, so only the most recent bar is examined.

Apply to your universe watch list, Range All quotations, press Explore. On a typical evening in a permitted regime you should get a handful of rows. On a great many evenings you should get none, and that is the formula working: a gated breakout system on a few hundred symbols is inactive most of the time.

Every row should have a stop level below the signal close, a planned share count greater than zero, and a binding-constraint column that reads sensibly against the numbers beside it.

  1. Set the risk budget very high — 5 per cent — and confirm the binding constraint switches to position value cap or participation cap for most rows. The caps are meant to bind under pressure, and a cap that never binds has not been tested.
  2. Set the participation cap to 25 per cent and watch planned sizes jump. Then ask yourself whether you would actually place those orders, which is a more useful exercise than reading about market impact.
  3. Pick one row and reproduce its arithmetic by hand from the columns shown. If your hand calculation and the sheet disagree, one of them is wrong, and finding out which is exactly the kind of check that catches a misplaced parenthesis.
  4. Set the ATR period to 2 and confirm stop distances collapse and share counts explode. This is not a bug; it is a demonstration that the sizing rule is only as sensible as the volatility estimate under it.
  • Every candidate has a huge share count. The stop distance is near zero, usually because ATR() is being computed on a symbol with a long run of identical closes. The floored denominator in the formula prevents an infinity but cannot make the number meaningful.
  • The breakout fires on almost every bar. Ref( High, -1 ) was dropped from the HHV(), so the level includes the bar that is breaking it.
  • No rows, ever, on any date. Check the regime state first — it is the gate most likely to be closed for the whole range — then the universe floors, then the trigger.
  • Alerts appear once and then never again. That is the AlertIf() de-duplication state machine working as documented, keyed on alert type and stored per symbol. Drop flags 4 and 8 while experimenting.
  • Alerts never appear at all. The Automatic Analysis checkbox under Tools → Preferences → Alerts is off.

Add a column that reports how many of tonight’s candidates share a sector, using SectorID(1). It will not solve the concentration problem — that is a portfolio property and needs the positions you already hold — but it turns the fourth review question from a memory exercise into a number, and it makes it obvious on the evenings when a screen has found the same trade eight times.

The candidate list is a question, and the ranking on it orders your attention rather than measures merit. Chart review is a time-capped protocol with five fixed questions, a three-word outcome and a categorised reason, and its authority is limited to rejection: an idea that arrives during review goes in the backlog, not into tonight’s orders. Risk assessment precedes sizing, and the planned size is the smallest of the risk budget, the position value cap and the participation cap — frequently less than the policy maximum, which is the correct outcome and not a shortfall to be recovered.

The boundary sits between the alert and the order. Everything to its left is taught here in full; everything to its right is described accurately and left as further study, because the failure modes change category at that line and the obligations on the other side of it are yours specifically.

The final lesson closes the loop: what to write down at the moment of decision, and how to use it later to tell whether the problem was the strategy or you.

Check your understanding

Question 1. During chart review a candidate looks unappealing, but a different symbol on the list would be a better trade if you moved the trigger from a 50-bar to a 40-bar breakout. What does the protocol permit?
Show the answer and why

Answer: Record the observation in the research backlog, decide tonight’s candidates on tonight’s rules, and test the change in the research loop

Changing a rule inside the operating loop destroys the comparison between what the rules said and what you did, which is the only measurement that can later tell you whether the strategy or the execution was at fault. The observation may well be a good one, and the research loop is where it gets tested against evidence rather than against tonight’s mood.

Question 2. Equity is 100,000, the risk budget is 0.5%, the stop distance is 2.63 and the participation cap allows 142 shares while the risk arithmetic allows 190. What size do you take, and what does the position risk?
Show the answer and why

Answer: 142 shares, risking about 373 - less than the policy maximum, which is the intended behaviour of a binding cap

Caps are maxima applied in sequence and the smallest one governs. Risking less than the budget when a cap binds is the design working. Widening the stop to restore the risk figure inverts the whole procedure: the stop would then be derived from the size rather than the size from the stop.

Question 3. An alert fires once for a symbol and never again on subsequent runs, even though the condition is still true. What is happening?
AlertIf( IsCandidate, "", "Candidate: " + Name(), 1 );
Show the answer and why

Answer: AlertIf’s default flags include 4 and 8, which suppress repeated alerts of the same type and date-time, and the state is kept per symbol

AlertIf contains a documented state machine that stores the last alert type per symbol, and the default flags 1+2+4+8 include the two suppression bits. This is helpful in production and confusing while experimenting; the documented remedy is to pass flags of 1+2 while testing. It is also why the type argument is a de-duplication key rather than a label.

Question 4. Which of these are reasons this course gives for stopping at the human decision? Select all that apply.
Show the answer and why

Answer: A defect that produced a wrong exploration row would instead produce a position, Repeated execution of order-placing code places repeated orders, which requires an idempotency discipline the course does not teach, What you may automate, and what records you must keep, depend on your jurisdiction, account type and broker agreement

AmiBroker does have an order interface - a separate beta application for one broker, documented and pointed at in the lesson - so the reason is never that the facility is missing. The reasons are that the consequences of a defect change category, that safe order placement is its own discipline, and that the obligations involved are specific to the individual.

Question 5. Why does the review sheet sort candidates by participation descending rather than by the ranking score?
Show the answer and why

Answer: Because it puts the candidates whose plans are least deliverable first, so that discovering an unworkable size costs little review time

The review budget is fixed, so the order in which candidates are examined is a resource decision. Candidates whose planned size is a large fraction of daily turnover are the ones most likely to be rejected on feasibility, and rejecting them quickly leaves the budget for candidates you could actually trade. Participation predicts nothing about returns; it describes deliverability.

Sources for this lesson

8 verified · checked 2026-08-31

  1. 01AFL Function Reference — AlertIfamibroker.com/guide/afl/alertif.html2026-08-31
  2. 02AmiBroker User's Guide — Using formula-based alertsamibroker.com/guide/h_alerts.html2026-08-31
  3. 03AFL Function Reference — ATRamibroker.com/guide/afl/atr.html2026-08-31
  4. 04AFL Function Reference — AddRankColumnamibroker.com/guide/afl/addrankcolumn.html2026-08-31
  5. 05AmiBroker User's Guide — Rankingamibroker.com/guide/h_ranking.html2026-08-31
  6. 06AmiBroker Automatic Trading Interface (IBController) readmeamibroker.com/at2026-08-31
  7. 07AFL Function Reference — GetTradingInterfaceamibroker.com/guide/afl/gettradinginterface.html2026-08-31
  8. 08AmiBroker User's Guide — Using New Analysis window§ Auto repeat Scan/Exploreamibroker.com/guide/h_newanalysis.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.