Trying to import a custom indicator. Keep getting Syntax Error

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”]

I hate to be the bearer of bad news but this does not look like Tradovate indicator code – as mentioned in a previous thread, I recommend starting with a working indicator that’s similar and tweak it slowly – most LLMs will not create solid Tradovate code out of the gates, they just guess poorly.