From 43f1eb9c6ed725d457866c09a231e41416da3457 Mon Sep 17 00:00:00 2001 From: Mukan Erkin Date: Sat, 25 Apr 2026 13:10:54 +0300 Subject: [PATCH] feat(mse): auto pivot-based support/resistance lines on chart Co-Authored-By: Claude Sonnet 4.6 --- src/web/bot.html | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/web/bot.html b/src/web/bot.html index c468540..fd800e1 100644 --- a/src/web/bot.html +++ b/src/web/bot.html @@ -532,10 +532,84 @@ async function loadKlines() { document.getElementById('ohlc-l').textContent = fmt(last.low); document.getElementById('ohlc-c').textContent = fmt(last.close); } + drawSupportResistance(candles); updateChartOverlays(); } catch(e) {} } +// ── Support / Resistance ────────────────────────── +let srLines = []; + +function drawSupportResistance(candles) { + // önceki çizgileri temizle + srLines.forEach(l => candleSeries.removePriceLine(l)); + srLines = []; + + if (candles.length < 10) return; + + const left = 5, right = 5; // her yönde kaç mum komşu bakılacak + const pivotHighs = [], pivotLows = []; + + for (let i = left; i < candles.length - right; i++) { + const c = candles[i]; + let isHigh = true, isLow = true; + for (let j = i - left; j <= i + right; j++) { + if (j === i) continue; + if (candles[j].high >= c.high) isHigh = false; + if (candles[j].low <= c.low) isLow = false; + } + if (isHigh) pivotHighs.push(c.high); + if (isLow) pivotLows.push(c.low); + } + + // yakın seviyeleri cluster'la: fiyat aralığının %0.3'ü içindeki pivotları birleştir + const currentPrice = candles[candles.length - 1].close; + const threshold = currentPrice * 0.003; + + function cluster(pivots) { + const sorted = [...pivots].sort((a, b) => a - b); + const groups = []; + for (const p of sorted) { + const last = groups[groups.length - 1]; + if (last && p - last[last.length - 1] <= threshold) { + last.push(p); + } else { + groups.push([p]); + } + } + // her grubun ortalamasını al, en az 2 pivot olan seviyeleri döndür + return groups + .filter(g => g.length >= 2) + .map(g => g.reduce((s, v) => s + v, 0) / g.length); + } + + const resistances = cluster(pivotHighs); + const supports = cluster(pivotLows); + + // mevcut fiyata yakın en fazla 4 direnç + 4 destek çiz + const near = levels => levels + .map(p => ({ p, dist: Math.abs(p - currentPrice) })) + .sort((a, b) => a.dist - b.dist) + .slice(0, 4) + .map(x => x.p); + + near(resistances).forEach(price => { + srLines.push(candleSeries.createPriceLine({ + price, color: 'rgba(231,76,60,0.55)', lineWidth: 1, + lineStyle: LightweightCharts.LineStyle.Dashed, + axisLabelVisible: false, title: 'R', + })); + }); + + near(supports).forEach(price => { + srLines.push(candleSeries.createPriceLine({ + price, color: 'rgba(46,204,113,0.55)', lineWidth: 1, + lineStyle: LightweightCharts.LineStyle.Dashed, + axisLabelVisible: false, title: 'S', + })); + }); +} + let klineWs = null; function startKlineWs() { if (klineWs) klineWs.close();