generated from bennycode/ts-node-starter
-
-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(DX): Add Directional Movement Index (DX) (#365)
- Loading branch information
Showing
7 changed files
with
203 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import {DX} from './DX'; | ||
|
||
describe('DX', () => { | ||
describe('getResult', () => { | ||
it('calculates the Directional Movement Index (DX)', () => { | ||
// Test data verified with: | ||
// https://tulipindicators.org/dx | ||
const candles = [ | ||
{high: 82.15, low: 81.29, close: 81.59}, | ||
{high: 81.89, low: 80.64, close: 81.06}, | ||
{high: 83.03, low: 81.31, close: 82.87}, | ||
{high: 83.3, low: 82.65, close: 83.0}, | ||
{high: 83.85, low: 83.07, close: 83.61}, | ||
{high: 83.9, low: 83.11, close: 83.15}, | ||
{high: 83.33, low: 82.49, close: 82.84}, | ||
{high: 84.3, low: 82.3, close: 83.99}, | ||
{high: 84.84, low: 84.15, close: 84.55}, | ||
{high: 85.0, low: 84.11, close: 84.36}, | ||
{high: 85.9, low: 84.03, close: 85.53}, | ||
{high: 86.58, low: 85.39, close: 86.54}, | ||
{high: 86.98, low: 85.76, close: 86.89}, | ||
{high: 88.0, low: 87.17, close: 87.77}, | ||
{high: 87.87, low: 87.01, close: 87.29}, | ||
]; | ||
|
||
const expectations = [ | ||
'50.19', | ||
'51.36', | ||
'11.09', | ||
'41.52', | ||
'52.77', | ||
'55.91', | ||
'69.96', | ||
'76.90', // The official TI page has a rounding mistake here | ||
'80.26', | ||
'86.51', | ||
'75.61', | ||
]; | ||
|
||
const dx = new DX(5); | ||
|
||
for (const candle of candles) { | ||
dx.update(candle); | ||
if (dx.isStable) { | ||
const expected = expectations.shift(); | ||
expect(dx.getResult().toFixed(2)).toBe(expected!); | ||
} | ||
} | ||
|
||
expect(dx.isStable).toBeTrue(); | ||
expect(dx.getResult().toFixed(2)).toBe('75.61'); | ||
expect(dx.lowest!.toFixed(2)).toBe('11.09'); | ||
expect(dx.highest!.toFixed(2)).toBe('86.51'); | ||
}); | ||
|
||
it('returns zero when there is no trend', () => { | ||
const candles = [ | ||
{high: 100, low: 90, close: 95}, | ||
{high: 100, low: 90, close: 95}, | ||
{high: 100, low: 90, close: 95}, | ||
{high: 100, low: 90, close: 95}, | ||
{high: 100, low: 90, close: 95}, | ||
]; | ||
|
||
const dx = new DX(5); | ||
|
||
for (const candle of candles) { | ||
dx.update(candle); | ||
} | ||
|
||
expect(dx.isStable).toBeTrue(); | ||
expect(dx.getResult().valueOf()).toBe('0'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import {BigIndicatorSeries} from '../Indicator'; | ||
import {HighLowClose} from '../util'; | ||
import Big from 'big.js'; | ||
import {MovingAverage} from '../MA/MovingAverage'; | ||
import {MovingAverageTypeContext} from '../MA/MovingAverageTypeContext'; | ||
import {WSMA} from '../WSMA/WSMA'; | ||
import {ATR} from '../ATR/ATR'; | ||
|
||
/** | ||
* Directional Movement Index (DMI / DX) | ||
* Type: Momentum | ||
* | ||
* The DX was developed by **John Welles Wilder, Jr.**. and may help traders assess the strength of a trend but NOT the | ||
* direction of the trend. | ||
* | ||
* If there is no change in the trend, then the DX is `0`. The return value increases when there is a stronger trend | ||
* (either negative or positive). When +DI is above -DI, then there is more upward pressure than downward pressure in | ||
* the market. | ||
* | ||
* @see https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/dmi | ||
*/ | ||
export class DX extends BigIndicatorSeries { | ||
private readonly movesUp: MovingAverage; | ||
private readonly movesDown: MovingAverage; | ||
private previousCandle?: HighLowClose; | ||
private readonly atr: ATR; | ||
|
||
constructor(public readonly interval: number, SmoothingIndicator: MovingAverageTypeContext = WSMA) { | ||
super(); | ||
this.movesUp = new SmoothingIndicator(this.interval); | ||
this.movesDown = new SmoothingIndicator(this.interval); | ||
this.atr = new ATR(this.interval, SmoothingIndicator); | ||
} | ||
|
||
update(candle: HighLowClose): Big | void { | ||
if (!this.previousCandle) { | ||
this.atr.update(candle); | ||
this.movesUp.update(new Big(0)); | ||
this.movesDown.update(new Big(0)); | ||
this.previousCandle = candle; | ||
return; | ||
} | ||
|
||
const currentHigh = new Big(candle.high); | ||
const previousHigh = new Big(this.previousCandle.high); | ||
|
||
const currentLow = new Big(candle.low); | ||
const previousLow = new Big(this.previousCandle.low); | ||
|
||
const higherHigh = currentHigh.minus(previousHigh); | ||
const lowerLow = previousLow.minus(currentLow); | ||
|
||
const noHigherHighs = higherHigh.lt(0); | ||
const lowsRiseFaster = higherHigh.lt(lowerLow); | ||
|
||
// Plus Directional Movement (+DM) | ||
const pdm = noHigherHighs || lowsRiseFaster ? new Big(0) : higherHigh; | ||
|
||
const noLowerLows = lowerLow.lt(0); | ||
const highsRiseFaster = lowerLow.lt(higherHigh); | ||
|
||
// Minus Directional Movement (-DM) | ||
const mdm = noLowerLows || highsRiseFaster ? new Big(0) : lowerLow; | ||
|
||
this.movesUp.update(pdm); | ||
this.movesDown.update(mdm); | ||
this.atr.update(candle); | ||
this.previousCandle = candle; | ||
|
||
if (this.movesUp.isStable) { | ||
// Plus Directional Indicator (+DI) | ||
const pdi = this.movesUp.getResult().div(this.atr.getResult()); | ||
// Minus Directional Indicator (-DI) | ||
const mdi = this.movesDown.getResult().div(this.atr.getResult()); | ||
|
||
const dmDiff = pdi.minus(mdi).abs(); | ||
const dmSum = pdi.plus(mdi); | ||
|
||
// Prevent division by zero | ||
if (dmSum.eq(0)) { | ||
return this.setResult(new Big(0)); | ||
} | ||
|
||
return this.setResult(dmDiff.div(dmSum).mul(100)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters