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
| Parameter | Type | Default | Description |
|---|---|---|---|
| tenkanPeriod | number | 9 | Tenkan period |
| kijunPeriod | number | 26 | Kijun period |
| senkouBPeriod | number | 52 | Senkou B period |
| rsiPeriod | number | 14 | RSI period |
| rsiOverbought | number | 70 | RSI overbought level |
| rsiOversold | number | 30 | RSI 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.
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.