1/** 2 * Deterministic OHLC fixtures for visual baseline tests (PR 2.3). 3 * 4 * All candles use CLOSE-convention `time` (seconds since epoch, floored to close boundary). 5 * Anchor timestamp is 2024-01-01T00:00:00Z (1704067200s) so generated data is 6 * reproducible across machines and CI runs. 7 * 8 * Each fixture has enough candles for every indicator in the baseline matrix to 9 * render something meaningful (zones, structural levels, monday ranges, etc.). 10 */ 11 12// Anchor: 2024-01-01T00:00:00Z 13export const ANCHOR_SECONDS = 1704067200; 14 15const INTERVAL_SECONDS = { 16 '1m': 60, 17 '5m': 300, 18 '1h': 3600, 19 '1d': 86400, 20}; 21 22// Seeded PRNG for deterministic candles. Mulberry32 produces uniform [0, 1). 23function mulberry32(seed) { 24 let a = seed >>> 0; 25 return function () { 26 a = (a + 0x6D2B79F5) >>> 0; 27 let t = a; 28 t = Math.imul(t ^ (t >>> 15), t | 1); 29 t ^= t + Math.imul(t ^ (t >>> 7), t | 61); 30 return ((t ^ (t >>> 14)) >>> 0) / 4294967296; 31 }; 32} 33 34function generateCandles(interval, count, seed, startPrice) { 35 const step = INTERVAL_SECONDS[interval]; 36 if (!step) throw new Error(`unknown interval: ${interval}`); 37 const rand = mulberry32(seed); 38 const out = []; 39 let price = startPrice; 40 for (let i = 0; i < count; i++) { 41 const drift = (rand() - 0.48) * startPrice * 0.01; 42 const open = price; 43 const close = Math.max(1, open + drift); 44 const range = Math.abs(drift) + startPrice * 0.004; 45 const high = Math.max(open, close) + rand() * range; 46 const low = Math.min(open, close) - rand() * range; 47 out.push({ 48 time: ANCHOR_SECONDS + i * step, 49 open: +open.toFixed(2), 50 high: +high.toFixed(2), 51 low: +low.toFixed(2), 52 close: +close.toFixed(2), 53 }); 54 price = close; 55 } 56 return out; 57} 58 59// Interval fixtures â counts sized so indicators have room to anchor meaningfully. 60// 1m: 360 candles = 6h window 61// 5m: 576 candles = 2d window 62// 1h: 720 candles = 30d window (enough for weekly/monthly anchors) 63// 1d: 400 candles = ~13mo (enough for yearly + multiple monthly anchors) 64export const CANDLES_1M = generateCandles('1m', 360, 0xABCD0001, 45000); 65export const CANDLES_5M = generateCandles('5m', 576, 0xABCD0002, 45000); 66export const CANDLES_1H = generateCandles('1h', 720, 0xABCD0003, 45000); 67export const CANDLES_1D = generateCandles('1d', 400, 0xABCD0004, 45000); 68 69export const FIXTURES = { 70 '1m': { candles: CANDLES_1M, interval: '1m', intervalSeconds: INTERVAL_SECONDS['1m'] }, 71 '5m': { candles: CANDLES_5M, interval: '5m', intervalSeconds: INTERVAL_SECONDS['5m'] }, 72 '1h': { candles: CANDLES_1H, interval: '1h', intervalSeconds: INTERVAL_SECONDS['1h'] }, 73 '1d': { candles: CANDLES_1D, interval: '1d', intervalSeconds: INTERVAL_SECONDS['1d'] }, 74}; 75 76// Deterministic trade markers for the 1h matrix cell. 77// Placed at specific candle indices so the markers land on real candle times. 78export function tradeMarkersFor1h() { 79 const picks = [30, 80, 140, 260, 380, 520]; 80 return picks.map((idx, i) => { 81 const c = CANDLES_1H[idx]; 82 return { 83 time: c.time, 84 price: (c.high + c.low) / 2, 85 side: i % 2 === 0 ? 'buy' : 'sell', 86 size: 0.25 + i * 0.15, 87 }; 88 }); 89} 90 91// Deterministic zone set for the 1h matrix cell. 92export function zonesFor1h() { 93 return [ 94 { 95 startTime: CANDLES_1H[120].time, 96 endTime: CANDLES_1H[260].time, 97 topPrice: CANDLES_1H[180].high + 200, 98 bottomPrice: CANDLES_1H[180].low - 200, 99 color: 'rgba(38, 166, 154, 0.18)', 100 }, 101 { 102 startTime: CANDLES_1H[400].time, 103 endTime: CANDLES_1H[550].time, 104 topPrice: CANDLES_1H[475].high + 150, 105 bottomPrice: CANDLES_1H[475].low - 150, 106 color: 'rgba(239, 83, 80, 0.18)', 107 }, 108 ]; 109} 110 111// Deterministic horizontal lines for the 1h matrix cell. 112export function horizontalLinesFor1h() { 113 return [ 114 { price: CANDLES_1H[100].close, date: new Date(CANDLES_1H[100].time * 1000).toISOString(), label: 'L1', color: '#B8F84E' }, 115 { price: CANDLES_1H[300].close, date: new Date(CANDLES_1H[300].time * 1000).toISOString(), label: 'L2', color: '#F
115FC107' }, 116 { price: CANDLES_1H[600].close, date: new Date(CANDLES_1H[600].time * 1000).toISOString(), label: 'L3', color: '#FF6B6B' }, 117 ]; 118}
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.