calculate method

  1. @override
IndicatorResult calculate(
  1. List<Candle> candles
)
override

Pure computation function that produces an IndicatorResult from candles.

Implementation

@override
IndicatorResult calculate(List<Candle> candles) {
  final macdValues = List<double?>.filled(candles.length, null);
  final signalValues = List<double?>.filled(candles.length, null);
  final histValues = List<double?>.filled(candles.length, null);

  if (candles.length < slowPeriod) {
    return IndicatorResult(
      indicatorId: id,
      name: name,
      isOverlay: false,
      series: [
        IndicatorSeries(
          id: 'histogram',
          label: 'Hist',
          color: const Color(0xFF00E676),
          values: histValues,
        ),
        IndicatorSeries(
          id: 'macd',
          label: 'MACD',
          color: macdColor,
          strokeWidth: 1.5,
          values: macdValues,
        ),
        IndicatorSeries(
          id: 'signal',
          label: 'Signal',
          color: signalColor,
          strokeWidth: 1.5,
          values: signalValues,
        ),
      ],
      horizontalLevels: const [0.0],
    );
  }

  // 1. Calculate Fast EMA (12) and Slow EMA (26)
  final fastEma = _calculateEmaSeries(candles, fastPeriod);
  final slowEma = _calculateEmaSeries(candles, slowPeriod);

  // 2. Calculate MACD Line = Fast EMA - Slow EMA
  for (int i = 0; i < candles.length; i++) {
    if (fastEma[i] != null && slowEma[i] != null) {
      macdValues[i] = fastEma[i]! - slowEma[i]!;
    }
  }

  // 3. Calculate Signal Line = EMA (9) of MACD Line
  // First find start index of non-null MACD values
  int macdStartIndex = -1;
  for (int i = 0; i < candles.length; i++) {
    if (macdValues[i] != null) {
      macdStartIndex = i;
      break;
    }
  }

  if (macdStartIndex != -1 &&
      (candles.length - macdStartIndex) >= signalPeriod) {
    // SMA of first signalPeriod valid MACD values
    double sum = 0.0;
    for (int i = macdStartIndex; i < macdStartIndex + signalPeriod; i++) {
      sum += macdValues[i]!;
    }
    double currentSignal = sum / signalPeriod;
    signalValues[macdStartIndex + signalPeriod - 1] = currentSignal;

    final k = 2.0 / (signalPeriod + 1);
    for (int i = macdStartIndex + signalPeriod; i < candles.length; i++) {
      if (macdValues[i] != null) {
        currentSignal = (macdValues[i]! * k) + (currentSignal * (1 - k));
        signalValues[i] = currentSignal;
      }
    }
  }

  // 4. Calculate Histogram = MACD - Signal
  for (int i = 0; i < candles.length; i++) {
    if (macdValues[i] != null && signalValues[i] != null) {
      histValues[i] = macdValues[i]! - signalValues[i]!;
    }
  }

  return IndicatorResult(
    indicatorId: id,
    name: name,
    isOverlay: false,
    series: [
      IndicatorSeries(
        id: 'histogram',
        label: 'Histogram',
        color: const Color(0xFF00E676),
        values: histValues,
      ),
      IndicatorSeries(
        id: 'macd',
        label: 'MACD',
        color: macdColor,
        strokeWidth: 1.5,
        values: macdValues,
      ),
      IndicatorSeries(
        id: 'signal',
        label: 'Signal',
        color: signalColor,
        strokeWidth: 1.5,
        values: signalValues,
      ),
    ],
    horizontalLevels: const [0.0],
  );
}