- Add 'Strategy' tab to sidebar for backtesting simulations - Create strategy-panel.js for Ping-Pong and Accumulation mode simulations - Refactor all indicators (MA, HTS, RSI, MACD, BB, STOCH, Hurst) to use strict crossover-based signal calculation - Update chart.js with setSimulationMarkers and clearSimulationMarkers support - Implement single-entry rule in Ping-Pong simulation mode
153 lines
4.6 KiB
JavaScript
153 lines
4.6 KiB
JavaScript
// Self-contained MACD indicator
|
|
// Includes math, metadata, signal calculation, and base class
|
|
|
|
// Signal constants (defined in each indicator file)
|
|
const SIGNAL_TYPES = {
|
|
BUY: 'buy',
|
|
SELL: 'sell',
|
|
HOLD: 'hold'
|
|
};
|
|
|
|
const SIGNAL_COLORS = {
|
|
buy: '#26a69a',
|
|
hold: '#787b86',
|
|
sell: '#ef5350'
|
|
};
|
|
|
|
// Base class (inline replacement for BaseIndicator)
|
|
class BaseIndicator {
|
|
constructor(config) {
|
|
this.id = config.id;
|
|
this.type = config.type;
|
|
this.name = config.name;
|
|
this.params = config.params || {};
|
|
this.timeframe = config.timeframe || '1m';
|
|
this.series = [];
|
|
this.visible = config.visible !== false;
|
|
this.cachedResults = null;
|
|
this.cachedMeta = null;
|
|
this.lastSignalTimestamp = null;
|
|
this.lastSignalType = null;
|
|
}
|
|
}
|
|
|
|
// EMA calculation inline (needed for MACD)
|
|
function calculateEMAInline(data, period) {
|
|
const multiplier = 2 / (period + 1);
|
|
const ema = [];
|
|
|
|
for (let i = 0; i < data.length; i++) {
|
|
if (i < period - 1) {
|
|
ema.push(null);
|
|
} else if (i === period - 1) {
|
|
ema.push(data[i]);
|
|
} else {
|
|
ema.push((data[i] - ema[i - 1]) * multiplier + ema[i - 1]);
|
|
}
|
|
}
|
|
|
|
return ema;
|
|
}
|
|
|
|
// Signal calculation for MACD
|
|
function calculateMACDSignal(indicator, lastCandle, prevCandle, values, prevValues) {
|
|
const macd = values?.macd;
|
|
const signal = values?.signal;
|
|
const prevMacd = prevValues?.macd;
|
|
const prevSignal = prevValues?.signal;
|
|
|
|
if (macd === undefined || macd === null || signal === undefined || signal === null ||
|
|
prevMacd === undefined || prevMacd === null || prevSignal === undefined || prevSignal === null) {
|
|
return null;
|
|
}
|
|
|
|
// BUY: MACD crosses UP through Signal line
|
|
if (prevMacd <= prevSignal && macd > signal) {
|
|
return {
|
|
type: SIGNAL_TYPES.BUY,
|
|
strength: 80,
|
|
value: macd,
|
|
reasoning: `MACD crossed UP through Signal line`
|
|
};
|
|
}
|
|
// SELL: MACD crosses DOWN through Signal line
|
|
else if (prevMacd >= prevSignal && macd < signal) {
|
|
return {
|
|
type: SIGNAL_TYPES.SELL,
|
|
strength: 80,
|
|
value: macd,
|
|
reasoning: `MACD crossed DOWN through Signal line`
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// MACD Indicator class
|
|
export class MACDIndicator extends BaseIndicator {
|
|
constructor(config) {
|
|
super(config);
|
|
this.lastSignalTimestamp = null;
|
|
this.lastSignalType = null;
|
|
}
|
|
|
|
calculate(candles) {
|
|
const fast = this.params.fast || 12;
|
|
const slow = this.params.slow || 26;
|
|
const signalPeriod = this.params.signal || 9;
|
|
|
|
const closes = candles.map(c => c.close);
|
|
|
|
// Use inline EMA calculation instead of MA.ema()
|
|
const fastEMA = calculateEMAInline(closes, fast);
|
|
const slowEMA = calculateEMAInline(closes, slow);
|
|
|
|
const macdLine = fastEMA.map((f, i) => (f !== null && slowEMA[i] !== null) ? f - slowEMA[i] : null);
|
|
|
|
let sum = 0;
|
|
let ema = 0;
|
|
let count = 0;
|
|
|
|
const signalLine = macdLine.map(m => {
|
|
if (m === null) return null;
|
|
count++;
|
|
if (count < signalPeriod) {
|
|
sum += m;
|
|
return null;
|
|
} else if (count === signalPeriod) {
|
|
sum += m;
|
|
ema = sum / signalPeriod;
|
|
return ema;
|
|
} else {
|
|
ema = (m - ema) * (2 / (signalPeriod + 1)) + ema;
|
|
return ema;
|
|
}
|
|
});
|
|
|
|
return macdLine.map((m, i) => ({
|
|
macd: m,
|
|
signal: signalLine[i],
|
|
histogram: (m !== null && signalLine[i] !== null) ? m - signalLine[i] : null
|
|
}));
|
|
}
|
|
|
|
getMetadata() {
|
|
return {
|
|
name: 'MACD',
|
|
description: 'Moving Average Convergence Divergence - trend & momentum',
|
|
inputs: [
|
|
{ name: 'fast', label: 'Fast Period', type: 'number', default: 12 },
|
|
{ name: 'slow', label: 'Slow Period', type: 'number', default: 26 },
|
|
{ name: 'signal', label: 'Signal Period', type: 'number', default: 9 }
|
|
],
|
|
plots: [
|
|
{ id: 'macd', color: '#2196f3', title: 'MACD' },
|
|
{ id: 'signal', color: '#ff5722', title: 'Signal' },
|
|
{ id: 'histogram', color: '#607d8b', title: 'Histogram', type: 'histogram' }
|
|
],
|
|
displayMode: 'pane'
|
|
};
|
|
}
|
|
}
|
|
|
|
export { calculateMACDSignal }; |