1/* chart.js â Animated TradingView-style chart mock with live-feel candles + overlays. 2 All drawn to <canvas>; overlay toggles come in via props. */ 3 4var h = React.createElement; 5 6function mulberry32(a) { 7 return function() { 8 var t = a += 0x6D2B79F5; 9 t = Math.imul(t ^ t >>> 15, t | 1); 10 t ^= t + Math.imul(t ^ t >>> 7, t | 61); 11 return ((t ^ t >>> 14) >>> 0) / 4294967296; 12 }; 13} 14 15function generateCandles(n, seed, startPrice, vol) { 16 vol = vol || 8; 17 var rand = mulberry32(seed); 18 var candles = []; 19 var price = startPrice; 20 for (var i = 0; i < n; i++) { 21 var drift = (rand() - 0.5) * vol * 0.4; 22 var trend = Math.sin(i / 12) * 2 + Math.sin(i / 31) * 4; 23 var o = price; 24 var c = o + drift + trend * 0.3; 25 var spread = vol * (0.3 + rand() * 0.7); 26 var hh = Math.max(o, c) + rand() * spread; 27 var ll = Math.min(o, c) - rand() * spread; 28 var v = 0.2 + rand() * 0.8; 29 candles.push({ o: o, h: hh, l: ll, c: c, v: v }); 30 price = c; 31 } 32 return candles; 33} 34 35function ChartMock(props) { 36 var overlays = props.overlays; 37 var height = props.height || 520; 38 var seed = props.seed || 7; 39 var candleCount = props.candleCount || 90; 40 41 var canvasRef = React.useRef(null); 42 var tickRef = React.useRef(0); 43 44 React.useEffect(function() { 45 var raf; 46 var last = performance.now(); 47 var loop = function(t) { 48 if (t - last > 45) { 49 tickRef.current += 1; 50 last = t; 51 drawChart(); 52 } 53 raf = requestAnimationFrame(loop); 54 }; 55 raf = requestAnimationFrame(loop); 56 return function() { cancelAnimationFrame(raf); }; 57 }, [overlays]); 58 59 // Redraw when overlays change 60 React.useEffect(function() { 61 drawChart(); 62 }, [overlays]); 63 64 function drawChart() { 65 var canvas = canvasRef.current; 66 if (!canvas) return; 67 var tick = tickRef.current; 68 var dpr = Math.min(window.devicePixelRatio || 1, 2); 69 var parent = canvas.parentElement; 70 var cssW = parent.clientWidth; 71 var cssH = height; 72 canvas.width = cssW * dpr; 73 canvas.height = cssH * dpr; 74 canvas.style.width = cssW + 'px'; 75 canvas.style.height = cssH + 'px'; 76 var ctx = canvas.getContext('2d'); 77 ctx.setTransform(dpr, 0, 0, dpr, 0, 0); 78 draw(ctx, cssW, cssH, tick); 79 } 80 81 function draw(ctx, W, H, t) { 82 ctx.clearRect(0, 0, W, H); 83 ctx.fillStyle = '#08090d'; 84 ctx.fillRect(0, 0, W, H); 85 86 var padL = 10; 87 var padR = 90; 88 var padT = 30; 89 var padB = 40; 90 var plotW = W - padL - padR; 91 var plotH = H - padT - padB; 92 93 var baseCandles = generateCandles(candleCount, seed, 7140, 9); 94 var lastIdx = baseCandles.length - 1; 95 var lc = baseCandles[lastIdx]; 96 var animOsc = Math.sin(t / 8) * 4 + Math.sin(t / 3) * 1.2; 97 var animatedLast = { 98 o: lc.o, v: lc.v, 99 c: lc.c + animOsc, 100 h: Math.max(lc.h, lc.c + animOsc + 1.5), 101 l: Math.min(lc.l, lc.c + animOsc - 1.5), 102 }; 103 var candles = baseCandles.slice(0, lastIdx).concat([animatedLast]); 104 105 var minP = Infinity, maxP = -Infinity; 106 for (var ci = 0; ci < candles.length; ci++) { 107 if (candles[ci].l < minP) minP = candles[ci].l; 108 if (candles[ci].h > maxP) maxP = candles[ci].h; 109 } 110 var pad = (maxP - minP) * 0.08; 111 minP -= pad; 112 maxP += pad; 113 114 var priceToY = function(p) { return padT + (1 - (p - minP) / (maxP - minP)) * plotH; }; 115 var iToX = function(i) { return padL + (i + 0.5) * (plotW / candles.length); }; 116 var cW = (plotW / candles.length) * 0.72; 117 118 // Grid 119 ctx.strokeStyle = 'rgba(120, 130, 150, 0.06)'; 120 ctx.setLineDash([1, 4]); 121 ctx.lineWidth = 1; 122 var g; 123 for (g = 0; g <= 8; g++) { 124 var gy = padT + (g / 8) * plotH; 125 ctx.beginPath();
125 ctx.moveTo(padL, gy); ctx.lineTo(padL + plotW, gy); ctx.stroke(); 126 } 127 for (g = 0; g <= 10; g++) { 128 var gx = padL + (g / 10) * plotW; 129 ctx.beginPath(); ctx.moveTo(gx, padT); ctx.lineTo(gx, padT + plotH); ctx.stroke(); 130 } 131 ctx.setLineDash([]); 132 133 // GEX zone shading 134 if (overlays.gexZones) { 135 var zPut1 = priceToY(minP + (maxP - minP) * 0.12); 136 var zPut2 = priceToY(minP + (maxP - minP) * 0.28); 137 var gradPut = ctx.createLinearGradient(0, zPut2, 0, zPut1); 138 gradPut.addColorStop(0, 'rgba(255, 60, 170, 0.22)'); 139 gradPut.addColorStop(1, 'rgba(255, 60, 170, 0.04)'); 140 ctx.fillStyle = gradPut; 141 ctx.fillRect(padL, zPut2, plotW, zPut1 - zPut2); 142 143 var zNeu1 = priceToY(minP + (maxP - minP) * 0.42); 144 var zNeu2 = priceToY(minP + (maxP - minP) * 0.58); 145 ctx.fillStyle = 'rgba(255, 200, 50, 0.05)'; 146 ctx.fillRect(padL, zNeu2, plotW, zNeu1 - zNeu2); 147 148 var zCall1 = priceToY(minP + (maxP - minP) * 0.72); 149 var zCall2 = priceToY(minP + (maxP - minP) * 0.88); 150 var gradCall = ctx.createLinearGradient(0, zCall2, 0, zCall1); 151 gradCall.addColorStop(0, 'rgba(70, 220, 255, 0.04)'); 152 gradCall.addColorStop(1, 'rgba(70, 220, 255, 0.18)'); 153 ctx.fillStyle = gradCall; 154 ctx.fillRect(padL, zCall2, plotW, zCall1 - zCall2); 155 } 156 157 // Gamma depth bars 158 if (overlays.gammaDepth) { 159 var gdRand = mulberry32(seed + 3); 160 var bars = 26; 161 var barH = plotH / bars; 162 var maxBarW = padR - 12; 163 for (var b = 0; b < bars; b++) { 164 var by = padT + b * barH; 165 var norm = b / bars; 166 var isCall = norm < 0.5; 167 var mag = Math.abs(gdRand() - 0.5) * 2; 168 var shape = Math.exp(-Math.pow((norm - 0.25) * 6, 2)) * 0.5 + 169 Math.exp(-Math.pow((norm - 0.78) * 6, 2)) * 0.7; 170 var bw = (mag * 0.3 + shape) * maxBarW * 0.9; 171 ctx.fillStyle = isCall ? 'rgba(70, 220, 255, 0.7)' : 'rgba(255, 60, 170, 0.7)'; 172 ctx.fillRect(W - padR + 6, by + 1, bw, barH - 2); 173 } 174 ctx.fillStyle = 'rgba(200,210,230,0.35)'; 175 ctx.font = '9px ui-monospace, Menlo, monospace'; 176 ctx.textAlign = 'right'; 177 ctx.fillText('GAMMA DEPTH', W - 8, H - 8); 178 } 179 180 // Levels 181 var levelDefs = []; 182 if (overlays.sr) { 183 levelDefs.push({ price: minP + (maxP - minP) * 0.58, color: '#4db6ff', label: 'S/R 7,184', weight: 2.5, dash: [] }); 184 levelDefs.push({ price: minP + (maxP - minP) * 0.32, color: '#4db6ff', label: 'S/R 7,141', weight: 2, dash: [] }); 185 } 186 if (overlays.hvn) { 187 levelDefs.push({ price: minP + (maxP - minP) * 0.86, color: '#ffb84d', label: 'HVN 7,210', weight: 2, dash: [] }); 188 levelDefs.push({ price: minP + (maxP - minP) * 0.18, color: '#ffb84d', label: 'HVN 7,118', weight: 2, dash: [] }); 189 } 190 if (overlays.lvn) { 191 levelDefs.push({ price: minP + (maxP - minP) * 0.45, color: '#28f07c', label: 'LVN 7,156', weight: 1.5, dash: [4, 4] }); 192 } 193 if (overlays.pivots) { 194 levelDefs.push({ price: minP + (maxP - minP) * 0.74, color: '#ff3caa', label: 'PIVOT', weight: 1.5, dash: [2, 6] }); 195 } 196 197 for (var li = 0; li < levelDefs.length; li++) { 198 var lv = levelDefs[li]; 199 var ly = priceToY(lv.price); 200 ctx.strokeStyle = lv.color; 201 ctx.lineWidth = lv.weight; 202 ctx.setLineDash(lv.dash); 203 ctx.beginPath();
204 ctx.moveTo(padL, ly); 205 ctx.lineTo(padL + plotW, ly); 206 ctx.stroke(); 207 ctx.setLineDash([]); 208 ctx.font = '10px ui-monospace, Menlo, monospace'; 209 var lw = ctx.measureText(lv.label).width + 10; 210 ctx.fillStyle = 'rgba(0,0,0,0.7)'; 211 ctx.fillRect(padL + 6, ly - 7, lw, 14); 212 ctx.fillStyle = lv.color; 213 ctx.textAlign = 'left'; 214 ctx.fillText(lv.label, padL + 11, ly + 3); 215 } 216 ctx.setLineDash([]); 217 218 // Blocks 219 if (overlays.blocks) { 220 var blocks = [ 221 { x0: 0.10, x1: 0.55, y: 0.82, bh: 0.035 }, 222 { x0: 0.04, x1: 0.40, y: 0.65, bh: 0.030 }, 223 { x0: 0.35, x1: 0.78, y: 0.50, bh: 0.030 }, 224 { x0: 0.15, x1: 0.38, y: 0.22, bh: 0.025 }, 225 ]; 226 for (var bi = 0; bi < blocks.length; bi++) { 227 var bl = blocks[bi]; 228 var bx = padL + bl.x0 * plotW; 229 var bbw = (bl.x1 - bl.x0) * plotW; 230 var bby = priceToY(minP + (1 - bl.y) * (maxP - minP)); 231 var bbh = bl.bh * plotH; 232 ctx.fillStyle = 'rgba(196, 140, 80, 0.35)'; 233 ctx.fillRect(bx, bby - bbh / 2, bbw, bbh); 234 ctx.strokeStyle = 'rgba(230, 170, 100, 0.9)'; 235 ctx.lineWidth = 1; 236 ctx.strokeRect(bx, bby - bbh / 2, bbw, bbh); 237 } 238 } 239 240 // Candles 241 for (var i = 0; i < candles.length; i++) { 242 var cd = candles[i]; 243 var x = iToX(i); 244 var up = cd.c >= cd.o; 245 var col = up ? '#2de8b2' : '#ff3caa'; 246 ctx.strokeStyle = col; 247 ctx.lineWidth = 1; 248 ctx.beginPath(); 249 ctx.moveTo(x, priceToY(cd.h)); 250 ctx.lineTo(x, priceToY(cd.l)); 251 ctx.stroke(); 252 var yO = priceToY(cd.o); 253 var yC = priceToY(cd.c); 254 var cby = Math.min(yO, yC); 255 var cbh = Math.max(1, Math.abs(yC - yO)); 256 ctx.fillStyle = col; 257 ctx.fillRect(x - cW / 2, cby, cW, cbh); 258 } 259 260 // News pin 261 if (overlays.news) { 262 var pinI = Math.floor(candles.length * 0.72); 263 var px = iToX(pinI); 264 var py = priceToY(candles[pinI].h) - 14; 265 var pulse = 0.5 + 0.5 * Math.sin(t / 6); 266 ctx.fillStyle = 'rgba(255, 200, 50, ' + (0.6 + pulse * 0.4) + ')'; 267 ctx.beginPath(); 268 ctx.arc(px, py, 4 + pulse * 2, 0, Math.PI * 2); 269 ctx.fill(); 270 ctx.strokeStyle = 'rgba(255, 200, 50, 0.4)'; 271 ctx.beginPath(); 272 ctx.arc(px, py, 8 + pulse * 4, 0, Math.PI * 2); 273 ctx.stroke(); 274 ctx.strokeStyle = 'rgba(255, 200, 50, 0.8)'; 275 ctx.setLineDash([2, 3]); 276 ctx.beginPath(); ctx.moveTo(px, py - 4); ctx.lineTo(px, padT + 10); ctx.stroke(); 277 ctx.setLineDash([]); 278 ctx.fillStyle = 'rgba(255, 200, 50, 0.15)'; 279 ctx.fillRect(px - 60, padT + 4, 120, 16); 280 ctx.fillStyle = '#ffd466'; 281 ctx.font = '10px ui-monospace, Menlo, monospace'; 282 ctx.textAlign = 'center'; 283 ctx.fillText('CPI HOT \u00b7 +0.4% MoM', px, padT + 15); 284 } 285 286 // Expected move bands 287 if (overlays.emBands) { 288 var midY = priceToY(minP + (maxP - minP) * 0.55); 289 var bandWidth = plotH * 0.18; 290 ctx.strokeStyle = 'rgba(255, 200, 50, 0.55)'; 291 ctx.setLineDash([5, 4]); 292 ctx.lineWidth = 1; 293 ctx.beginPath();
293 ctx.moveTo(padL, midY - bandWidth); ctx.lineTo(padL + plotW, midY - bandWidth); ctx.stroke(); 294 ctx.beginPath(); ctx.moveTo(padL, midY + bandWidth); ctx.lineTo(padL + plotW, midY + bandWidth); ctx.stroke(); 295 ctx.setLineDash([]); 296 } 297 298 // Last price tag 299 var lastPrice = candles[candles.length - 1].c; 300 var lastY = priceToY(lastPrice); 301 ctx.fillStyle = animOsc >= 0 ? 'rgba(45, 232, 178, 0.95)' : 'rgba(255, 60, 170, 0.95)'; 302 ctx.fillRect(W - padR, lastY - 9, padR - 2, 18); 303 ctx.fillStyle = '#04060a'; 304 ctx.font = 'bold 11px ui-monospace, Menlo, monospace'; 305 ctx.textAlign = 'left'; 306 ctx.fillText(lastPrice.toFixed(2), W - padR + 6, lastY + 3); 307 ctx.strokeStyle = animOsc >= 0 ? 'rgba(45, 232, 178, 0.35)' : 'rgba(255, 60, 170, 0.35)'; 308 ctx.setLineDash([3, 3]); 309 ctx.beginPath(); ctx.moveTo(padL, lastY); ctx.lineTo(W - padR, lastY); ctx.stroke(); 310 ctx.setLineDash([]); 311 312 // Price axis 313 ctx.fillStyle = 'rgba(180,190,210,0.45)'; 314 ctx.font = '10px ui-monospace, Menlo, monospace'; 315 ctx.textAlign = 'left'; 316 for (g = 0; g <= 8; g++) { 317 var ay = padT + (g / 8) * plotH; 318 var ap = maxP - (g / 8) * (maxP - minP); 319 if (Math.abs(ay - lastY) > 14) { 320 ctx.fillText(ap.toFixed(0), W - padR + 6, ay + 3); 321 } 322 } 323 324 // Time axis 325 ctx.fillStyle = 'rgba(180,190,210,0.35)'; 326 ctx.textAlign = 'center'; 327 ctx.font = '10px ui-monospace, Menlo, monospace'; 328 var times = ['09:30', '10:30', '11:30', '12:30', '13:30', '14:30']; 329 for (var ti = 0; ti < times.length; ti++) { 330 var tx = padL + (ti / (times.length - 1)) * plotW; 331 ctx.fillText(times[ti], tx, H - 14); 332 } 333 334 // Top bar 335 ctx.fillStyle = 'rgba(200,210,230,0.65)'; 336 ctx.textAlign = 'left'; 337 ctx.font = 'bold 11px ui-monospace, Menlo, monospace'; 338 ctx.fillText('NQ1!', padL + 4, 18); 339 ctx.fillStyle = 'rgba(140,150,170,0.7)'; 340 ctx.font = '10px ui-monospace, Menlo, monospace'; 341 ctx.fillText('\u00b7 5m \u00b7 CME', padL + 34, 18); 342 ctx.fillStyle = animOsc >= 0 ? 'rgba(45, 232, 178, 0.95)' : 'rgba(255, 60, 170, 0.95)'; 343 ctx.fillText(lastPrice.toFixed(2) + ' ' + (animOsc >= 0 ? '+' : '') + animOsc.toFixed(2), padL + 100, 18); 344 } 345 346 return h('div', { style: { position: 'relative', width: '100%' } }, 347 h('canvas', { ref: canvasRef, style: { display: 'block', borderRadius: 12 } }) 348 ); 349} 350 351window.ChartMock = ChartMock;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.