UNPKG

@ldrick/trade-indicators

Version:
45 lines (44 loc) 2.57 kB
import { Big } from 'big.js'; import { apply as AP, either as E, function as F, readonlyNonEmptyArray as RNEA } from 'fp-ts/lib'; import { smmaC } from '../averages/smma.js'; import * as arr from '../utils/array.js'; import * as num from '../utils/number.js'; import * as rec from '../utils/record.js'; import { atrC } from './atr.js'; const compareMovement = (base, comparision) => base.gt(comparision) && base.gt(0) ? base : new Big(0); const movement = (up, down, move) => move === 'up' ? compareMovement(up, down) : compareMovement(down, up); const directionalMovement = (values, move) => F.pipe(values.low, arr.tail, E.map(RNEA.mapWithIndex((index, low) => { const previous = index; const current = index + 1; const up = values.high[current].sub(values.high[previous]); const down = values.low[previous].sub(low); return movement(up, down, move); }))); const directionalIndex = (values, period, move) => F.pipe(E.bindTo('dm')(directionalMovement(values, move)), E.bind('dividends', ({ dm }) => smmaC(dm, period)), E.bind('divisors', () => atrC(values, period)), E.map(({ dividends, divisors }) => F.pipe(dividends, RNEA.mapWithIndex((index, dividend) => { const divisor = divisors[index]; return divisor.eq(0) ? new Big(0) : dividend.mul(100).div(divisor); })))); const calculation = (pdi, mdi) => F.pipe(pdi, RNEA.mapWithIndex((index, plus) => { const sum = plus.add(mdi[index]); const dividend = plus.sub(mdi[index]).abs(); return sum.eq(0) ? new Big(0) : dividend.div(sum); })); /** * The Average Directional Index (ADX) determines trend strength. * It also delivers Plus Directional Movement Indicator (PDI) * and Minus Directional Movement Indicator (MDI). * Crossings of these three values can be used to determine trend changes. * * @public */ export const adx = (values, period = 14) => F.pipe(AP.sequenceS(E.Applicative)({ periodV: num.validatePositiveInteger(period), valuesV: rec.validateRequiredSize(2 * period)(values), }), E.bind('valuesB', ({ valuesV }) => rec.toBig(valuesV)), E.bind('pdi', ({ valuesB, periodV }) => directionalIndex(valuesB, periodV, 'up')), E.bind('mdi', ({ valuesB, periodV }) => directionalIndex(valuesB, periodV, 'down')), E.bind('smoothed', ({ pdi, mdi, periodV }) => { const calculated = calculation(pdi, mdi); return smmaC(calculated, periodV); }), E.map(({ smoothed, pdi, mdi }) => ({ adx: F.pipe(smoothed, RNEA.map((value) => value.mul(100)), arr.toNumber, arr.fillLeftW(mdi.length, null)), mdi: arr.toNumber(mdi), pdi: arr.toNumber(pdi), })));