/**
* Trend Detection Analysis
* detectTrend, detectMarketStructure functions
*/
export function detectTrend(closes, ema20, ema50, ema200) {
if (!closes || closes.length === 0 || !ema20 || ema20.length === 0) {
return {
trend: 'neutral',
strength: 0,
reason: 'Insufficient data'
};
}
const currentPrice = closes[closes.length - 1];
const currentEMA20 = ema20[ema20.length - 1];
const currentEMA50 = ema50 && ema50.length > 0 ? ema50[ema50.length - 1] : null;
const currentEMA200 = ema200 && ema200.length > 0 ? ema200[ema200.length - 1] : null;
let trend = 'neutral';
let strength = 0;
let reason = '';
// Determine trend based on EMA crossovers and price position
if (currentEMA50 && currentEMA200) {
// Strong uptrend: Price > EMA20 > EMA50 > EMA200
if (currentPrice > currentEMA20 && currentEMA20 > currentEMA50 && currentEMA50 > currentEMA200) {
trend = 'uptrend';
strength = 3;
reason = 'Strong uptrend: Price > EMA20 > EMA50 > EMA200';
}
// Strong downtrend: Price < EMA20 < EMA50 < EMA200
else if (currentPrice < currentEMA20 && currentEMA20 < currentEMA50 && currentEMA50 < currentEMA200) {
trend = 'downtrend';
strength = 3;
reason = 'Strong downtrend: Price < EMA20 < EMA50 < EMA200';
}
// Moderate uptrend: Price > EMA20 > EMA50
else if (currentPrice > currentEMA20 && currentEMA20 > currentEMA50) {
trend = 'uptrend';
strength = 2;
reason = 'Moderate uptrend: Price > EMA20 > EMA50';
}
// Moderate downtrend: Price < EMA20 < EMA50
else if (currentPrice < currentEMA20 && currentEMA20 < currentEMA50) {
trend = 'downtrend';
strength = 2;
reason = 'Moderate downtrend: Price < EMA20 < EMA50';
}
// Weak uptrend: Price > EMA20
else if (currentPrice > currentEMA20) {
trend = 'uptrend';
strength = 1;
reason = 'Weak uptrend: Price > EMA20';
}
// Weak downtrend: Price < EMA20
else if (currentPrice < currentEMA20) {
trend = 'downtrend';
strength = 1;
reason = 'Weak downtrend: Price < EMA20';
}
}
else if (currentEMA50) {
// Only EMA20 and EMA50 available
if (currentPrice > currentEMA20 && currentEMA20 > currentEMA50) {
trend = 'uptrend';
strength = 2;
reason = 'Uptrend: Price > EMA20 > EMA50';
}
else if (currentPrice < currentEMA20 && currentEMA20 < currentEMA50) {
trend = 'downtrend';
strength = 2;
reason = 'Downtrend: Price < EMA20 < EMA50';
}
else if (currentPrice > currentEMA20) {
trend = 'uptrend';
strength = 1;
reason = 'Weak uptrend: Price > EMA20';
}
else if (currentPrice < currentEMA20) {
trend = 'downtrend';
strength = 1;
reason = 'Weak downtrend: Price < EMA20';
}
}
else {
// Only EMA20 available
if (currentPrice > currentEMA20) {
trend = 'uptrend';
strength = 1;
reason = 'Weak uptrend: Price > EMA20';
}
else if (currentPrice < currentEMA20) {
trend = 'downtrend';
strength = 1;
reason = 'Weak downtrend: Price < EMA20';
}
}
return {
trend: trend,
strength: strength,
reason: reason
};
}
export function detectMarketStructure(highs, lows, closes, lookbackPeriod = 20) {
if (highs.length < lookbackPeriod || lows.length < lookbackPeriod || closes.length < lookbackPeriod) {
return {
higherHighs: false,
lowerLows: false,
higherLows: false,
lowerHighs: false,
structure: 'neutral'
};
}
// Get recent swing highs and lows
const recentHighs = highs.slice(-lookbackPeriod);
const recentLows = lows.slice(-lookbackPeriod);
// Find highest high and lowest low in recent period
const highestHigh = Math.max(...recentHighs);
const lowestLow = Math.min(...recentLows);
const highestHighIndex = recentHighs.indexOf(highestHigh);
const lowestLowIndex = recentLows.indexOf(lowestLow);
// Check for higher highs (uptrend structure)
const higherHighs = highestHighIndex === recentHighs.length - 1; // Highest high is most recent
// Check for lower lows (downtrend structure)
const lowerLows = lowestLowIndex === recentLows.length - 1; // Lowest low is most recent
// Check for higher lows (bullish structure)
const firstHalfLows = recentLows.slice(0, Math.floor(recentLows.length / 2));
const secondHalfLows = recentLows.slice(Math.floor(recentLows.length / 2));
const avgFirstHalfLows = firstHalfLows.reduce((a, b) => a + b, 0) / firstHalfLows.length;
const avgSecondHalfLows = secondHalfLows.reduce((a, b) => a + b, 0) / secondHalfLows.length;
const higherLows = avgSecondHalfLows > avgFirstHalfLows;
// Check for lower highs (bearish structure)
const firstHalfHighs = recentHighs.slice(0, Math.floor(recentHighs.length / 2));
const secondHalfHighs = recentHighs.slice(Math.floor(recentHighs.length / 2));
const avgFirstHalfHighs = firstHalfHighs.reduce((a, b) => a + b, 0) / firstHalfHighs.length;
const avgSecondHalfHighs = secondHalfHighs.reduce((a, b) => a + b, 0) / secondHalfHighs.length;
const lowerHighs = avgSecondHalfHighs < avgFirstHalfHighs;
// Determine market structure
let structure = 'neutral';
if (higherHighs && higherLows) {
structure = 'uptrend';
}
else if (lowerLows && lowerHighs) {
structure = 'downtrend';
}
else if (higherHighs || higherLows) {
structure = 'bullish';
}
else if (lowerLows || lowerHighs) {
structure = 'bearish';
}
return {
higherHighs: higherHighs,
lowerLows: lowerLows,
higherLows: higherLows,
lowerHighs: lowerHighs,
structure: structure
};
}