← All Bots

NVDA Triple EMA Trend

FREEOpen SourcestockMIT License

Triple EMA trend-following strategy for NVIDIA. Enters when fast, medium, and slow EMAs align bullish.

2Downloads
4Views
20.67%Best Return
0.9Sharpe Ratio

How It Works

Uses three Exponential Moving Averages (8, 21, 55 periods) to confirm trend direction. Only enters when all three align: fast > medium > slow = bullish trend. This triple confirmation reduces false signals compared to simple dual-MA crossovers. NVDA's strong trending behavior makes it an excellent candidate for this approach.

Deploy NVDA Triple EMA Trend Live

Download the bot, connect TradingView for charts, and use Alpaca or IBKR to execute trades.

Free charts · Paper trading available · Takes 5 minutes

Backtest Results

Backtested against real historical market data from Yahoo Finance.

SymbolPeriodReturnSharpeMax DrawdownWin RateTrades
NVDA2025-02-24 — 2026-02-2320.67%0.9-24.25%66.67%3

Configuration

fast8
medium21
slow55
positionPct90
Symbols: NVDA

Source Code

#!/usr/bin/env node
// NVDA Triple EMA Trend — JC Trading Bots
// https://trading.jc.holdings/bot/nvda-triple-ema-trend-xy9p
// 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 nvda-triple-ema-trend-xy9p.js                    # Print signals
//   ALPACA_KEY=x ALPACA_SECRET=y node nvda-triple-ema-trend-xy9p.js  # Paper trade
//   ALPACA_KEY=x ALPACA_SECRET=y LIVE=1 node nvda-triple-ema-trend-xy9p.js  # REAL money

// ─── Configuration ─────────────────────────────────────────
const CONFIG = {
  "fast": 8,
  "medium": 21,
  "slow": 55,
  "positionPct": 90
};
const SYMBOLS = ["NVDA"];

// ─── 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 = "NVDA Triple EMA Trend Follower";
const DATA_RANGE = "1y";

function evaluate(candles, config) {
  const closes = candles.map(c => c.close);
  const fast = ema(closes, config.fastPeriod || 8);
  const mid = ema(closes, config.midPeriod || 21);
  const slow = ema(closes, config.slowPeriod || 55);
  const price = closes[closes.length - 1];
  if (!fast || !mid || !slow) return { action: "hold", reason: "Not enough data" };

  const bullish = fast > mid && mid > slow;
  const bearish = fast < mid && mid < slow;

  // Check alignment just changed
  const prevCloses = closes.slice(0, -1);
  const pf = ema(prevCloses, config.fastPeriod || 8);
  const pm = ema(prevCloses, config.midPeriod || 21);
  const ps = ema(prevCloses, config.slowPeriod || 55);
  const wasBullish = pf > pm && pm > ps;
  const wasBearish = pf < pm && pm < ps;

  if (bullish && !wasBullish) {
    return { action: "buy", reason: "Triple EMA aligned BULLISH. Fast " + fast.toFixed(2) + " > Mid " + mid.toFixed(2) + " > Slow " + slow.toFixed(2) };
  }
  if (bearish && !wasBearish) {
    return { action: "sell", reason: "Triple EMA aligned BEARISH. Fast " + fast.toFixed(2) + " < Mid " + mid.toFixed(2) + " < Slow " + slow.toFixed(2) };
  }
  return { action: "hold", reason: "EMA Fast:" + fast.toFixed(2) + " Mid:" + mid.toFixed(2) + " Slow:" + slow.toFixed(2) + " | " + (bullish ? "BULLISH" : bearish ? "BEARISH" : "MIXED") };
}

// ─── 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); });
Download .js File

Full source code. MIT License. Free forever. Modify and deploy however you want.

Where to Run This Bot

Compare All Brokers →

Deploy this bot →Get TradingView Free