Bot & Automation

Treding Forex Ai

/root/hermes-projects/Treding Forex Ai

src/utils/math.ts text
import type { Candle } from "../domain/types";

export function clamp(value: number, min: number, max: number): number {
  return Math.min(Math.max(value, min), max);
}

export function roundTo(value: number, digits = 5): number {
  const factor = 10 ** digits;
  return Math.round(value * factor) / factor;
}

export function simpleMovingAverage(values: number[], period: number): number {
  if (values.length < period) {
    return average(values);
  }

  return average(values.slice(-period));
}

export function exponentialMovingAverage(values: number[], period: number): number {
  if (values.length === 0) {
    return 0;
  }

  const smoothing = 2 / (period + 1);
  const seed = simpleMovingAverage(values.slice(0, Math.min(period, values.length)), period);

  return values.slice(1).reduce((previous, current) => {
    return current * smoothing + previous * (1 - smoothing);
  }, seed);
}

export function relativeStrengthIndex(values: number[], period = 14): number {
  if (values.length <= period) {
    return 50;
  }

  const changes = values.slice(1).map((value, index) => value - values[index]);
  const recent = changes.slice(-period);
  const gains = recent.filter((value) => value > 0).reduce((sum, value) => sum + value, 0);
  const losses = Math.abs(recent.filter((value) => value < 0).reduce((sum, value) => sum + value, 0));

  if (losses === 0) {
    return 100;
  }

  const relativeStrength = gains / losses;
  return 100 - 100 / (1 + relativeStrength);
}

export function averageTrueRange(candles: Candle[], period = 14): number {
  if (candles.length < 2) {
    return 0;
  }

  const trueRanges = candles.slice(1).map((candle, index) => {
    const previousClose = candles[index].close;
    return Math.max(
      candle.high - candle.low,
      Math.abs(candle.high - previousClose),
      Math.abs(candle.low - previousClose)
    );
  });

  return simpleMovingAverage(trueRanges, Math.min(period, trueRanges.length));
}

export function macdHistogram(values: number[]): number {
  const ema12 = exponentialMovingAverage(values, 12);
  const ema26 = exponentialMovingAverage(values, 26);
  const macdLine = ema12 - ema26;
  const recentMacdValues = values.slice(-9).map((_, index, recent) => {
    const sliceEnd = values.length - recent.length + index + 1;
    const slice = values.slice(0, sliceEnd);
    return exponentialMovingAverage(slice, 12) - exponentialMovingAverage(slice, 26);
  });
  const signalLine = exponentialMovingAverage(recentMacdValues, 9);

  return macdLine - signalLine;
}

export function supportResistance(
  candles: Candle[],
  lookback = 40,
  excludeRecent = 0
): { support: number; resistance: number } {
  const endIndex = excludeRecent > 0 ? candles.length - excludeRecent : candles.length;
  const boundedEndIndex = Math.max(endIndex, 1);
  const recent = candles.slice(Math.max(0, boundedEndIndex - lookback), boundedEndIndex);

  return {
    support: Math.min(...recent.map((candle) => candle.low)),
    resistance: Math.max(...recent.map((candle) => candle.high))
  };
}

function average(values: number[]): number {
  if (values.length === 0) {
    return 0;
  }

  return values.reduce((sum, value) => sum + value, 0) / values.length;
}