How It Works
The Golden Cross (50-day MA crosses above 200-day MA) is the most famous and widely-watched technical signal in all of trading.
Historical performance: Since 1950, Golden Crosses on the S&P 500 have preceded average rallies of 10-15% over the following 12 months. Death Crosses (the inverse) preceded average declines of 8-12%.
Why it works: Self-fulfilling prophecy. When every fund manager, algorithmic system, and retail trader watches the same signal, they all act on it together, creating the very move they predicted.
Note: This is a long-term trend strategy. It may only trigger 1-3 times per year. Patience is required.
Backtest Results
Backtested against real historical market data from Yahoo Finance.
| Symbol | Period | Return | Sharpe | Max Drawdown | Win Rate | Trades |
|---|---|---|---|---|---|---|
| QQQ | 2024-02-23 — 2026-02-23 | 10.72% | 0.67 | -7.54% | 100% | 1 |
| SPY | 2024-02-23 — 2026-02-23 | 9.87% | 0.82 | -4.84% | 100% | 1 |
Configuration
Source Code
#!/usr/bin/env node
// Golden/Death Cross Sniper — JC Trading Bots
// https://trading.jc.holdings/bot/golden-death-cross-sniper
// License: MIT | Free forever | Modify however you want
//
// DISCLAIMER: Not financial advice. Past performance does not guarantee
// future results. Trading involves substantial risk of loss.
// Use at your own risk. No guarantees of profit.
//
// Usage:
// node golden-death-cross-sniper.js # Print signals
// ALPACA_KEY=x ALPACA_SECRET=y node golden-death-cross-sniper.js # Paper trade
// ALPACA_KEY=x ALPACA_SECRET=y LIVE=1 node golden-death-cross-sniper.js # REAL money
// ─── Configuration ─────────────────────────────────────────
const CONFIG = {
"fastPeriod": 50,
"slowPeriod": 200,
"positionPct": 95
};
const SYMBOLS = ["SPY"];
// ─── Technical Indicators ───────────────────────────────────
function sma(data, period) {
if (data.length < period) return null;
const slice = data.slice(-period);
return slice.reduce((a, b) => a + b, 0) / period;
}
function ema(data, period) {
if (data.length < period) return null;
const k = 2 / (period + 1);
let val = sma(data.slice(0, period), period);
for (let i = period; i < data.length; i++) {
val = data[i] * k + val * (1 - k);
}
return val;
}
function rsi(closes, period = 14) {
if (closes.length < period + 1) return 50;
let gains = 0, losses = 0;
for (let i = closes.length - period; i < closes.length; i++) {
const diff = closes[i] - closes[i - 1];
if (diff > 0) gains += diff;
else losses -= diff;
}
if (losses === 0) return 100;
const rs = (gains / period) / (losses / period);
return 100 - (100 / (1 + rs));
}
function bollingerBands(closes, period = 20, mult = 2) {
const mid = sma(closes.slice(-period), period);
if (!mid) return { upper: null, mid: null, lower: null };
const slice = closes.slice(-period);
const variance = slice.reduce((sum, v) => sum + Math.pow(v - mid, 2), 0) / period;
const std = Math.sqrt(variance);
return { upper: mid + mult * std, mid, lower: mid - mult * std };
}
function atr(candles, period = 14) {
if (candles.length < period + 1) return 0;
let sum = 0;
for (let i = candles.length - period; i < candles.length; i++) {
const tr = Math.max(
candles[i].high - candles[i].low,
Math.abs(candles[i].high - candles[i - 1].close),
Math.abs(candles[i].low - candles[i - 1].close)
);
sum += tr;
}
return sum / period;
}
function macd(closes, fast = 12, slow = 26, sig = 9) {
if (closes.length < slow + sig) return { macd: 0, signal: 0, histogram: 0 };
const macdSeries = [];
for (let i = slow; i <= closes.length; i++) {
const slice = closes.slice(0, i);
const f = ema(slice, fast);
const s = ema(slice, slow);
if (f !== null && s !== null) macdSeries.push(f - s);
}
if (macdSeries.length < sig) return { macd: 0, signal: 0, histogram: 0 };
const macdLine = macdSeries[macdSeries.length - 1];
const signalLine = ema(macdSeries, sig);
return { macd: macdLine, signal: signalLine || 0, histogram: macdLine - (signalLine || 0) };
}
// ─── Data Fetching (Yahoo Finance) ──────────────────────────
async function fetchCandles(symbol, range = "1y", interval = "1d") {
const url = "https://query1.finance.yahoo.com/v8/finance/chart/" + symbol
+ "?range=" + range + "&interval=" + interval + "&includePrePost=false";
const res = await fetch(url, {
headers: { "User-Agent": "Mozilla/5.0 JCTradingBot/1.0" }
});
const json = await res.json();
const result = json.chart.result[0];
const ts = result.timestamp;
const q = result.indicators.quote[0];
const candles = [];
for (let i = 0; i < ts.length; i++) {
if (q.close[i] == null) continue;
candles.push({
timestamp: ts[i] * 1000,
date: new Date(ts[i] * 1000).toISOString().split("T")[0],
open: q.open[i],
high: q.high[i],
low: q.low[i],
close: q.close[i],
volume: q.volume[i] || 0,
});
}
return candles;
}
// ─── Alpaca Integration (optional) ──────────────────────────
// Set ALPACA_KEY and ALPACA_SECRET env vars to enable trading.
// Add LIVE=1 for real money (default is paper trading).
async function alpacaTrade(symbol, action, reason) {
const key = process.env.ALPACA_KEY;
const secret = process.env.ALPACA_SECRET;
if (!key || !secret) {
console.log("[DRY RUN] " + action.toUpperCase() + " " + symbol + " — " + reason);
return;
}
const baseUrl = process.env.LIVE === "1"
? "https://api.alpaca.markets"
: "https://paper-api.alpaca.markets";
// Get account buying power
const acct = await fetch(baseUrl + "/v2/account", {
headers: { "APCA-API-KEY-ID": key, "APCA-API-SECRET-KEY": secret }
}).then(r => r.json());
if (action === "buy") {
const budget = parseFloat(acct.buying_power) * 0.9;
if (budget < 10) { console.log("Insufficient funds: $" + acct.buying_power); return; }
const price = await fetch(baseUrl + "/v2/stocks/" + symbol + "/quotes/latest", {
headers: { "APCA-API-KEY-ID": key, "APCA-API-SECRET-KEY": secret }
}).then(r => r.json());
const qty = Math.floor(budget / (price.quote?.ap || 999999));
if (qty < 1) { console.log("Price too high for budget"); return; }
const order = await fetch(baseUrl + "/v2/orders", {
method: "POST",
headers: { "APCA-API-KEY-ID": key, "APCA-API-SECRET-KEY": secret, "Content-Type": "application/json" },
body: JSON.stringify({ symbol, qty: String(qty), side: "buy", type: "market", time_in_force: "day" })
}).then(r => r.json());
console.log("ORDER PLACED: BUY " + qty + " " + symbol + " — " + reason);
console.log("Order ID: " + order.id);
} else if (action === "sell") {
// Close position
const close = await fetch(baseUrl + "/v2/positions/" + symbol, {
method: "DELETE",
headers: { "APCA-API-KEY-ID": key, "APCA-API-SECRET-KEY": secret }
}).then(r => r.json());
console.log("POSITION CLOSED: " + symbol + " — " + reason);
}
}
const BOT_NAME = "Golden/Death Cross Sniper";
const DATA_RANGE = "2y";
function evaluate(candles, config) {
const closes = candles.map(c => c.close);
const fast = sma(closes, config.fastPeriod || 50);
const slow = sma(closes, config.slowPeriod || 200);
if (!fast || !slow) return { action: "hold", reason: "Need 200+ days of data" };
const prevCloses = closes.slice(0, -1);
const prevFast = sma(prevCloses, config.fastPeriod || 50);
const prevSlow = sma(prevCloses, config.slowPeriod || 200);
const price = closes[closes.length - 1];
if (fast > slow && prevFast <= prevSlow) {
return { action: "buy", reason: "GOLDEN CROSS! 50-day MA (" + fast.toFixed(2) + ") crossed above 200-day MA (" + slow.toFixed(2) + "). Historically +10-15% over 12 months." };
}
if (fast < slow && prevFast >= prevSlow) {
return { action: "sell", reason: "DEATH CROSS! 50-day MA (" + fast.toFixed(2) + ") crossed below 200-day MA (" + slow.toFixed(2) + "). Historically -8-12%." };
}
const spread = ((fast - slow) / slow * 100).toFixed(2);
return { action: "hold", reason: "50d MA: " + fast.toFixed(2) + " | 200d MA: " + slow.toFixed(2) + " | Spread: " + spread + "% | " + (fast > slow ? "BULLISH" : "BEARISH") };
}
// ─── Main ───────────────────────────────────────────────────
async function run() {
console.log("\n" + "=".repeat(60));
console.log(" " + BOT_NAME);
console.log(" " + new Date().toISOString());
console.log("=".repeat(60) + "\n");
for (const symbol of SYMBOLS) {
try {
console.log("Fetching data for " + symbol + "...");
const candles = await fetchCandles(symbol, DATA_RANGE);
console.log("Got " + candles.length + " candles (" + candles[0].date + " to " + candles[candles.length-1].date + ")");
const signal = evaluate(candles, CONFIG);
const icon = signal.action === "buy" ? "BUY" : signal.action === "sell" ? "SELL" : "HOLD";
console.log("\n " + symbol + ": " + icon + " — " + signal.reason);
if (signal.action !== "hold") {
await alpacaTrade(symbol, signal.action, signal.reason);
}
console.log("");
} catch (err) {
console.error("Error on " + symbol + ": " + err.message);
}
}
}
run().catch(err => { console.error(err); process.exit(1); });Full source code. MIT License. Free forever. Modify and deploy however you want.
Where to Run This Bot
TradingView
Charts, screeners, alerts & strategy backtesting. 30% off Premium — essential for any trader.
Open Account →ProfessionalInteractive Brokers
Low-cost global trading for stocks, options, futures. Professional-grade API.
Open Account →Prediction MarketsPolymarket
Prediction market trading. Sports, politics, crypto events. Where our arb bots operate.
Open Account →