Sectors and Intermarket Analysis
A stock that rose eight per cent last month while every one of its competitors rose nine has told you almost nothing about itself. This lesson is about placing an instrument inside the two contexts that usually explain most of what it did — its peer group and its market — and about the intermarket claims that get made when the same technique is pointed at unrelated asset classes.
The three-level comparison
Section titled “The three-level comparison”Comparing a stock with the broad market answers one question. Comparing it with its sector answers a second. Doing both at once answers a third that neither can reach alone.
Three ratios, three different questions
- Stock / marketDid this holding beat the obvious alternative?
- Stock / sectorDid this company do better than its competitors?
- Sector / marketWas the sector where the movement came from?
The combination is what makes it worth the extra Foreign() call. A stock strongly
ahead of the market and level with its sector was carried; the interesting object is
the sector, not the company. A stock level with the market but well ahead of its
sector did something its peers did not, in a sector that went nowhere. Those two
situations look identical on a stock-versus-market chart and call for completely
different follow-up questions.
Sector proxies and what they are not
Section titled “Sector proxies and what they are not”To compute “stock against sector” you need a number for the sector. Almost everyone uses a sector ETF or a published sector index, and almost no one states what that substitution costs.
A fund is an instrument, not a measurement. A sector ETF has its own supply and demand, its own liquidity, its own creation and redemption mechanics, and a price that can drift from the value of its holdings. On a quiet day the difference is negligible; on a disorderly one it is not, and disorderly days are usually the ones being studied.
Weighting decides what you are measuring. A capitalisation-weighted sector proxy is, in many sectors, mostly a chart of its two or three largest members. If your stock is one of them, “stock versus sector” is partly the stock versus itself, and the ratio is damped toward flat by construction. An equal-weighted proxy of the same sector answers a genuinely different question, and the two frequently disagree.
Constituents change. Index and fund providers add and remove members, and the proxy’s history reflects whoever was in it at the time — not whoever is in it now. That is the correct behaviour for a tradable fund and it is a survivorship consideration when you use its history as a research series.
Classification is a judgement. Whether a large retailer that sells most of its goods online belongs to retail or to technology is a decision someone made, and different providers made it differently. A stock’s sector is a property of the classification scheme, not of the company.
AmiBroker’s own category system
Section titled “AmiBroker’s own category system”AmiBroker classifies every symbol along four independent axes — market, group,
sector and industry — plus any number of watch lists. Each axis has one
reading function, and each takes the same mode argument:
Fragment — not a complete formula
SectorID(); // numeric sector idSectorID( 1 ); // sector NAME, as a stringIndustryID( 1 ); // industry nameMarketID( 1 ); // market nameGroupID( 1 ); // group namemode = 0, the default, returns the numeric identifier. mode = 1 returns the
name as a string. The return type changes with the argument, which is a small trap
worth naming: compare names to strings and identifiers to numbers, never the two
mixed.
Three further facts decide how you should use these in a course formula or a shared library.
The identifiers are ordinal positions, and positions move. The Categories window
has gained Move Up and Move Down buttons since version 6.10, and the documentation
notes that reordering forces symbols to be re-indexed because symbols refer to the
ordinal position of a category. A formula containing SectorID() == 7 therefore
means something different after somebody tidies the list. Prefer names, or resolve a
name to an identifier at run time.
The identifier ranges differ per axis. CategoryGetName() documents the valid
range as 0–255 for market, group and industry, but only 0–63 for sector. Watch
lists have no documented upper limit. A single generic loop written for “all
categories” will run off the end of the sector range.
There is no InSector(). Nor InGroup() or InIndustry() — those pages do not
exist in the function reference and the names are not in the official index. The
membership tests that do exist are InWatchList(), InWatchListName(), InGICS()
and InICB(), plus the property tests IsIndex(), IsFavorite() and
IsContinuous(). For the four ordinal axes, compare the identifier or the name:
Fragment — not a complete formula
InThisSector = SectorID( 1 ) == "Information Technology";Getting the members of a category
Section titled “Getting the members of a category”CategoryGetSymbols( category, index, mode = 0 ) returns a comma-separated string
of the symbols in a category. The category argument takes the documented constants
categoryMarket, categoryGroup, categorySector, categoryIndustry,
categoryWatchlist, categoryFavorite, categoryIndex, categoryGICS,
categoryICB and — from version 5.50 — categoryAll, meaning every symbol in the
database. mode is also new in 5.50: 0 returns tickers, 1 returns full names.
The result is one long string, parsed with StrExtract() in a loop. That loop, with
SetForeign() inside it, is how you would build a sector average yourself rather
than trusting a fund — and it is also the pattern that will make a formula slow if
you point it at categoryAll from a chart, for the reasons the previous lesson gave.
GICS and ICB
Section titled “GICS and ICB”If your data source supplies them, AmiBroker also carries the GICS and ICB
classifications, with GicsID( mode ), IcbID( mode ), InGICS( "code" ) and
InICB( "code" ). These behave differently from the ordinal axes in a way that
matters: their codes are hierarchical and fixed, so they do not shift when
anyone reorders a list, and a request for a parent code includes its children. The
CategoryGetName() page gives the example directly: asking for the symbols of GICS
code 10 returns everything in the energy sector including sub-codes such as
10101010 and 10102050.
If your database has GICS or ICB codes, prefer them for anything you intend to keep. If it does not, the ordinal axes are what you have, and the advice above about names over numbers is how you keep them from breaking.
Intermarket relationships and their instability
Section titled “Intermarket relationships and their instability”Intermarket analysis extends the sector idea outward: bonds against equities,
currencies against commodities, one country’s index against another’s. The technique
is the same Foreign() call. The epistemics are much harder, and the difference is
worth being explicit about.
A sector relationship has a mechanism you can state in a sentence: these companies sell similar things to similar customers and are exposed to similar costs, so their revenues move together. You can be wrong about how strong it is; you are not guessing about whether a connection exists.
Intermarket claims — “yields lead equities”, “the metal leads the miners”, “this currency pair leads that index” — usually arrive without a mechanism, and often without a stated window, direction or lag. What they arrive with instead is a chart of two lines that moved together over a period chosen after the fact.
The measurable part of such a claim is the correlation between the two series’ returns. The next formula measures it, and measures whether it stayed put.
Formula: correlation stability audit
Section titled “Formula: correlation stability audit”Take any two symbols and answer three questions at once: how strongly do their one-bar returns move together right now, how strongly did they move together at various points in the past, and how often has that number changed sign? A single correlation figure is a summary of one window. A picture of the same figure over time is evidence about whether the relationship is a relationship.
Complete formula
Section titled “Complete formula”Complete runnable AFL
// ===========================================================================// Correlation stability audit//// Measures how the relationship between this symbol and one other symbol has// moved over time, and shows two ways of measuring it that give very// different answers.//// HOW TO RUN// Apply Indicator to a new pane. Ctrl+R sets the partner symbol and the two// rolling windows.//// WHAT IT SHOWS// - Rolling correlation of ONE-BAR RETURNS, at a short and a long window.// This is the quantity most intermarket claims are implicitly about.// - Rolling correlation of PRICE LEVELS, drawn dashed. Two series that both// drift upwards tend to produce a large level correlation whether or not// anything connects them, which is why this line is here as a warning and// not as a measurement.// - How many times the short-window return correlation has changed sign.// A relationship that changes sign is not a stable relationship.//// WHAT IT DOES NOT SHOW// Nothing here identifies a mechanism, a direction of causation or a lead// and lag. A correlation is a summary of two columns of numbers over one// window. Reading a cause into it is a separate claim that needs separate// evidence.//// ASSUMPTIONS// - Foreign() aligns the partner to this symbol's bars. Bars the partner did// not trade are padded flat, which makes its return exactly zero on those// bars and pulls the measured correlation toward zero. The padded-bar// count is printed so the size of that effect is visible.// - Correlation() needs a full window before it returns anything, so the// left edge of the pane is empty by design.// ===========================================================================
_SECTION_BEGIN( "Correlation stability" );
PartnerSymbol = ParamStr( "Partner symbol", "^GSPC" );ShortWindow = Param( "Short window (bars)", 60, 10, 500, 5 );LongWindow = Param( "Long window (bars)", 250, 20, 2000, 10 );LookBack = Param( "Compare with the value this many bars ago", 250, 20, 2000, 10 );
PartnerClose = Foreign( PartnerSymbol, "C" ); // fixup 1: holes filled flatPartnerRaw = Foreign( PartnerSymbol, "C", 0 ); // fixup 0: holes stay Null
PartnerBars = LastValue( Cum( NOT IsNull( PartnerRaw ) ) );PaddedBars = LastValue( Cum( IsNull( PartnerRaw ) AND NOT IsNull( PartnerClose ) ) );
HomeReturn = ROC( Close, 1 );PartnerReturn = ROC( PartnerClose, 1 );
CorrShort = Correlation( HomeReturn, PartnerReturn, ShortWindow );CorrLong = Correlation( HomeReturn, PartnerReturn, LongWindow );CorrLevels = Correlation( Close, PartnerClose, LongWindow );
// A relationship that keeps crossing zero is not one relationship observed// repeatedly; it is a number that happens to be computable on every bar.SignChanges = LastValue( Cum( Cross( CorrShort, 0 ) OR Cross( 0, CorrShort ) ) );
CorrLongNow = LastValue( CorrLong );CorrLongThen = LastValue( Ref( CorrLong, -LookBack ) );CorrShortNow = LastValue( CorrShort );
Plot( CorrShort, StrFormat( "Return correlation, %g bars", ShortWindow ), colorBlue, styleLine );Plot( CorrLong, StrFormat( "Return correlation, %g bars", LongWindow ), colorSeaGreen, styleLine | styleThick );Plot( CorrLevels, StrFormat( "PRICE LEVEL correlation, %g bars - not a measurement", LongWindow ), colorGrey40, styleLine | styleDashed );PlotGrid( 0, colorLightGrey );PlotGrid( 0.5, colorLightGrey );PlotGrid( -0.5, colorLightGrey );
_N( Title = StrFormat( "%s against %s - correlation of one-bar returns\n", Name(), PartnerSymbol ) + StrFormat( "Now: %.2f over %g bars, %.2f over %g bars\n", CorrShortNow, ShortWindow, CorrLongNow, LongWindow ) + StrFormat( "The same %g-bar figure as it stood %g bars ago: %.2f\n", LongWindow, LookBack, CorrLongThen ) + StrFormat( "Sign changes in the %g-bar figure over the whole history: %g\n", ShortWindow, SignChanges ) + StrFormat( "Partner bars with real quotes: %g padded flat bars: %g\n", PartnerBars, PaddedBars ) + "The dashed line is the correlation of price levels. Two rising series " + "produce a high value there whether or not anything links them." );
_SECTION_END();How it works
Section titled “How it works”The partner symbol is read twice, once padded and once raw, for the same reason as in the previous lesson: the difference is the padded-bar count, and padded bars have a specific effect here that the title has to report. A padded bar carries the previous close, so the partner’s one-bar return on that bar is exactly zero, and a run of zeros pulls a measured correlation toward zero. On a pair with different holiday calendars this is not a rounding effect.
Two rolling windows are plotted, short and long, so that the reader can see the trade-off directly: the short window responds quickly and is noisy, the long window is stable and late. Neither is the true value, because there is no true value — there is a series of estimates over windows you chose.
The dashed line is the correlation of price levels rather than returns. It is drawn as a warning. Two series that both drift upwards over a decade will produce a large level correlation whether or not anything connects them, because both are mostly a function of time. Almost every casually quoted “correlation” between two markets is either this quantity or a chart that behaves like it.
The sign-change count is the blunt summary. A relationship whose measured correlation has crossed zero repeatedly is not one relationship being observed repeatedly; it is a number that can be computed on every bar.
Key functions
Section titled “Key functions”Correlation( array1, array2, periods )— the rolling correlation coefficient over the lastperiodsbars. It needs a full window before it returns anything, so the left edge of the pane is empty by design.ROC( array, periods = 1 )— rate of change in per cent. Used here to turn two price series into two return series before comparing them.Cross( array1, array2 )— true on the bar where the first crosses above the second. Used twice, in both directions, to count sign changes about zero.
Expected result
Section titled “Expected result”A pane with two solid lines wandering between −1 and +1 and a dashed line that usually sits much closer to +1 or −1 than either of them. The title gives the current short and long readings, the long reading as it stood a year ago, the number of sign changes, and the padded-bar count.
Test it
Section titled “Test it”- Set the partner symbol to the chart’s own symbol. Both solid lines should read 1.00 everywhere after the warm-up. Anything else means the two series being compared are not identical, which is itself worth investigating.
- Compare the current long-window reading with the same reading a year ago, printed on the third title line. Write down the difference. That difference is the quantity most intermarket claims omit.
- Halve and double both windows. If your conclusion about the pair changes, the conclusion was about the window.
- Note the padded-bar count. Then swap the two symbols — chart the partner and set the original as the partner — and note it again. They will often differ, for reasons the next lesson explains.
Common errors
Section titled “Common errors”| Symptom | Cause |
|---|---|
| Both lines empty | The window is longer than the available history, or the partner ticker is wrong |
| Correlation implausibly close to zero | A large padded-bar count: the partner’s return is zero on every manufactured bar |
| Dashed line near 1.00 quoted as “these markets are correlated” | That line is the level correlation, and it is in the formula as a counter-example |
| A confident reading from a 60-bar window on weekly data | Sixty weekly bars is over a year; the window length means different things per interval |
Extension
Section titled “Extension”Replace ROC( Close, 1 ) with a multi-bar return and see how the picture changes.
Correlations of overlapping multi-bar returns are inflated by the overlap, which is
a well-known effect and easy to reproduce here: compare the same pair using 1-bar
returns and 20-bar overlapping returns, and watch the second look far more
convincing than the first for no additional reason.
Correlation is not a mechanism
Section titled “Correlation is not a mechanism”Everything the previous formula produces is a description of two columns of numbers. Four things it cannot supply, and which any intermarket claim needs:
Direction. A correlation is symmetric. It says nothing about which series moved first, and adding a lag to the calculation does not fix that — it just adds another parameter you chose after looking.
Cause. Two series can move together because one drives the other, because a third thing drives both, because they share investors who rebalance on the same schedule, or because both are denominated in the same currency and you are partly measuring that currency.
Stability. Even where a connection is real, its strength moves. A number measured over 2010–2015 is a statement about 2010–2015.
Independence of the search. If you test a hundred pairs of instruments across several windows, some pairs will show strong correlations even when nothing connects them at all. The strength of the strongest result tells you very little unless you also state how many you looked at. This is the same multiplicity problem the course raised about optimisation, arriving in a different costume.
A stock sits inside a sector and a market, and comparing it with both at once separates its own contribution from its peer group’s. The sector proxy you use for that is a fund or an index with its own weighting, its own constituents and its own inception date — a substitution worth naming rather than assuming.
AmiBroker’s four ordinal category axes are read with MarketID(), GroupID(),
SectorID() and IndustryID(), all taking mode = 1 for names; their identifiers
are ordinal positions that move when categories are reordered, and sectors are
limited to 0–63 where the others run to 0–255. GICS and ICB codes, where your data
supplies them, are hierarchical and fixed and are the better key for anything
durable.
Intermarket relationships are measurable and unstable. Measure them over rolling windows, look at the sign changes, keep the level correlation firmly in the category of warning rather than evidence, and remember that a correlation never contained a mechanism to begin with.
Check your understanding
Sources for this lesson
8 verified · checked 2026-09-01
- 01AFL Function Reference — SectorIDamibroker.com/guide/afl/sectorid.html2026-08-31
- 02AFL Function Reference — IndustryIDamibroker.com/guide/afl/industryid.html2026-08-31
- 03AFL Function Reference — MarketIDamibroker.com/guide/afl/marketid.html2026-08-31
- 04AFL Function Reference — GroupIDamibroker.com/guide/afl/groupid.html2026-08-31
- 05AFL Function Reference — CategoryGetNameamibroker.com/guide/afl/categorygetname.html2026-08-31
- 06AFL Function Reference — CategoryGetSymbolsamibroker.com/guide/afl/categorygetsymbols.html2026-08-31
- 07AFL Function Reference — Correlationamibroker.com/guide/afl/correlation.html2026-08-31
- 08AmiBroker User's Guide — Categories window§ Rearranging categoriesamibroker.com/guide/w_categories.html2026-09-01
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.