/**
* Trend Strength Calculation
* calculateTrendStrengthIndex function
*/
import { TrendAlignment } from '../types'
import { TechnicalIndicators } from '../technical-indicators/aggregator'
/**
* Calculate Trend Strength Index (-1 to +1)
* Combines EMA alignment, ADX, Aroon, and multi-timeframe data
*/
export function calculateTrendStrengthIndex(
indicators: TechnicalIndicators | null | undefined,
trendAlignment: TrendAlignment | null | undefined
): number {
let strength = 0
let components = 0
// 1. EMA Alignment (weight: 0.3)
if (indicators && indicators.ema20 && indicators.ema50 && indicators.ema200 && indicators.price) {
const price = indicators.price
const ema20 = indicators.ema20
const ema50 = indicators.ema50
const ema200 = indicators.ema200
// Perfect bullish: Price > EMA20 > EMA50 > EMA200
if (price > ema20 && ema20 > ema50 && ema50 > ema200) {
strength += 0.3
}
// Good bullish: Price > EMA20 > EMA50
else if (price > ema20 && ema20 > ema50) {
strength += 0.2
}
// Perfect bearish: Price < EMA20 < EMA50 < EMA200
else if (price < ema20 && ema20 < ema50 && ema50 < ema200) {
strength -= 0.3
}
// Good bearish: Price < EMA20 < EMA50
else if (price < ema20 && ema20 < ema50) {
strength -= 0.2
}
components++
}
// 2. ADX Trend Strength (weight: 0.25)
if (indicators && indicators.adx !== null && indicators.adx !== undefined) {
const adxValue = indicators.adx
const adxNormalized = Math.min(1, adxValue / 50) // Normalize to 0-1
// Use +DI and -DI to determine direction
if (indicators.plusDI && indicators.minusDI) {
const diDiff = indicators.plusDI - indicators.minusDI
const diNormalized = Math.max(-1, Math.min(1, diDiff / 25)) // Normalize DI difference
strength += 0.25 * adxNormalized * diNormalized
} else {
// If no DI, use ADX strength with price direction
if (indicators.price && indicators.ema20) {
const priceDirection = indicators.price > indicators.ema20 ? 1 : -1
strength += 0.25 * adxNormalized * priceDirection
}
}
components++
}
// 3. Aroon Trend Strength (weight: 0.2)
if (indicators && indicators.aroon && indicators.aroon.up !== undefined && indicators.aroon.down !== undefined) {
const aroonDiff = indicators.aroon.up - indicators.aroon.down
const aroonNormalized = aroonDiff / 100 // Normalize to -1 to +1
strength += 0.2 * aroonNormalized
components++
}
// 4. Multi-timeframe Alignment (weight: 0.25)
if (trendAlignment) {
const alignmentScore = (trendAlignment as any).alignmentScore || 0
const dailyTrend = (trendAlignment as any).dailyTrend || trendAlignment.trend
let trendDirection = 0
if (dailyTrend === 'uptrend') trendDirection = 1
else if (dailyTrend === 'downtrend') trendDirection = -1
const alignmentNormalized = alignmentScore / 100 // 0-1
strength += 0.25 * alignmentNormalized * trendDirection
components++
}
// Normalize by number of components available
if (components > 0) {
strength = strength / components
}
// Clamp to -1 to +1
return Math.max(-1, Math.min(1, strength))
}