VaultCharts
Trading Strategystocksforexcrypto

Ichimoku Cloud Strategy

Ichimoku trend (price vs cloud, TK cross) with RSI filter. Long above cloud + bullish TK + RSI<70; short below cloud + bearish TK + RSI>30.

What is Ichimoku Cloud?

Trend and momentum from the Ichimoku cloud; RSI as complement to avoid overbought/oversold entries. Long: price above cloud, Tenkan > Kijun (or price above Kijun), Chikou above price (or recent), and RSI not overbought. Short: price below cloud, Tenkan < Kijun, RSI not oversold. Exits when price crosses cloud or TK flips or RSI extreme.

Strategy Parameters

ParameterTypeDefaultDescription
tenkanPeriodnumber9Tenkan period
kijunPeriodnumber26Kijun period
senkouBPeriodnumber52Senkou B period
rsiPeriodnumber14RSI period
rsiOverboughtnumber70RSI overbought level
rsiOversoldnumber30RSI oversold level

Use Cases

  • Ichimoku trend with RSI filter
  • Above/below cloud bias
  • Tenkan-Kijun cross
  • Stocks, forex, crypto

Strategy Script (JavaScript)

This strategy runs in VaultCharts using the built-in strategy engine. Below is the full script in a readable format. You can copy it or run it directly in VaultCharts.

strategy.jsVaultCharts built-in
module.exports = {
  meta: {
    name: "Ichimoku Cloud",
    params: {
      tenkanPeriod: { type: "number", default: 9 },
      kijunPeriod: { type: "number", default: 26 },
      senkouBPeriod: { type: "number", default: 52 },
      rsiPeriod: { type: "number", default: 14 },
      rsiOverbought: { type: "number", default: 70 },
      rsiOversold: { type: "number", default: 30 }
    }
  },
  compute: (data, params, utils) => {
    const cleanData = data.filter(d =>
      d && Number.isFinite(d.high) && Number.isFinite(d.low) && Number.isFinite(d.close) &&
      Number.isFinite(d.time) && d.high >= d.low && d.close > 0
    );
    const tPeriod = params?.tenkanPeriod ?? 9;
    const kPeriod = params?.kijunPeriod ?? 26;
    const sBPeriod = params?.senkouBPeriod ?? 52;
    const rsiPeriod = params?.rsiPeriod ?? 14;
    const rsiOB = params?.rsiOverbought ?? 70;
    const rsiOS = params?.rsiOversold ?? 30;

    const shift = kPeriod;
    const minLen = sBPeriod + shift + 5;
    if (!cleanData || cleanData.length < minLen) return { signals: [] };

    const { technicalindicators: TI } = utils;
    if (!TI || !TI.RSI) return { signals: [] };

    const highs = cleanData.map(d => d.high);
    const lows = cleanData.map(d => d.low);
    const closes = cleanData.map(d => d.close);
    const rsi = TI.RSI.calculate({ values: closes, period: rsiPeriod });

    const tenkan = [];
    const kijun = [];
    for (let i = 0; i < cleanData.length; i++) {
      const th = i >= tPeriod - 1 ? Math.max(...highs.slice(i - tPeriod + 1, i + 1)) : highs[i];
      const tl = i >= tPeriod - 1 ? Math.min(...lows.slice(i - tPeriod + 1, i + 1)) : lows[i];
      tenkan.push((th + tl) / 2);
      const kh = i >= kPeriod - 1 ? Math.max(...highs.slice(i - kPeriod + 1, i + 1)) : highs[i];
      const kl = i >= kPeriod - 1 ? Math.min(...lows.slice(i - kPeriod + 1, i + 1)) : lows[i];
      kijun.push((kh + kl) / 2);
    }
    const senkouA = [];
    const senkouB = [];
    for (let i = 0; i < cleanData.length; i++) {
      const prev = i - shift;
      senkouA.push(prev >= 0 ? (tenkan[prev] + kijun[prev]) / 2 : null);
      const mid = prev - (sBPeriod - 1);
      if (mid >= 0) {
        const h = Math.max(...highs.slice(mid, prev + 1));
        const l = Math.min(...lows.slice(mid, prev + 1));
        senkouB.push((h + l) / 2);
      } else {
        senkouB.push(null);
      }
    }

    const signals = [];
    let position = null;

    for (let i = minLen; i < cleanData.length; i++) {
      const candle = cleanData[i];
      const c = candle.close;
      const cloudTop = Math.max(senkouA[i] != null ? senkouA[i] : -Infinity, senkouB[i] != null ? senkouB[i] : -Infinity);
      const cloudBottom = Math.min(senkouA[i] != null ? senkouA[i] : Infinity, senkouB[i] != null ? senkouB[i] : Infinity);
      const aboveCloud = c > cloudTop;
      const belowCloud = c < cloudBottom;
      const tenkanVal = tenkan[i];
      const kijunVal = kijun[i];
      const tkBull = tenkanVal > kijunVal;
      const tkBear = tenkanVal < kijunVal;
      const rsiVal = i < rsi.length ? rsi[i] : 50;
      const rsiOkLong = Number.isFinite(rsiVal) && rsiVal < rsiOB;
      const rsiOkShort = Number.isFinite(rsiVal) && rsiVal > rsiOS;

      if (position === 'long') {
        if (belowCloud || tkBear || (Number.isFinite(rsiVal) && rsiVal > rsiOB)) {
          signals.push({ type: "exit", direction: "long", time: candle.time, price: c, index: i });
          position = null;
        }
        continue;
      }

      if (position === 'short') {
        if (aboveCloud || tkBull || (Number.isFinite(rsiVal) && rsiVal < rsiOS)) {
          signals.push({ type: "exit", direction: "short", time: candle.time, price: c, index: i });
          position = null;
        }
        continue;
      }

      if (!position && aboveCloud && tkBull && rsiOkLong) {
        signals.push({ type: "entry", direction: "long", time: candle.time, price: c, index: i });
        position = 'long';
      }
      if (!position && belowCloud && tkBear && rsiOkShort) {
        signals.push({ type: "entry", direction: "short", time: candle.time, price: c, index: i });
        position = 'short';
      }
    }
    return { signals };
  }
};

Run Ichimoku Cloud in VaultCharts

VaultCharts includes this strategy as a built-in option. Backtest it, adjust parameters, and use it on your own data—all stored locally on your device.

Related Strategies

Explore More