63 lines
2.0 KiB
TypeScript
63 lines
2.0 KiB
TypeScript
export class Indicators {
|
|
static calculateEMA(data: number[], period: number): number {
|
|
if (data.length === 0) return 0;
|
|
const k = 2 / (period + 1);
|
|
let ema = data[0] || 0;
|
|
for (let i = 1; i < data.length; i++) {
|
|
const current = data[i] || 0;
|
|
ema = current * k + ema * (1 - k);
|
|
}
|
|
return ema;
|
|
}
|
|
|
|
static calculateRSI(data: number[], period: number): number {
|
|
if (data.length <= period) return 50; // Default if not enough data
|
|
|
|
let gains = 0;
|
|
let losses = 0;
|
|
|
|
// First average
|
|
for (let i = 1; i <= period; i++) {
|
|
const diff = data[i] - data[i - 1];
|
|
if (diff >= 0) gains += diff;
|
|
else losses -= diff;
|
|
}
|
|
|
|
let avgGain = gains / period;
|
|
let avgLoss = losses / period;
|
|
|
|
// Smoothing
|
|
for (let i = period + 1; i < data.length; i++) {
|
|
const diff = data[i] - data[i - 1];
|
|
if (diff >= 0) {
|
|
avgGain = (avgGain * (period - 1) + diff) / period;
|
|
avgLoss = (avgLoss * (period - 1)) / period;
|
|
} else {
|
|
avgGain = (avgGain * (period - 1)) / period;
|
|
avgLoss = (avgLoss * (period - 1) - diff) / period;
|
|
}
|
|
}
|
|
|
|
if (avgLoss === 0) return 100;
|
|
const rs = avgGain / avgLoss;
|
|
return 100 - (100 / (1 + rs));
|
|
}
|
|
|
|
static calculateATR(candles: any[], period: number): number {
|
|
if (candles.length <= period) return 0;
|
|
|
|
let trs: number[] = [];
|
|
for (let i = 1; i < candles.length; i++) {
|
|
const h = candles[i].high;
|
|
const l = candles[i].low;
|
|
const pc = candles[i - 1].close;
|
|
const tr = Math.max(h - l, Math.abs(h - pc), Math.abs(l - pc));
|
|
trs.push(tr);
|
|
}
|
|
|
|
// Return SMA of TR
|
|
const recentTrs = trs.slice(-period);
|
|
return recentTrs.reduce((a, b) => a + b, 0) / period;
|
|
}
|
|
}
|