Skip to content
Level 4 · Trading System ResearcherReality checkPart 28 · page 8 of 845 min
45Minutes
11AFL functions
5Sources
StandardRequires
AFL functions taught here11

Reality Check: Is the Golden Cross Worth Anything?

When the 50-day moving average crosses above the 200-day moving average, a major uptrend has begun. This is the golden cross. The death cross — the same lines crossing the other way — marks the start of a downtrend.

It is the most widely reported technical signal in existence. It gets its own headlines in the financial press. It is also, as stated, untestable: “a major uptrend has begun” is not a claim with a truth value.

You now have every tool needed to fix that.

From a headline to something a backtest can answer

  1. The claim as stated"A major uptrend has begun." No universe, no holding period, no comparison, no costs. Nothing to test.
  2. Make the rule mechanicalBuy when MA(Close,50) crosses above MA(Close,200). Sell when it crosses back. Long only. No stops, no filters, no discretion.
  3. Fix the universe and periodA stated watch list, a liquidity floor, a stated date range. Write all three down before running anything.
  4. Choose a fair benchmarkPassive ownership of the SAME names over the SAME period paying the SAME costs. Not an index.
  5. Decide what would count as an answerBefore you look: what difference in Risk Adjusted Return, over what period, would change your mind?

The rule is easy to make mechanical, which is part of why it is so popular:

Fragment — not a complete formula

Buy = Cross( MA( Close, 50 ), MA( Close, 200 ) );
Sell = Cross( MA( Close, 200 ), MA( Close, 50 ) );

Note what this specification excludes, deliberately: no stops, no profit targets, no regime filter, no position-sizing cleverness. If we add those, we are testing “the golden cross plus four other ideas” and we will not know which part did the work.

Step 2: the benchmark question, which is where most tests go wrong

Section titled “Step 2: the benchmark question, which is where most tests go wrong”

A strategy result on its own is uninterpretable. It has to be compared with something. The question is what.

The obvious answer — compare against a major index — is wrong, and wrong in a way that is easy to miss. If your strategy trades thirty mid-cap shares and your benchmark is a large-cap index, then the difference between them measures the difference between two universes at least as much as it measures the strategy. Beat the index and you may have discovered nothing more than that mid-caps outperformed over your period.

The benchmark that isolates the rule is passive ownership of the very same names, over the very same period, paying the very same frictions.

Complete runnable AFL

equal-weight-benchmark.afl
// equal-weight-benchmark.afl
// Part 28 - Reality Check: Is the Golden Cross Worth Anything?
//
// The benchmark the golden-cross test has to beat: buy every eligible member of
// the SAME universe as early as it becomes eligible, in equal weight, pay the
// SAME costs, and hold to the end of the SAME range.
//
// WHY NOT JUST USE AN INDEX. Comparing a strategy that trades thirty mid-cap
// stocks against a large-cap index measures the difference between two
// universes at least as much as it measures the strategy. The only benchmark
// that isolates the RULE is passive ownership of the very same names over the
// very same period with the very same frictions.
//
// ============================ ASSUMPTIONS ==================================
// Universe Must be the identical watch list, range, periodicity and
// liquidity floor as golden-cross-portfolio.afl.
// Entry The first bar on which a symbol passes the liquidity floor
// and is inside the analysis range; filled at the next bar's
// open, with the same slippage.
// Exit None. The portfolio backtester closes any position still open
// at the end of the range, at the closing price.
// Weighting 100 / NamesHeld percent of portfolio equity per position.
// Symbols that become eligible later are bought at that same
// percentage of a LATER equity figure, so the weights are equal
// at purchase, not equal forever. There is no rebalancing.
// Costs Same commission and slippage as the strategy. Buy-and-hold
// pays them once each way; that asymmetry is the whole
// economic argument against trading, so do not remove it.
// Financing No interest, no dividends. Excluding dividends understates a
// buy-and-hold benchmark's return, and it understates the
// strategy's too whenever the strategy was holding. Say so
// whenever you quote either number.
// ===========================================================================
// --------------------------------------------------------- 1. the parameters
NamesHeld = 10; // set to the number of positions you want held
StartingEquity = 100000;
SlippagePercent = 0.05;
CommissionPercent = 0.10;
MinTurnover = 2000000;
MinPriceLevel = 2;
// ----------------------------------------------------------- 2. the account
SetOption( "InitialEquity", StartingEquity );
SetOption( "MaxOpenPositions", NamesHeld );
SetOption( "AllowPositionShrinking", True );
SetOption( "MinPosValue", 1000 );
SetOption( "AccountMargin", 100 );
SetOption( "InterestRate", 0 );
SetOption( "CommissionMode", 1 );
SetOption( "CommissionAmount", CommissionPercent );
RoundLotSize = 1;
SetBacktestMode( backtestRegular );
SetPositionSize( 100 / NamesHeld, spsPercentOfEquity );
SetTradeDelays( 1, 1, 1, 1 );
BuyPrice = Open * ( 1 + SlippagePercent / 100 );
SellPrice = Open * ( 1 - SlippagePercent / 100 );
ShortPrice = Open * ( 1 - SlippagePercent / 100 );
CoverPrice = Open * ( 1 + SlippagePercent / 100 );
// ----------------------------------------------------------- 3. the universe
Turnover = MA( Close * Volume, 50 );
Tradeable = Turnover >= MinTurnover
AND Close >= MinPriceLevel
AND Volume > 0;
// -------------------------------------------------------- 4. buy once, hold
// Eligible bars are those inside the analysis range that also pass the floor.
// The first of them, and only the first, produces a signal.
Eligible = Tradeable AND Status( "barinrange" );
BarsSoFar = Cum( Eligible );
Buy = Eligible AND Nz( Ref( BarsSoFar, -1 ), 0 ) == 0;
Sell = 0; // held to the end; the backtester closes it at the final close
// -------------------------------------------------------------- 5. the rank
// Identical to the strategy's rank, so that if the universe is larger than
// NamesHeld the same names are chosen by the same criterion.
PositionScore = Turnover;
// -------------------------------------------------------- 6. what to record
// The same list as the strategy: number of trades, Exposure %, Annual Return %,
// Risk Adjusted Return %, Max. system % drawdown, CAR/MaxDD, Avg. Bars Held.
// Exposure % here will be close to 100, and that is the point of recording it:
// a strategy that is in the market half the time is not directly comparable on
// return alone, which is exactly what Risk Adjusted Return % is for.

Download equal-weight-benchmark.afl89 lines

Buy each eligible member of the same universe as soon as it becomes eligible, in equal weight, and hold to the end of the range. The portfolio backtester closes anything still open at the final close.

Complete runnable AFL

golden-cross-portfolio.afl
// golden-cross-portfolio.afl
// Part 28 - Reality Check: Is the Golden Cross Worth Anything?
//
// The claim under test, made objective: buy when a symbol's FastPeriod-bar
// simple moving average of closing prices crosses above its SlowPeriod-bar
// simple moving average; sell when it crosses back below. Long only. No stops,
// no targets, no filters, no discretion, nothing else.
//
// Run it as a portfolio BACKTEST at the default 50/200 first. Then run it as an
// OPTIMIZATION over both periods to see the whole surface, because a claim that
// only survives at one pair of numbers is a claim about those numbers.
//
// ============================ ASSUMPTIONS ==================================
// Universe The watch list you point the Analysis window at. Write down
// how it was built. If it is today's index membership, the test
// is contaminated by survivorship and cannot be repaired here.
// Period The Range you set. Quote it every time you quote a result.
// Averages Simple, on closing prices, on daily bars. Neither average is
// defined until it has its full look-back, so no signal can
// occur in the first SlowPeriod bars of a symbol's history.
// Fill price Next bar's open, moved against us by SlippagePercent.
// Commission CommissionPercent of trade value, entry and exit.
// Sizing Equal weight across PosQty slots, from portfolio equity.
// Selection When candidates outnumber slots, the most liquid wins. This
// must match the benchmark formula exactly or the comparison is
// between two different universes.
// Financing No interest, no borrow, no dividends.
// ===========================================================================
//
// This produces one number from one history. It is not an expected return, it
// does not establish that the rule works, and it does not establish that it
// fails. It is one observation, and the lesson explains how much weight one
// observation can carry.
// --------------------------------------------------------- 1. the parameters
// Defaults are the popular specification. The ranges exist so that the same
// formula answers the far more interesting question: does anything special
// happen AT 50 and 200, or is the surface flat?
FastPeriod = Optimize( "Fast MA period", 50, 20, 90, 10 );
SlowPeriod = Optimize( "Slow MA period", 200, 100, 250, 25 );
PosQty = 10;
StartingEquity = 100000;
SlippagePercent = 0.05;
CommissionPercent = 0.10;
MinTurnover = 2000000;
MinPriceLevel = 2;
// ----------------------------------------------------------- 2. the account
SetOption( "InitialEquity", StartingEquity );
SetOption( "MaxOpenPositions", PosQty );
SetOption( "AllowPositionShrinking", True );
SetOption( "MinPosValue", 1000 );
SetOption( "AccountMargin", 100 );
SetOption( "InterestRate", 0 );
SetOption( "CommissionMode", 1 );
SetOption( "CommissionAmount", CommissionPercent );
RoundLotSize = 1;
SetBacktestMode( backtestRegular );
SetPositionSize( 100 / PosQty, spsPercentOfEquity );
SetTradeDelays( 1, 1, 1, 1 );
BuyPrice = Open * ( 1 + SlippagePercent / 100 );
SellPrice = Open * ( 1 - SlippagePercent / 100 );
ShortPrice = Open * ( 1 - SlippagePercent / 100 );
CoverPrice = Open * ( 1 + SlippagePercent / 100 );
// ----------------------------------------------------------- 3. the universe
// Identical to the benchmark formula, deliberately. A benchmark measured over a
// different set of symbols is not a benchmark.
Turnover = MA( Close * Volume, 50 );
Tradeable = Turnover >= MinTurnover
AND Close >= MinPriceLevel
AND Volume > 0;
// -------------------------------------------------------------- 4. the rule
FastMa = MA( Close, FastPeriod );
SlowMa = MA( Close, SlowPeriod );
Buy = Cross( FastMa, SlowMa ) AND Tradeable;
Sell = Cross( SlowMa, FastMa );
// A fast average that is not faster than the slow one describes nothing. Rather
// than let such combinations produce a meaningless row in the optimization
// table, silence them: they will appear with no trades at all.
if( SlowPeriod <= FastPeriod )
{
Buy = 0;
Sell = 0;
}
// -------------------------------------------------------------- 5. the rank
PositionScore = Turnover;
// -------------------------------------------------------- 6. what to record
// For the 50/200 run write down, from the report: number of trades, Exposure %,
// Annual Return %, Risk Adjusted Return %, Max. system % drawdown, CAR/MaxDD,
// Avg. Bars Held and Winners percentage. Then record the SAME list from the
// benchmark formula, over the same universe, range and costs. A single one of
// those numbers on its own settles nothing.

Download golden-cross-portfolio.afl102 lines

The periods are wrapped in Optimize() for step 5. At the defaults it is exactly the popular specification: 50 and 200.

One small piece of hygiene worth copying:

Fragment — not a complete formula

if( SlowPeriod <= FastPeriod )
{
Buy = 0;
Sell = 0;
}

A “fast” average that is not faster than the slow one describes nothing. Silencing those combinations keeps meaningless rows out of the optimization table rather than leaving you to spot them.

Step 4: run the pair and record everything

Section titled “Step 4: run the pair and record everything”

Run both formulas over the identical watch list, range, periodicity, costs and slot count. Record this list from each report:

Golden cross Equal-weight buy and hold
Number of trades
Exposure %
Annual Return %
Risk Adjusted Return %
Max. system % drawdown
CAR/MaxDD
Avg. Bars Held
Winners %
Total commissions paid

Here is the question that the headlines never ask: is there anything special about 50 and 200?

Run the strategy as an Optimization over both period ranges and look at the whole surface, not the best cell.

You are looking for one of three shapes:

A broad plateau. Neighbouring parameter pairs give similar results, and 50/200 sits somewhere inside a wide region that all behaves alike. This is the encouraging shape — it suggests you are looking at a property of trend-following in general rather than at an artefact of two numbers.

A single spike. 50/200 looks good and 40/180 and 60/220 do not. This is the discouraging shape, and it is what curve fitting looks like from the outside. A result that exists only at one parameter pair is a result about that pair.

A flat, uninteresting surface. Everything looks about the same, and about the same as the benchmark. This is the most common shape, and it is a perfectly good answer.

Step 6: read the evidence without wishful thinking

Section titled “Step 6: read the evidence without wishful thinking”

Four questions, in this order. Answer each in writing before moving to the next.

Did the strategy beat the matched benchmark on Risk Adjusted Return, after identical costs? Not on annual return. Not against an index. Against the same names with the same frictions.

By how much, relative to how noisy the comparison is? You have one number from one history. If the gap is a fraction of the year-to-year variation in either series, you cannot distinguish it from luck by looking at it.

Is the drawdown behaviour different? This one is often more interesting than the return. A trend rule that is out of the market during declines may deliver a similar return with a materially smaller maximum drawdown — and if so, the return comparison was the wrong question all along.

Would the conclusion survive doubling the costs? Run the sensitivity sweep from the costs lesson. A conclusion that flips inside your own cost uncertainty is not a conclusion.

Step 7: what this result does and does not license

Section titled “Step 7: what this result does and does not license”

It does not establish that the golden cross “works”. One period, one universe, one specification, one parameter pair, no dividends, survivorship in the universe, and a benchmark that is itself a choice.

It does not establish that it fails, either. A rule that matches a benchmark’s return with lower exposure and a smaller drawdown has done something, even if it did not beat the headline number.

It does say something about the popular version of the claim. The claim as reported — “a major uptrend has begun” — implies the signal identifies something exceptional. If your test shows the rule performing much like passive ownership of the same names, then whatever the crossing identifies, it is not exceptional enough to separate itself from simply owning the things.

And it says something about how the claim is normally presented. Every published golden-cross statistic you will read specifies the universe imprecisely, the costs not at all, and the benchmark rarely. You now know that all three change the answer.

The golden cross becomes testable the moment you replace “a major uptrend has begun” with a mechanical rule, a stated universe, a stated period, stated costs and a matched benchmark. The benchmark is the part most tests get wrong: comparing against an index measures the difference between universes, so the benchmark must be passive ownership of the same names paying the same frictions. Exposure is read before return, and Risk Adjusted Return is the figure built for comparing two systems that are in the market for different fractions of the time. The parameter surface tells you whether you have found a property or a coincidence. And after all of that you have one observation, which is exactly as much as one observation is worth.

Check your understanding

Question 1. Why is a major stock index a poor benchmark for a golden-cross strategy trading thirty mid-cap shares?
Show the answer and why

Answer: The comparison measures the difference between two universes at least as much as it measures the strategy

Beating the index might mean only that mid-caps outperformed large-caps over your period. Passive ownership of the same names over the same period with the same costs is the benchmark that isolates the rule.

Question 2. The benchmark formula charges the same commission and slippage as the strategy. Why not remove costs from the benchmark?
Show the answer and why

Answer: Because buy-and-hold pays them once each way while the strategy pays them on every round trip, and that asymmetry is the economic argument against trading

The cost difference between trading and holding is exactly what the comparison is meant to expose. Zeroing the benchmark's costs would hide the strategy's cost disadvantage rather than measuring it.

Question 3. Strategy: Exposure 62%, Annual Return 9%. Benchmark: Exposure 99%, Annual Return 11%. Which conclusions are sound? Select all that apply.
Show the answer and why

Answer: Risk Adjusted Return is the appropriate comparison because the exposures differ substantially, The strategy produced its return while out of the market more than a third of the time, which is a fact about the design worth stating, The drawdown comparison may be more informative than the return comparison

Comparing headline returns across very different exposures is the standard error in this exercise. A rule that is flat during declines can deliver a similar risk-adjusted result with a materially different drawdown profile, and that is often the interesting finding.

Question 4. The optimization shows 50/200 performing well while 40/180 and 60/220 perform poorly. What does that shape indicate?
Show the answer and why

Answer: A single spike rather than a plateau — the result exists only at one parameter pair, which is what curve fitting looks like from outside

Neighbouring parameters describe nearly the same rule, so they should behave nearly the same way. When they do not, the good cell is far more likely to be noise than to be a discovery — and reporting the best cell of dozens of tests compounds the problem.

Question 5. Which limitations apply to this test even when it is run carefully? Select all that apply.
Show the answer and why

Answer: Neither side receives dividends, which understates the buy-and-hold benchmark more than the strategy, The universe contains the symbols that exist today, so survivorship bias is present and cannot be removed from inside the formula, It produces one observation from one history

The first three are genuine and must be stated whenever a number is quoted. Optimizing afterwards does the opposite of making the result reliable: it adds dozens of tests on the same data, so the best of them looks good partly because it is the best of many.

Sources for this lesson

5 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — Portfolio-level backtestingamibroker.com/guide/h_portfolio.html2026-08-31
  2. 02AmiBroker User's Guide — Backtest report§ Risk Adjusted Return, Exposureamibroker.com/guide/w_report.html2026-08-31
  3. 03AFL Function Reference — Optimizeamibroker.com/guide/afl/optimize.html2026-08-31
  4. 04AFL Function Reference — MAamibroker.com/guide/afl/ma.html2026-08-31
  5. 05AFL Function Reference — Crossamibroker.com/guide/afl/cross.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.