UNPKG

@ldrick/trade-indicators

Version:
31 lines (30 loc) 1.94 kB
import { apply as AP, either as E, function as F, readonlyArray as RA, readonlyNonEmptyArray as RNEA, } from 'fp-ts/lib'; import { PeriodSizeMissmatchError } from '../errors/PeriodSizeMissmatchError.js'; import * as arr from '../utils/array.js'; import * as num from '../utils/number.js'; import { emaC } from './ema.js'; const validatePeriodSizes = (slowPeriod, fastPeriod) => slowPeriod > fastPeriod ? E.right(true) : E.left(new PeriodSizeMissmatchError('slowPeriod', 'fastPeriod')); const calculate = (fast, slow) => F.pipe(slow, RNEA.mapWithIndex((index, value) => { const shortened = RA.takeRight(slow.length)(fast); return shortened[index].sub(value); })); /** * The Moving Average Convergence Divergence (MACD) is the relationship of * two Exponential Moving Averages (EMA) with different periods. * It generates crosses with the generated signal EMA which can be used * to indicate uptrends or downtrends. * * @public */ export const macd = (values, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) => F.pipe(AP.sequenceS(E.Applicative)({ fastPeriodV: num.validatePositiveInteger(fastPeriod), slowPeriodV: num.validatePositiveInteger(slowPeriod), signalPeriodV: num.validatePositiveInteger(signalPeriod), periodSizes: validatePeriodSizes(slowPeriod, fastPeriod), valuesV: arr.validateRequiredSize(slowPeriod + signalPeriod - 1)(values), }), E.bind('valuesB', ({ valuesV }) => arr.toBig(valuesV)), E.bind('emaSlow', ({ valuesB, slowPeriodV }) => emaC(valuesB, slowPeriodV)), E.bind('emaFast', ({ valuesB, fastPeriodV }) => emaC(valuesB, fastPeriodV)), E.bind('macdResult', ({ emaFast, emaSlow }) => E.right(calculate(emaFast, emaSlow))), E.bind('signal', ({ macdResult, signalPeriodV }) => emaC(macdResult, signalPeriodV)), E.map(({ macdResult, signal }) => ({ macd: arr.toNumber(macdResult), signal: F.pipe(signal, arr.toNumber, arr.fillLeftW(macdResult.length, null)), })));