feat(mse): auto pivot-based support/resistance lines on chart
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
df9b85e3c6
commit
43f1eb9c6e
1 changed files with 74 additions and 0 deletions
|
|
@ -532,10 +532,84 @@ async function loadKlines() {
|
||||||
document.getElementById('ohlc-l').textContent = fmt(last.low);
|
document.getElementById('ohlc-l').textContent = fmt(last.low);
|
||||||
document.getElementById('ohlc-c').textContent = fmt(last.close);
|
document.getElementById('ohlc-c').textContent = fmt(last.close);
|
||||||
}
|
}
|
||||||
|
drawSupportResistance(candles);
|
||||||
updateChartOverlays();
|
updateChartOverlays();
|
||||||
} catch(e) {}
|
} 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;
|
let klineWs = null;
|
||||||
function startKlineWs() {
|
function startKlineWs() {
|
||||||
if (klineWs) klineWs.close();
|
if (klineWs) klineWs.close();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue