I have been trying for hours to import this custom indicator and i can not figure out what im doing wrong. Below you will find my script and the error I keep getting. Please can someone help!?
Syntax Errors
MyIndicator.js (447:1): Unexpected token (447:1)
// Precision Market Entropy Heatmap with Zones
// Originally by LuxAlgo (Pine Script v6)
// Licensed under CC BY-NC-SA 4.0 — Deed - Attribution-NonCommercial-ShareAlike 4.0 International - Creative Commons
// Ported for Tradovate custom indicator format
// ---------------------------------------------------------------------------
// Settings — edit these values to configure the indicator
// ---------------------------------------------------------------------------
var ROWS = 50; // Number of price rows per session profile
var MAX_SESSIONS = 10; // How many past sessions to show
var SHOW_DEV_POC = true; // Show developing POC line
var EXTEND_POCS = true; // Extend POC lines to current bar
var HEATMAP_ALPHA = 0.30; // Heatmap cell opacity (0 = invisible, 1 = solid)
// Heatmap gradient colours [R, G, B]
var COLOR_LOW = [0, 0, 51]; // #000033 dark blue
var COLOR_MID = [0, 255, 136]; // #00ff88 green
var COLOR_HIGH = [255, 255, 0]; // #ffff00 yellow
var COLOR_BULL = [8, 153, 129]; // #089981 teal (POC below price)
var COLOR_BEAR = [242, 54, 69]; // #f23645 red (POC above price)
var COLOR_DEV = [255, 255, 255]; // #ffffff white (developing POC)
// ---------------------------------------------------------------------------
// Colour helpers
// ---------------------------------------------------------------------------
function lerpColor(t, c0, c1) {
return [
Math.round(c0[0] + (c1[0] - c0[0]) * t),
Math.round(c0[1] + (c1[1] - c0[1]) * t),
Math.round(c0[2] + (c1[2] - c0[2]) * t)
];
}
function toRgba(rgb, alpha) {
return ‘rgba(’ + rgb[0] + ‘,’ + rgb[1] + ‘,’ + rgb[2] + ‘,’ + alpha.toFixed(3) + ‘)’;
}
function rowColor(intensity) {
var rgb;
if (intensity > 0.5) {
rgb = lerpColor((intensity - 0.5) * 2, COLOR_MID, COLOR_HIGH);
} else {
rgb = lerpColor(intensity * 2, COLOR_LOW, COLOR_MID);
}
return toRgba(rgb, HEATMAP_ALPHA);
}
function pocColor(pocPrice, closePrice) {
if (pocPrice < closePrice) {
return toRgba(COLOR_BULL, 1.0);
}
return toRgba(COLOR_BEAR, 1.0);
}
// ---------------------------------------------------------------------------
// Volume formatting
// ---------------------------------------------------------------------------
function fmtVol(v) {
if (v >= 1000000000) { return (v / 1000000000).toFixed(2) + ‘B’; }
if (v >= 1000000) { return (v / 1000000).toFixed(2) + ‘M’; }
if (v >= 1000) { return (v / 1000).toFixed(2) + ‘K’; }
return v.toFixed(2);
}
// ---------------------------------------------------------------------------
// Build a volume profile for one session from an array of bars
// Each bar: { open, high, low, close, volume, timestamp }
// ---------------------------------------------------------------------------
function buildProfile(bars, sessionStartTime) {
var i, j, d, sHigh, sLow, rowSize;
var isBull, idxH, idxL, span, vSplit;
var maxVol, peakIdx, pocPriceMin, pocPriceMax, pocPrice;
var rowVolumes, rowBullVol, rowBearVol, rows, intensity;
// 1. Find session high / low
sHigh = -Infinity;
sLow = Infinity;
for (i = 0; i < bars.length; i++) {
d = bars[i];
if (d.high > sHigh) { sHigh = d.high; }
if (d.low < sLow) { sLow = d.low; }
}
if (!isFinite(sHigh) || !isFinite(sLow) || sHigh <= sLow) {
return emptyProfile(sessionStartTime);
}
rowSize = (sHigh - sLow) / ROWS;
// 2. Accumulate volume into rows
rowVolumes = [];
rowBullVol = [];
rowBearVol = [];
for (i = 0; i < ROWS; i++) {
rowVolumes.push(0);
rowBullVol.push(0);
rowBearVol.push(0);
}
for (i = 0; i < bars.length; i++) {
d = bars[i];
if (d.high == null || d.low == null || d.volume == null) { continue; }
isBull = d.close >= d.open;
idxH = Math.min(ROWS - 1, Math.max(0, Math.floor((d.high - sLow) / rowSize)));
idxL = Math.min(ROWS - 1, Math.max(0, Math.floor((d.low - sLow) / rowSize)));
span = idxH - idxL + 1;
for (j = idxL; j <= idxH; j++) {
vSplit = d.volume / span;
rowVolumes[j] += vSplit;
if (isBull) {
rowBullVol[j] += vSplit;
} else {
rowBearVol[j] += vSplit;
}
}
}
// 3. Find POC (highest volume row)
maxVol = 0;
peakIdx = 0;
for (i = 0; i < ROWS; i++) {
if (rowVolumes[i] > maxVol) {
maxVol = rowVolumes[i];
peakIdx = i;
}
}
pocPriceMin = sLow + peakIdx * rowSize;
pocPriceMax = pocPriceMin + rowSize;
pocPrice = pocPriceMin + rowSize / 2;
// 4. Build row data array
rows = [];
for (i = 0; i < ROWS; i++) {
intensity = maxVol > 0 ? rowVolumes[i] / maxVol : 0;
rows.push({
volume: rowVolumes[i],
bullVol: rowBullVol[i],
bearVol: rowBearVol[i],
priceMin: sLow + i * rowSize,
priceMax: sLow + (i + 1) * rowSize,
intensity: intensity,
color: rowColor(intensity)
});
}
return {
startTime: sessionStartTime,
sessionHigh: sHigh,
sessionLow: sLow,
pocPrice: pocPrice,
pocPriceMin: pocPriceMin,
pocPriceMax: pocPriceMax,
peakVolume: maxVol,
peakBullVol: rowBullVol[peakIdx],
peakBearVol: rowBearVol[peakIdx],
rows: rows
};
}
function emptyProfile(startTime) {
var i, rows;
rows = ;
for (i = 0; i < ROWS; i++) {
rows.push({
volume: 0, bullVol: 0, bearVol: 0,
priceMin: NaN, priceMax: NaN,
intensity: 0, color: toRgba(COLOR_LOW, HEATMAP_ALPHA)
});
}
return {
startTime: startTime,
sessionHigh: NaN,
sessionLow: NaN,
pocPrice: NaN,
pocPriceMin: NaN,
pocPriceMax: NaN,
peakVolume: 0,
peakBullVol: 0,
peakBearVol: 0,
rows: rows
};
}
// ---------------------------------------------------------------------------
// Label text for a profile block
// ---------------------------------------------------------------------------
function profileLabel(profile, nowMs) {
var daysAgo, bPerc, sPerc, domTxt, volTxt;
daysAgo = Math.floor((nowMs - profile.startTime) / 86400000);
bPerc = profile.peakVolume > 0 ? (profile.peakBullVol / profile.peakVolume) * 100 : 0;
sPerc = profile.peakVolume > 0 ? (profile.peakBearVol / profile.peakVolume) * 100 : 0;
domTxt = bPerc >= sPerc
? Math.round(bPerc) + ‘% B’
: Math.round(sPerc) + ‘% S’;
volTxt = ‘Vol: ’ + fmtVol(profile.peakVolume) + ’ (’ + domTxt + ‘)’;
if (daysAgo === 0) {
return 'Current Profile | ' + volTxt;
}
return daysAgo + ' Days | ' + volTxt;
}
// ---------------------------------------------------------------------------
// State — session accumulator
// ---------------------------------------------------------------------------
var sessionBars = ;
var completedBlocks = ;
var activeBlock = null;
var lastSessionDay = null;
// ---------------------------------------------------------------------------
// Tradovate indicator entry point
// Called on every bar update by the platform
//
// bar: { open, high, low, close, volume, timestamp (ms) }
// draw: Tradovate drawing API { line, rect, text, … }
// ---------------------------------------------------------------------------
function map(bar, draw) {
var barDay = new Date(bar.timestamp).toDateString();
var isNewSession = (lastSessionDay !== null && barDay !== lastSessionDay);
// On new session boundary, finalise the previous session
if (isNewSession && sessionBars.length > 0) {
var finishedBlock = buildProfile(sessionBars, sessionBars[0].timestamp);
completedBlocks.push(finishedBlock);
// Trim to max sessions
while (completedBlocks.length > MAX_SESSIONS) {
completedBlocks.shift();
}
sessionBars = [];
activeBlock = null;
}
lastSessionDay = barDay;
sessionBars.push(bar);
// Build the live (developing) profile for the current session
activeBlock = buildProfile(sessionBars, sessionBars[0].timestamp);
// -----------------------------------------------------------------------
// Rendering — draw all profiles
// -----------------------------------------------------------------------
var allBlocks = completedBlocks.concat(activeBlock ? [activeBlock] : []);
var nowMs = bar.timestamp;
var closePrice = bar.close;
for (var b = 0; b < allBlocks.length; b++) {
var block = allBlocks[b];
if (!block || isNaN(block.pocPrice)) { continue; }
var isActive = (b === allBlocks.length - 1);
var pCol = pocColor(block.pocPrice, closePrice);
// --- Heatmap rows ---
for (var r = 0; r < block.rows.length; r++) {
var row = block.rows[r];
if (isNaN(row.priceMin)) { continue; }
draw.rect({
x: block.startTime,
y: row.priceMax,
width: isActive ? (nowMs - block.startTime) : null,
height: row.priceMax - row.priceMin,
fill: row.color,
stroke: 'transparent'
});
}
// --- POC zone (highlighted band) ---
draw.rect({
x: block.startTime,
y: block.pocPriceMax,
width: EXTEND_POCS || isActive ? (nowMs - block.startTime) : null,
height: block.pocPriceMax - block.pocPriceMin,
fill: pCol.replace('1.000', '0.200'),
stroke: pCol.replace('1.000', '0.700')
});
// --- POC midline ---
draw.line({
x1: block.startTime,
y1: block.pocPrice,
x2: nowMs,
y2: block.pocPrice,
color: pCol,
style: 'dashed',
width: 1
});
// --- POC label ---
draw.text({
x: nowMs,
y: block.pocPrice,
text: profileLabel(block, nowMs),
color: pCol,
size: 10
});
// --- Developing POC dot (active session only) ---
if (isActive && SHOW_DEV_POC) {
draw.line({
x1: block.startTime,
y1: block.pocPrice,
x2: nowMs,
y2: block.pocPrice,
color: toRgba(COLOR_DEV, 0.9),
style: 'solid',
width: 2
});
}
}
}
// ---------------------------------------------------------------------------
// Tradovate indicator descriptor
// This tells the platform the name, inputs, and entry function
// ---------------------------------------------------------------------------
var indicatorDescriptor = {
name: ‘Precision Market Entropy Heatmap’,
description: ‘Volume profile heatmap with POC zones per session. Port of LuxAlgo Pine Script.’,
calculator: map,
inputs: [
{ name: ‘rows’, type: ‘number’, defaultValue: 50, title: ‘Profile Rows’ },
{ name: ‘maxSessions’, type: ‘number’, defaultValue: 10, title: ‘Max Sessions’ },
{ name: ‘showDevPoc’, type: ‘boolean’, defaultValue: true, title: ‘Show Developing POC’ },
{ name: ‘extendPocs’, type: ‘boolean’, defaultValue: true, title: ‘Extend POCs’ }
],
plots:
};
module.exports = {name:‘my indicator13’, description: ‘My Indicator13’, calculator: MyIndicator, tags:[“> My Indicators”]