@ldrick/trade-indicators
Version:
Trade Indicators
31 lines (30 loc) • 1.94 kB
JavaScript
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)),
})));