borsa / nextjs-app /src /lib /technical /support-resistance.ts
veteroner's picture
feat: sync US market support — bootstrap fix, symbol validation, ghost cleanup, correct dir paths
cb11e81
import type { Candle, SRLevel } from './types'
export function findSupportResistance(candles: Candle[], numLevels = 6): SRLevel[] {
if (candles.length < 20) return []
const levels: SRLevel[] = []
const tolerance = 0.015 // 1.5%
// Find local peaks and troughs (using 5-bar window)
const peaks: number[] = []
const troughs: number[] = []
for (let i = 2; i < candles.length - 2; i++) {
const c = candles[i]
if (c.high >= candles[i - 1].high && c.high >= candles[i - 2].high &&
c.high >= candles[i + 1].high && c.high >= candles[i + 2].high) {
peaks.push(c.high)
}
if (c.low <= candles[i - 1].low && c.low <= candles[i - 2].low &&
c.low <= candles[i + 1].low && c.low <= candles[i + 2].low) {
troughs.push(c.low)
}
}
// Cluster nearby peaks/troughs
function cluster(arr: number[]): { price: number; count: number }[] {
if (!arr.length) return []
const sorted = [...arr].sort((a, b) => a - b)
const clusters: { prices: number[]; sum: number }[] = []
for (const p of sorted) {
let found = false
for (const c of clusters) {
const avg = c.sum / c.prices.length
if (Math.abs(p - avg) / avg < tolerance) {
c.prices.push(p); c.sum += p; found = true; break
}
}
if (!found) clusters.push({ prices: [p], sum: p })
}
return clusters
.map((c) => ({ price: c.sum / c.prices.length, count: c.prices.length }))
.sort((a, b) => b.count - a.count)
}
const lastPrice = candles[candles.length - 1].close
for (const { price, count } of cluster(troughs).slice(0, numLevels)) {
if (price < lastPrice) {
levels.push({ price, type: 'support', strength: Math.min(5, count), touches: count })
}
}
for (const { price, count } of cluster(peaks).slice(0, numLevels)) {
if (price > lastPrice) {
levels.push({ price, type: 'resistance', strength: Math.min(5, count), touches: count })
}
}
return levels.sort((a, b) => a.price - b.price).slice(0, numLevels)
}