Skip to content
Level 3 · AFL DeveloperProjectPart 13 · page 5 of 560 min
60Minutes
13AFL functions
7Sources
StandardRequires
AFL functions taught here13

Project: Market Relative Strength Ranking Tool

This is the tool the previous four lessons were building towards: an Exploration that scores every eligible symbol in a universe against a benchmark over three lookback windows, ranks those scores across the whole universe bar by bar, and reports each symbol’s position together with the evidence you need to decide whether to believe it.

Set aside an hour. Roughly twenty minutes of that is running and checking rather than typing, and the checking is the part that makes the tool worth having.

Produce a table, for one bar, with one row per eligible symbol, ordered by rank, showing:

  • the symbol’s rank in the universe, where rank 1 is the strongest;
  • the blended excess return that produced that rank;
  • the rank expressed as a percentage of the field, so the number survives a change of universe size;
  • the liquidity and price figures the eligibility decision was made on;
  • for excluded symbols, the reason they were excluded;
  • two self-check numbers that make a broken ranking visible instead of plausible.

And produce it without leaving anything behind in memory that could contaminate the next run.

Every one of these is a choice, and a different choice would give a different tool. They are set out here so you can change them deliberately rather than by accident.

The score is the weighted average of the symbol’s excess return over the benchmark across three windows — 63, 126 and 252 bars by default, roughly a quarter, a half and a year of daily trading. Three windows rather than one, because Part 13’s first lesson showed that a single window is a strong opinion disguised as a measurement, and blending at least makes the opinion explicit and adjustable.

Excess over a benchmark rather than raw return, because a ranking of raw returns in a rising market mostly ranks how much of the market’s move each symbol captured. Subtracting the benchmark’s return over the same bars leaves what is specific to the symbol. Both are defensible; the weights and the benchmark are parameters so you can compare them.

Three absolute tests decide who is in the comparison at all: a minimum close price, a minimum median turnover over 50 bars, and enough history for the longest lookback plus the turnover window to be defined. Symbols that fail get a Null score on the bars where they fail, so they take no position in the ranking.

The order matters. Ranking first and filtering afterwards would give you the top twenty of everything and then remove most of them, leaving a ragged list of whatever survived. Filtering first gives a top twenty of things you could actually act on.

Median turnover rather than average turnover, because one frantic day on fifty times normal volume should not qualify a stock that is untradeable on the other 49 days.

From the Analysis window, through GetOption( "ApplyTo" ) and GetOption( "FilterIncludeWatchlist" ), not from a list typed into the formula. This is the multi-threading chapter’s own recommendation, and it removes an entire class of bug: the list you rank and the list you report on cannot disagree, because there is only one list.

The formula refuses to run with Apply To set to “current symbol” and says why. A ranking of one symbol is not a ranking.

One prefix family, RsRank..., long enough to be unmistakably this tool’s. Input scores go under RsRankScore, ranks are generated with the output prefix RsRankOrder — so the generated names begin RsRankOrderRsRankScore, which does not begin with RsRankScore and therefore cannot be fed back into a second ranking call. Everything is removed at the start of pass one, with the wildcard, and nothing is written with persistent set.

What one run of the tool does

  1. GuardRead Apply To; refuse a single-symbol run
  2. RemoveWipe this tool’s own static variables, wildcard included
  3. Benchmark onceSetForeign, read closes, restore
  4. Score every symbolEligibility, three lookbacks, blend, store
  5. Rank onceStaticVarGenerateRanks, normal mode
  6. Self-checkCount what was scored; record the highest rank issued
  7. Report per symbolRead own score and rank; build the table
Steps two to six all happen inside the Status("stocknum") == 0 block, on one thread, before any other symbol runs.

Complete runnable AFL

rs-ranking-tool.afl
// rs-ranking-tool.afl
// Part 13 - Project: Market Relative Strength Ranking Tool
//
// A cross-sectional relative strength ranking with an Exploration front end.
// It scores every eligible symbol in the chosen universe against a benchmark
// over three lookback windows, ranks the scores across the universe bar by
// bar, and reports the rank together with the evidence needed to check it.
//
// WHAT THIS IS NOT: it is not a trading system. There is no entry, no exit, no
// position sizing, no cost model and no risk control anywhere in this file.
// A rank is a description of the cross-section, nothing more.
//
// How to run it: Analysis window, Apply to = Filter (a watch list) or All,
// Range = 1 recent bar, then Explore. Set the parameters from the Analysis
// window's Parameters button.
//
// Assumptions declared up front:
// - Daily bars, one currency, prices adjusted consistently for splits.
// Unadjusted prices make every momentum score wrong on split days.
// - The benchmark must exist in this database. If it does not, the formula
// stops with a message rather than silently ranking absolute returns.
// - Everything inside one AFL execution is aligned to the bar grid of the
// symbol that execution is running on. Pass 1 runs on the first symbol of
// the universe, so the whole ranking inherits that symbol's calendar.
// Use a full-history reference symbol, or turn on "Pad and align all data
// to reference symbol" in Analysis settings, before trusting long history.
// - StaticVarSet stores only the bars currently in use, so the Analysis
// range and QuickAFL decide how much history the ranking covers.
// - The ranking function is called ONCE per run, inside the
// Status("stocknum") == 0 block, as the documentation requires.
// ------------------------------------------------------------------- naming
// One long, distinctive prefix family. Static variable names are shared by
// every formula in the running AmiBroker, and the ranking function matches
// input names by "starts with", so a short prefix is a correctness bug.
InputPrefix = "RsRankScore";
OutputPrefix = "RsRankOrder";
ScoredName = "RsRankScoredCount";
IssuedName = "RsRankHighestIssued";
BenchName = "RsRankBenchmarkOk";
// --------------------------------------------------------------- parameters
BenchSymbol = ParamStr( "Benchmark symbol", "^GSPC" );
Look1 = Param( "Lookback 1 (bars)", 63, 10, 250, 1 );
Look2 = Param( "Lookback 2 (bars)", 126, 20, 400, 1 );
Look3 = Param( "Lookback 3 (bars)", 252, 40, 750, 1 );
Weight1 = Param( "Weight on lookback 1", 1, 0, 5, 0.1 );
Weight2 = Param( "Weight on lookback 2", 1, 0, 5, 0.1 );
Weight3 = Param( "Weight on lookback 3", 1, 0, 5, 0.1 );
MinPrice = Param( "Minimum close price", 5, 0, 500, 0.5 );
MinTurnover = Param( "Minimum median turnover", 2000000, 0, 50000000, 100000 );
TurnPeriod = Param( "Turnover median period", 50, 5, 250, 1 );
TopCount = Param( "Mark the top N", 20, 1, 500, 1 );
TieToggle = ParamToggle( "Tie mode", "1224 shared ranks|1234 consecutive ranks", 0 );
ShowAll = ParamToggle( "Rows to show", "Ranked symbols only|Every symbol", 0 );
if( TieToggle ) TieMode = 1234; else TieMode = 1224;
// The longest lookback plus the turnover window is the shortest history a
// symbol can have and still produce a defined score on the last bar.
MinBars = Max( Max( Look1, Look2 ), Look3 ) + TurnPeriod;
// ---------------------------------------------------------------- functions
// Defined once and called from both passes, so the eligibility rule that
// decides who is ranked is literally the same code that explains why.
// It reads the price arrays currently in force, which is what makes it work
// unchanged inside a SetForeign block.
function IsLiquidEnough( TurnoverFloor, MedianPeriod )
{
local MedianTurnover;
MedianTurnover = Median( Close * Volume, MedianPeriod );
return MedianTurnover >= TurnoverFloor;
}
function IsEligible( PriceFloor, TurnoverFloor, MedianPeriod, BarsNeeded )
{
local Liquid, Priced, LongEnough;
Liquid = IsLiquidEnough( TurnoverFloor, MedianPeriod );
Priced = Close >= PriceFloor;
LongEnough = BarIndex() >= BarsNeeded;
return Liquid AND Priced AND LongEnough;
}
// ----------------------------------------------------------------- universe
ApplySetting = GetOption( "ApplyTo" );
if( ApplySetting == 2 )
{
WatchListNum = GetOption( "FilterIncludeWatchlist" );
SymbolList = CategoryGetSymbols( categoryWatchlist, WatchListNum );
}
else if( ApplySetting == 0 )
{
SymbolList = CategoryGetSymbols( categoryAll, 0 );
}
else
{
Error( "Set Apply to = All symbols, or Filter with an Include watch list." );
}
// ------------------------------------------------------------------- PASS 1
if( Status( "stocknum" ) == 0 )
{
StaticVarRemove( InputPrefix + "*" );
StaticVarRemove( OutputPrefix + InputPrefix + "*" );
StaticVarRemove( ScoredName );
StaticVarRemove( IssuedName );
StaticVarRemove( BenchName );
// The benchmark is read once, not once per symbol. Its returns are on the
// bar grid of this execution's symbol, and so is every symbol read below,
// which is what makes the subtraction legitimate.
BenchOk = SetForeign( BenchSymbol );
BenchC = Close;
RestorePriceArrays();
StaticVarSet( BenchName, BenchOk );
if( NOT BenchOk )
Error( "Benchmark symbol " + BenchSymbol + " is not in this database." );
BenchRoc1 = ROC( BenchC, Look1 );
BenchRoc2 = ROC( BenchC, Look2 );
BenchRoc3 = ROC( BenchC, Look3 );
WeightSum = Max( Weight1 + Weight2 + Weight3, 0.0001 );
for( i = 0; ( Sym = StrExtract( SymbolList, i ) ) != ""; i++ )
{
if( SetForeign( Sym ) )
{
Eligible = IsEligible( MinPrice, MinTurnover, TurnPeriod, MinBars );
SymRoc1 = ROC( Close, Look1 );
SymRoc2 = ROC( Close, Look2 );
SymRoc3 = ROC( Close, Look3 );
RestorePriceArrays();
// Excess return over the benchmark on each window, blended by the
// chosen weights. Positive means the symbol outpaced the
// benchmark over that window; it says nothing about what follows.
Blend = ( Weight1 * ( SymRoc1 - BenchRoc1 ) +
Weight2 * ( SymRoc2 - BenchRoc2 ) +
Weight3 * ( SymRoc3 - BenchRoc3 ) ) / WeightSum;
// Ineligible bars get Null rather than a number, and a symbol that
// is ineligible on every bar still gets a variable so that the
// self-check below can count it. Ranking behaviour for Null input
// is not documented, which is why the check exists.
Score = IIf( Eligible, Blend, Null );
StaticVarSet( InputPrefix + Sym, Score );
}
}
StaticVarGenerateRanks( OutputPrefix, InputPrefix, 0, TieMode );
// -------------------------------------------------------- self-check
HighestIssued = 0;
ScoredSymbols = 0;
for( i = 0; ( Sym = StrExtract( SymbolList, i ) ) != ""; i++ )
{
ThisScore = StaticVarGet( InputPrefix + Sym );
ThisRank = StaticVarGet( OutputPrefix + InputPrefix + Sym );
HighestIssued = Max( HighestIssued, Nz( ThisRank ) );
ScoredSymbols = ScoredSymbols + ( NOT IsNull( ThisScore ) );
}
StaticVarSet( ScoredName, ScoredSymbols );
StaticVarSet( IssuedName, HighestIssued );
}
// ------------------------------------------------------------------- PASS 2
// Read once into ordinary variables and do the arithmetic on those.
OwnScore = StaticVarGet( InputPrefix + Name() );
OwnRank = StaticVarGet( OutputPrefix + InputPrefix + Name() );
Scored = StaticVarGet( ScoredName );
Issued = StaticVarGet( IssuedName );
// Recomputed locally from this symbol's own data - cheap, and it lets the
// table say why a symbol is absent from the ranking. The names differ from
// the ones inside the functions above, which are local to those functions.
SelfTurnover = Median( Close * Volume, TurnPeriod );
SelfLiquid = SelfTurnover >= MinTurnover;
SelfPriced = Close >= MinPrice;
SelfLongEnough = BarIndex() >= MinBars;
RankPercent = 100 * OwnRank / Max( Scored, 1 );
StatusText = WriteIf( NOT IsNull( OwnRank ), "ranked",
WriteIf( NOT SelfLiquid, "excluded: thin",
WriteIf( NOT SelfPriced, "excluded: low price",
WriteIf( NOT SelfLongEnough, "excluded: short history",
"excluded: no score" ) ) ) );
Filter = IIf( ShowAll, 1, NOT IsNull( OwnRank ) );
AddColumn( OwnRank, "Rank", 1.0 );
AddColumn( OwnScore, "Blended excess %", 1.2 );
AddColumn( RankPercent, "Rank as % of field", 1.1 );
AddColumn( SelfTurnover, "Median turnover", 1.0 );
AddColumn( Close, "Close", 1.2 );
AddColumn( Scored, "Symbols ranked", 1.0 );
AddColumn( Issued, "Highest rank issued", 1.0 );
AddTextColumn( StatusText, "Status" );
AddTextColumn( WriteIf( OwnRank <= TopCount, "TOP " + NumToStr( TopCount, 1.0 ), "" ),
"Selection" );
// Column 1 is the ticker, column 2 the date, so Rank is column 3.
SetSortColumns( 3 );

Download rs-ranking-tool.afl217 lines

The header and the parameters. Every threshold, weight and lookback is a Param(), so the tool can be re-pointed from the Analysis window’s Parameters button without editing code. MinBars is derived rather than entered: it is the longest lookback plus the turnover window, which is the shortest history that lets both the score and the liquidity test be defined on the same bar.

The two functions. IsLiquidEnough() and IsEligible() are declared with the function keyword and use local for their working variables. They read whichever price arrays are currently in force, which is what lets the same code run inside a SetForeign() block in pass one and on the symbol’s own data in pass two. That matters more than it looks: the rule that decides who is ranked and the text that explains why a symbol was excluded are then guaranteed to be the same rule.

The universe guard. Three branches on GetOption( "ApplyTo" ): 2 for a filter, 0 for all symbols, and Error() for anything else, which stops execution with a message rather than producing an empty table.

Pass one. Inside Status( "stocknum" ) == 0, so AmiBroker runs it on the first symbol, on one thread, and waits for it before launching the rest. It removes its own variables, reads the benchmark once with SetForeign() — checking the documented return value, and stopping with a message if the benchmark is missing — and computes the benchmark’s three rates of change. Then it loops the universe: switch to the symbol, test eligibility, compute three rates of change, restore the arrays, blend, blank the ineligible bars with Null, and store.

Note that the benchmark’s rates of change are computed once, outside the loop. Reading the benchmark 500 times would produce identical arrays 500 times at 500 times the cost.

The ranking call. Once, with topranks set to 0 for normal mode and the tie mode from the toggle.

The self-check. A second loop over the same universe, reading back from the store rather than from the database. It records the highest rank the function issued and how many symbols had a non-Null score, and stores both. This is cheap — it touches no price data at all — and it is what turns “the table looks reasonable” into “the table is consistent with the number of symbols that had scores”.

Pass two. Runs for every symbol. Reads its own score and rank, recomputes its own eligibility locally so the status text can explain an absence, computes the rank as a percentage of the field, and builds the table. SetSortColumns( 3 ) sorts by rank ascending, so rank 1 is the top row.

  • StaticVarGenerateRanks( "outputprefix", "inputprefix", topranks, tiemode ) — the ranking call. topranks = 0 is normal mode, in which every symbol gets its own rank array named outputprefix + inputprefix + symbol.
  • SetForeign( ticker, fixup = True, tradeprices = False ) — returns 1 if the ticker exists and 0 otherwise, leaving the arrays untouched on failure. Both the benchmark check and the per-symbol loop use that return value.
  • RestorePriceArrays( tradeprices = False ) — mandatory after every SetForeign(), including inside the loop body.
  • Status( "stocknum" ) — 0 on the first symbol of the run; the documented guard for once-per-run work.
  • GetOption( "ApplyTo" ), GetOption( "FilterIncludeWatchlist" ) — the Analysis window’s own settings, read natively.
  • Median( array, period ) — the liquidity measure.
  • WriteIf( EXPRESSION, "TRUE TEXT", "FALSE TEXT" ) — nested here to build one status string from several conditions.
Column What it is What it is for
Rank Position in the universe, 1 is strongest The answer
Blended excess % The score the rank came from Tells you whether rank 1 is strong or merely least weak
Rank as % of field Rank divided by symbols ranked Comparable across universes of different sizes
Median turnover The liquidity figure used Shows how close to the threshold a row is
Close Last price Sanity check against the price floor
Symbols ranked How many had a real score Self-check, same on every row
Highest rank issued Largest rank the function produced Self-check, same on every row
Status ranked, or why not Explains absences
Selection Marks the top N Convenience

The two self-check columns are the ones to look at first, before the ranks. They should hold the same value on every row, and the highest rank issued should equal the number of symbols ranked, or be a little smaller when tie mode 1224 has compressed the range.

Do all six steps once, in order. They take about twenty minutes and they are the difference between a tool and a decoration.

1. Prove the arithmetic on one symbol. Set the range to one recent bar and run on a watch list of six symbols. Pick a row. Read that symbol’s close on the bar and its close 63 bars earlier from its chart; compute the percentage change by hand. Do the same for the benchmark. Subtract. Repeat for 126 and 252 bars, average the three, and compare with the Blended excess column. It should match to the displayed precision.

2. Prove the ordering. Copy the Blended excess and Rank columns into a spreadsheet, sort your copy descending by score, and number the rows. Your numbering and the Rank column must agree. If they are reversed, your build ranks lowest-first and you should negate the stored score.

3. Prove the cleanup. Note StaticVarCount() in the inspector formula from the third lesson. Run the tool. Run it again. The count must be the same after the second run as after the first. If it grows, something is being written that is not being removed.

4. Prove the universe. Remove one symbol from the watch list and re-run. “Symbols ranked” must fall by exactly one, and the removed symbol must vanish from the table. If it is still there, you are reading stale static variables.

5. Prove the eligibility. Set the minimum turnover to an absurdly high value and re-run with “Every symbol” selected. Every row should now read “excluded: thin”, and “Symbols ranked” should be zero. Set it back.

6. Prove the history handling. Add a recently listed symbol with less than a year of data. It should appear as “excluded: short history” and take no rank. Then shorten the longest lookback below its history and confirm it joins the ranking.

  • Every symbol excluded as “thin”. The turnover threshold is in the currency units of Close * Volume in your database. A database of index-level or foreign-currency data needs a different number, not a different formula.
  • The formula stops with “Benchmark symbol is not in this database”. That is the check working. Fix the ticker in the parameters, or add the symbol.
  • Every rank is Null although scores exist. The read-back name is wrong. It is OutputPrefix + InputPrefix + Name(), both prefixes.
  • Highest rank issued exceeds symbols ranked. Stale static variables are joining the ranking, or something else in your AmiBroker is writing names that start with RsRankScore. Check with the inspector.
  • Ranks that stop part way back through history. A static array stores only the bars currently in use, so the Analysis range and QuickAFL decide how far back the ranking goes. Widen the range if you need more.
  • Ranks that look wrong only in the distant past. Everything in pass one is aligned to the bar grid of the first symbol processed. If that symbol has a short or gappy history, the whole ranking inherits it. Turn on “Pad and align all data to reference symbol” in Analysis settings — the manual notes it is off by default and warns that it can slightly change indicator values where there are data holes — and choose a reference symbol with complete history.
  • The run takes minutes. Pass one is doing 500 foreign reads and one ranking call; that part is unavoidable. What is avoidable is running it on every symbol: check that the Status( "stocknum" ) == 0 guard is present and is not inside an #include file, where the documentation states it will not be detected.
  • Different results on Standard and Professional. The results should be identical. The editions differ only in how many threads an Analysis window may use — 2 against 32 — so they differ in how long the run takes. If the numbers differ, you have a synchronisation problem: something is writing static variables outside the guard.

The most natural extension is to change what is being ranked. Instead of ranking shares against a market index, rank sectors against it. Build one symbol per sector — a sector index if your data source provides one, a sector ETF if not, or a composite you construct yourself with the techniques in Part 16 — put them in a watch list, and run the same tool over that list with the market index as the benchmark.

The output is a sector strength table. Two honest cautions before you read anything into it. First, sector proxies are not sectors: an ETF has its own weighting, its own fees and its own liquidity, and a composite you build has whatever survivorship properties your database has. Second, a sector ranking has a very small effective sample size — ten or eleven sectors, changing slowly — so a pattern you notice in it is supported by far less independent evidence than the number of bars suggests. Part 30 is about exactly this trap.

A second version worth building: rank symbols within their own sector rather than across the whole market, by running the tool once per sector watch list, or by including SectorID( 1 ) as a text column and reading the table sector by sector. That answers “which are the strongest miners?” rather than “which are the strongest shares?”, and the two questions have different uses.

Divide the blended excess return by the symbol’s volatility over the same window — the standard deviation of daily returns, or ATR() as a fraction of price — before storing it. Ranking on return alone quietly ranks volatility as well, because a more volatile symbol covers more ground in either direction. Whether the risk-adjusted ranking is better is an empirical question you now have two tools to compare.

Store the rank array and report Ref( OwnRank, -20 ) and the change alongside today’s rank. A score whose ranking reshuffles completely every month is measuring something very short-lived, whatever window you computed it over. This is also the cheapest way to estimate how much turnover any selection built on the ranking would generate.

Write the day’s top N to a text static variable with StaticVarSetText(), or export the Exploration to a file with a dated name, and keep the record. A ranking tool that overwrites its own history can never be checked against what it actually said at the time. Building that habit now costs nothing and is the raw material for every out-of-sample question in Part 32.

The tool works, the ranks are verified, and the top row of the table is the strongest name in your universe on your measurement. It is worth being blunt about how far that is from something you could trade.

There is no entry. A rank says where a symbol stands, not when to act. Rank 1 today may have been rank 1 for six months.

There is no exit. Nothing in the tool says what happens when a holding falls to rank 40, or to rank 200, or is delisted.

There is no position sizing and no risk control. Twenty names ranked by momentum is a concentration decision made by accident. Part 34 is about making it on purpose.

There are no costs. Every ranking implies turnover, every turnover implies spread and commission, and short holding periods are where costs do the most damage. A ranking that looks strong before costs can be anything at all after them.

The universe is a decision. Ranking today’s index members over five years of history ranks a set chosen with hindsight. Part 30 explains why that is not a small effect.

The measurement is entirely backward-looking. The blended excess return describes what already happened. Whether the ordering it produces has any relationship to what happens next is a question, not a premise, and answering it needs the whole apparatus of Parts 27 to 33: a stated hypothesis, a point-in-time universe, a holding rule, a cost model, a comparison against the base rate of holding the universe unselected, and out-of-sample evidence.

You have built a two-pass cross-sectional tool: a guarded universe read, a single-threaded scoring pass, one ranking call, a self-check that reads its own output back, and a per-symbol reporting pass. The design choices — excess over a benchmark, three blended windows, eligibility before ranking, median turnover, a long private prefix, no persistence — are all visible in the formula and all changeable.

More importantly, you have a validation procedure that would catch the tool being wrong, and a clear statement of what the output is not. Every part after this one that ranks anything — portfolio backtesting with PositionScore in Part 28, rotational research in Part 35 — assumes you can produce a ranking you have checked. You can now.

Check your understanding

Question 1. Why does the formula compute the benchmark’s rates of change once, outside the symbol loop?
Show the answer and why

Answer: Because the arrays would otherwise be identical, recomputed once per symbol at 500 times the cost

Everything inside one AFL execution is aligned to that execution’s own symbol, so the benchmark arrays are the same on every iteration. Recomputing them inside the loop is pure waste, and every cross-symbol read takes a global lock.

Question 2. The tool reports 180 symbols ranked and a highest rank issued of 214. What should you do first?
Show the answer and why

Answer: Check for stale static variables matching the input prefix

More positions than scored symbols means the ranking function matched more variables than you wrote. That is a cleanup failure - a removal call without its asterisk, or a prefix that changed between runs.

Question 3. Which of these does the eligibility test deliberately do BEFORE the ranking? Select all that apply.
Show the answer and why

Answer: Exclude symbols with insufficient history for the longest lookback, Exclude symbols whose median turnover is below the threshold, Exclude symbols priced below the floor

Eligibility is absolute and self-contained, so it belongs before the comparison. Selecting the top N is a property of the ranking and can only happen after it.

Question 4. Running the same watch list on Standard and Professional editions gives different rank values. What does that indicate?
Show the answer and why

Answer: A synchronisation problem: something is writing static variables outside the run-once guard

The editions differ only in the thread limit per Analysis window, 2 against 32, so they change how long a run takes and not what it returns. Results that depend on thread count mean writes are happening outside the one-writer guard.

Question 5. What would have to be added before this tool could be described as a trading system?
Show the answer and why

Answer: Entry and exit rules, Position sizing and risk control, A cost model, A point-in-time universe and out-of-sample evidence

All four, and the last one is the one most often skipped. A rank is a description of a cross-section at a moment; converting a description into a decision procedure is the subject of Parts 27 to 34.

Sources for this lesson

7 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide - Ranking functionalityamibroker.com/guide/h_ranking.html2026-08-31
  2. 02AFL Function Reference - StaticVarGenerateRanksamibroker.com/guide/afl/staticvargenerateranks.html2026-08-31
  3. 03AFL Function Reference - StaticVarSetamibroker.com/guide/afl/staticvarset.html2026-08-31
  4. 04AFL Function Reference - SetForeignamibroker.com/guide/afl/setforeign.html2026-08-31
  5. 05AmiBroker User's Guide - Analysis settings§ Pad and align all data to reference symbolamibroker.com/guide/w_settings.html2026-08-31
  6. 06AmiBroker User's Guide - Efficient use of multithreadingamibroker.com/guide/h_multithreading.html2026-08-31
  7. 07AmiBroker User's Guide - Categories windowamibroker.com/guide/w_categories.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.