const rsi14 = calcRSI(close);
// -- MACD
function EMA(arr, period) {
const k = 2 / (period + 1);
let emaArr = [arr[0]];
for (let i = 1; i < [Link]; i++) {
[Link](arr[i] * k + emaArr[i - 1] * (1 - k));
}
return emaArr;
}
function calcMACD(closes) {
if ([Link] < 26) return { macd: null, signal: null };
const ema12 = EMA(closes, 12);
const ema26 = EMA(closes, 26);
const macdArr = [Link]((val, i) => val - (ema26[i] || 0));
const signalArr = EMA(macdArr, 9);
return {
macd: Number([Link](-1).toFixed(2)),
signal: Number([Link](-1).toFixed(2))
};
}
const macd = calcMACD(close);
// INPUT: Array as you posted above
const data = $[Link]().[Link][0];
const meta = [Link];
const close = [Link][0].close;
const open = [Link][0].open;
const high = [Link][0].high;
const low = [Link][0].low;
const volume = [Link][0].volume;
const timestamps = [Link];
// -- MA20 + slope
function SMA(arr, period) {
if ([Link] < period) return null;
return [Link](-period).reduce((a, b) => a + b, 0) / period;
}
const ma20 = Number(SMA(close, 20)?.toFixed(2));
const prev_ma20 = Number(SMA([Link](0, -1), 20)?.toFixed(2));
const slope_ma20 = (ma20 > prev_ma20) ? "Up" : (ma20 < prev_ma20) ? "Down" :
"Flat";
// -- Volatility (ATR%)
function avgATRpct(highs, lows, closes) {
let tr = [];
for (let i = 1; i < [Link]; i++) {
const hl = highs[i] - lows[i];
const hc = [Link](highs[i] - closes[i - 1]);
const lc = [Link](lows[i] - closes[i - 1]);
[Link]([Link](hl, hc, lc) / closes[i] * 100);
}
return Number(([Link]((a, b) => a + b, 0) / [Link]).toFixed(2));
}
const volatility = avgATRpct(high, low, close);
// -- Support/Resistance
const support = Number([Link](...low).toFixed(2));
const resistance = Number([Link](...high).toFixed(2));
// -- Trend
let trend = "Sideways";
if ([Link](-1) > ma20 && slope_ma20 === "Up") trend = "Up";
else if ([Link](-1) < ma20 && slope_ma20 === "Down") trend = "Down";
// -- Volume Trend (5-bar SMA)
function SMAvol(volumes, period = 5) {
if ([Link] < period) return null;
return [Link](-period).reduce((a, b) => a + b, 0) / period;
}
const smaVol5 = SMAvol(volume, 5);
const volTrend = [Link](-1) > smaVol5 ? "Up" : "Down";
// -- Key Pattern (last 30 bars)
function getKeyPattern(highs, lows) {
const bars = 30;
if ([Link] < bars || [Link] < bars) return "Not enough data";
const h = [Link](-bars), l = [Link](-bars);
let up = true, down = true;
for (let i = 1; i < bars; i++) {
if (h[i] <= h[i - 1] || l[i] <= l[i - 1]) up = false;
if (h[i] >= h[i - 1] || l[i] >= l[i - 1]) down = false;
}
if (up) return "Up-channel";
if (down) return "Down-channel";
return "Sideways consolidation";
}
const keyPattern = getKeyPattern(high, low);
// -- Volume Spike: any bar where volume >= 2 × avg volume
const avgVol = [Link]((a, b) => a + b, 0) / [Link];
const volSpikes = [];
for (let i = 0; i < [Link]; i++) {
if (volume[i] >= 2 * avgVol) {
[Link]({ bar: i, volume: volume[i], avg: avgVol });
}
}
const volSpikeText = [Link] > 0
? [Link](v => `Bar ${[Link]}: vol=${[Link]}, avg=$
{[Link]([Link])}`).join("; ")
: "None detected (All bars < 2 × avg volume)";
// -- Unusual Gap: any bar where |open − prev close| ÷ prev close ≥ 1%
const gapArr = [];
for (let i = 1; i < [Link]; i++) {
const gap = [Link](open[i] - close[i - 1]) / close[i - 1];
if (gap >= 0.01) [Link]({ index: i, gap: Number((gap * 100).toFixed(2)),
open: open[i], prevClose: close[i - 1] });
}
const gapText = [Link] > 0
? [Link](g => `Bar ${[Link]}: gap=${[Link]}%`).join("; ")
: "None detected (All opens within 1% of prior close)";
// -- Bias
let bias = "Neutral";
if (trend === "Up" && rsi14 <= 70 && [Link] > 0) bias = "Bullish";
if (trend === "Down" && rsi14 >= 30 && [Link] < 0) bias = "Bearish";
// -- Entry Price (last close)
const entryPrice = Number([Link](-1).toFixed(2));
// -- Stops/Targets
let stopLoss = null, takeProfit = null;
if (bias === "Bullish") {
stopLoss = Number((support - 0.005 * support).toFixed(2));
takeProfit = Number((resistance + 0.01 * resistance).toFixed(2));
} else if (bias === "Bearish") {
stopLoss = Number((support - 0.005 * support).toFixed(2));
takeProfit = Number((support - 0.01 * support).toFixed(2));
} else {
stopLoss = "N/A";
takeProfit = "N/A";
}
// -- Output object
return [{
json: {
Ticker: [Link],
Name: [Link],
Timeframe: "15m",
Period: "7d",
RSI_14: rsi14,
MACD: macd,
MA20: ma20,
MA20_slope: slope_ma20,
Volatility_ATR_pct: volatility,
Support: support,
Resistance: resistance,
Trend: trend,
Volume_Trend: volTrend,
Volume_SMA5: [Link](smaVol5),
KeyPattern: keyPattern,
Bias: bias,
Entry_Price: entryPrice,
Stop_Loss: stopLoss,
Take_Profit: takeProfit,
Vol_Spike: [Link] > 0 ? volSpikes : "None",
Unusual_Gap: [Link] > 0 ? gapArr : "None"
}
}];
// -- RSI(14)
function calcRSI(closes, period = 14) {
if ([Link] < period + 1) return null;
let gains = 0, losses = 0;
for (let i = [Link] - period; i < [Link]; i++) {
const diff = closes[i] - closes[i - 1];
if (diff > 0) gains += diff;
else losses -= diff;
}
let avgGain = gains / period, avgLoss = losses / period;
let rs = avgGain / (avgLoss || 1e-9);
let rsi = 100 - (100 / (1 + rs));
return Number([Link](1));
}