Golden Cross — Code Walkthrough
Understand the SMA calculation, why 50/200 periods are used, and how the Golden Cross and Death Cross crossover signals are detected.
Golden Cross — Code Walkthrough
The Golden Cross is one of the most well-known technical signals in finance. When the 50-day SMA crosses above the 200-day SMA, it signals a long-term bullish trend shift. Let's walk through every line of golden-cross-strategy.ts.
Step 1 — Configuration
export interface GoldenCrossConfig {
fastPeriod: number; // short-term SMA (default 50)
slowPeriod: number; // long-term SMA (default 200)
stopLossPct: number; // 0.10 = exit if price falls 10% from entry
positionSizePct: number; // 0.20 = allocate 20% of portfolio per trade
}
export const defaultConfig: GoldenCrossConfig = {
fastPeriod: 50,
slowPeriod: 200,
stopLossPct: 0.10,
positionSizePct: 0.20,
};50 and 200 are the canonical periods because they approximate 10-week and 40-week (roughly one trading year) timeframes. They're used by institutional investors as filters — many funds won't buy a stock trading below its 200-day SMA.
Step 2 — calculateSMA: Simple Moving Average
export function calculateSMA(closes: number[], period: number): number[] {
return closes.map((_, i) => {
if (i < period - 1) return NaN; // not enough history yet
// slice the window, sum it, divide by period
return closes.slice(i - period + 1, i + 1)
.reduce((a, b) => a + b, 0) / period;
});
}Performance Note
This SMA implementation is O(n × period) because it re-slices on every bar. For 200-period SMA over 5000 bars, that's 1,000,000 additions. A production system would use a running sum: add today's close, subtract the close that fell out of the window — O(n) total.
For a teaching tool and lab-scale backtests (< 10,000 bars), this is perfectly fine. The simplicity makes the logic transparent: you can read "slice from i-period+1 to i" and immediately understand the window.
Step 3 — generateSignals: Golden and Death Cross Detection
export function generateSignals(closes, config) {
const fast = calculateSMA(closes, config.fastPeriod); // 50-day SMA
const slow = calculateSMA(closes, config.slowPeriod); // 200-day SMA
return closes.map((_, i) => {
const smaFast = fast[i];
const smaSlow = slow[i];
// Skip until both SMAs have enough data (first 200 bars)
if (isNaN(smaFast) || isNaN(smaSlow) || i === 0) {
return { index: i, signal: 0, smaFast, smaSlow };
}
// Golden Cross: 50-day crossed ABOVE 200-day (yesterday it was below)
const goldenCross = fast[i - 1] < slow[i - 1] && smaFast > smaSlow;
// Death Cross: 50-day crossed BELOW 200-day (yesterday it was above)
const deathCross = fast[i - 1] > slow[i - 1] && smaFast < smaSlow;
const signal = goldenCross ? 1 : deathCross ? -1 : 0;
return { index: i, signal, smaFast, smaSlow };
});
}| Condition | Signal | Interpretation |
|---|---|---|
| 50 SMA crosses above 200 SMA | +1 (BUY) | Golden Cross — long-term bullish shift |
| 50 SMA crosses below 200 SMA | -1 (SELL) | Death Cross — long-term bearish shift |
| No crossover this bar | 0 (HOLD) | Trend continues, no action needed |
| Either SMA is NaN | 0 (HOLD) | Insufficient history, skip bar |
Signal Frequency
Golden/Death Crosses are rare — on daily SPY data you might see only 3–6 per year. This is a long-term trend strategy. Expect low trade frequency, but when it fires, the position is held for months.
- SMA is the simplest average: sum of last N closes divided by N
- Golden Cross (50 SMA crosses above 200 SMA) is a widely-followed bullish signal
- Death Cross (50 SMA crosses below 200 SMA) is the bearish counterpart
- The strategy needs 200 bars of data before it can generate any signal