/**
* Enhanced Metrics Analysis
* calculateEnhancedMetrics function
*/
/**
* Calculate enhanced metrics from historical data
* Volume trend, volatility pattern, volume-price divergence
*/
export function calculateEnhancedMetrics(historicalData, _indicators, _externalData) {
if (!historicalData || historicalData.length < 14) {
return null;
}
const closes = historicalData.map(d => d.close);
const volumes = historicalData.map(d => d.volume || 0);
// Volume trend - improved calculation
if (volumes.length < 20) {
// Not enough data for trend
return {
volumeTrend: 'stable',
volatilityPattern: 'normal',
volumePriceDivergence: 0,
volumeChangePercent: 0,
timestamp: Date.now()
};
}
// Use multiple timeframes for better trend detection
const recentVolumes = volumes.slice(-10); // Last 10 periods
const midVolumes = volumes.slice(-20, -10); // Previous 10 periods
const olderVolumes = volumes.length >= 30 ? volumes.slice(-30, -20) : midVolumes; // Older 10 periods (if available)
const recentAvg = recentVolumes.reduce((a, b) => a + b, 0) / recentVolumes.length;
const midAvg = midVolumes.length > 0 ? midVolumes.reduce((a, b) => a + b, 0) / midVolumes.length : recentAvg;
const olderAvg = olderVolumes.length > 0 ? olderVolumes.reduce((a, b) => a + b, 0) / olderVolumes.length : midAvg;
// Calculate trend with multiple comparisons (more accurate)
let volumeTrend = 'stable';
if (olderAvg > 0 && midAvg > 0) {
const recentChange = (recentAvg - midAvg) / midAvg;
const midChange = (midAvg - olderAvg) / olderAvg;
// Both periods show same direction = stronger trend
if (recentChange > 0.05 && midChange > 0.02) {
volumeTrend = 'increasing'; // Strong increasing trend
}
else if (recentChange < -0.05 && midChange < -0.02) {
volumeTrend = 'decreasing'; // Strong decreasing trend
}
else if (recentChange > 0.1) {
volumeTrend = 'increasing'; // Recent spike (10%+)
}
else if (recentChange < -0.1) {
volumeTrend = 'decreasing'; // Recent drop (10%+)
}
else if (Math.abs(recentChange) > 0.02) {
// Small but consistent change
volumeTrend = recentChange > 0 ? 'increasing' : 'decreasing';
}
}
else if (recentAvg > 0 && midAvg === 0) {
volumeTrend = 'increasing'; // Was zero, now has volume
}
// Volatility pattern - improved detection with standard deviation
const recentPrices = closes.slice(-10);
const priceChanges = [];
for (let i = 1; i < recentPrices.length; i++) {
priceChanges.push(Math.abs((recentPrices[i] - recentPrices[i - 1]) / recentPrices[i - 1]));
}
const avgVolatility = priceChanges.length > 0
? priceChanges.reduce((a, b) => a + b, 0) / priceChanges.length
: 0;
// Also check standard deviation of price changes
const volatilityStdDev = priceChanges.length > 1
? Math.sqrt(priceChanges.reduce((sum, v) => sum + Math.pow(v - avgVolatility, 2), 0) / priceChanges.length)
: 0;
let volatilityPattern = 'normal';
if (avgVolatility > 0.05 || volatilityStdDev > 0.03) {
volatilityPattern = 'high'; // High volatility
}
else if (avgVolatility < 0.01 && volatilityStdDev < 0.005) {
volatilityPattern = 'low'; // Low volatility
}
// Volume-price divergence - improved calculation with continuous scale
// CRITICAL FIX: Check for division by zero - closes[closes.length - 10] can be 0
const oldPrice = closes.length >= 10 ? closes[closes.length - 10] : 0;
const priceChange = oldPrice > 0
? (closes[closes.length - 1] - oldPrice) / oldPrice
: 0;
// Use same volume change calculation as above
const volumeChange = midAvg > 0
? (recentAvg - midAvg) / midAvg
: (recentAvg > 0 ? 1 : 0);
// Divergence: price up but volume down = bearish (-1 to -2), price down but volume up = bullish (+1 to +2)
// Use continuous scale instead of binary
let volumePriceDivergence = 0;
if (Math.abs(priceChange) > 0.005 && Math.abs(volumeChange) > 0.02) { // Significant changes only
if (priceChange > 0 && volumeChange < -0.05) {
// Bearish divergence: Price rising but volume decreasing
volumePriceDivergence = Math.max(-2, -1 - Math.abs(volumeChange)); // -1 to -2
}
else if (priceChange < 0 && volumeChange > 0.05) {
// Bullish divergence: Price falling but volume increasing
volumePriceDivergence = Math.min(2, 1 + Math.abs(volumeChange)); // +1 to +2
}
else if (priceChange > 0 && volumeChange > 0.1) {
// Strong volume confirmation: Price and volume both rising
volumePriceDivergence = 0.5; // Slight bullish confirmation
}
else if (priceChange < 0 && volumeChange < -0.1) {
// Strong volume confirmation: Price and volume both falling
volumePriceDivergence = -0.5; // Slight bearish confirmation
}
}
return {
volumeTrend: volumeTrend,
volatilityPattern: volatilityPattern,
volumePriceDivergence: volumePriceDivergence,
volumeChangePercent: volumeChange * 100, // Add volume change percentage
timestamp: Date.now()
};
}