Skip to content

Commit

Permalink
feat(ROC): Add faster implementation (#385)
Browse files Browse the repository at this point in the history
  • Loading branch information
bennycode authored Dec 27, 2021
1 parent 51c7096 commit 1064ebb
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 62 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ All indicators can be updated over time by streaming data (prices or candles) to
1. Dual Moving Average (DMA)
1. Exponential Moving Average (EMA)
1. Mean Absolute Deviation (MAD)
1. Momentum (MOM)
1. Momentum (MOM / MTM)
1. Moving Average Convergence Divergence (MACD)
1. Rate-of-Change (ROC)
1. Relative Strength Index (RSI)
Expand Down
3 changes: 2 additions & 1 deletion src/MOM/MOM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import Big, {BigSource} from 'big.js';
import {getFixedArray} from '../util/getFixedArray';

/**
* Momentum Indicator (MOM)
* Momentum Indicator (MOM / MTM)
* Type: Momentum
*
* The Momentum indicator returns the change between the current price and the price n times ago.
*
* @see https://en.wikipedia.org/wiki/Momentum_(technical_analysis)
* @see https://www.warriortrading.com/momentum-indicator/
*/
export class MOM extends BigIndicatorSeries {
Expand Down
63 changes: 18 additions & 45 deletions src/ROC/ROC.test.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,39 @@
import {Big} from 'big.js';
import {ROC} from './ROC';
import {FasterROC, ROC} from './ROC';
import {NotEnoughDataError} from '../error';

describe('ROC', () => {
describe('getResult', () => {
it('identifies an up-trending asset by a positive ROC', () => {
// Test data verified with:
// https://tulipindicators.org/roc
const prices = [
81.59, 81.06, 82.87, 83.0, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29,
];
/*************************************************
Result calculation:
Close | Calculation | Result
------------------------------------------------
81.59 | ----------------------- | Unstable (null)
81.06 | ----------------------- | Unstable (null)
82.87 | ----------------------- | Unstable (null)
83.00 | ----------------------- | Unstable (null)
83.61 | ----------------------- | Unstable (null)
83.15 | (83.15 - 81.59) / 81.59 | 0.01911999019
82.84 | (82.84 - 81.06) / 81.06 | 0.02195904268
83.99 | (83.99 - 82.87) / 82.87 | 0.0135151442
84.55 | (84.55 - 83.00) / 83.00 | 0.01867469879
84.36 | (84.36 - 83.61) / 83.61 | 0.00897021887
85.53 | (85.53 - 83.15) / 83.15 | 0.02862297053
86.54 | (86.54 - 82.84) / 82.84 | 0.04466441332
86.89 | (86.89 - 83.99) / 83.99 | 0.03452791999
87.77 | (87.77 - 84.55) / 84.55 | 0.03808397397
87.29 | (87.29 - 84.36) / 84.36 | 0.03473210052
*************************************************/
const results = [
null,
null,
null,
null,
null,
0.01911999019,
0.02195904268,
0.0135151442,
0.01867469879,
0.00897021887,
0.02862297053,
0.04466441332,
0.03452791999,
0.03808397397,
0.03473210052,

const expectations = [
0.01911999019, 0.02195904268, 0.0135151442, 0.01867469879, 0.00897021887, 0.02862297053, 0.04466441332,
0.03452791999, 0.03808397397, 0.03473210052,
];

const roc = new ROC(5);
const fasterROC = new FasterROC(5);

prices.forEach((price, index) => {
roc.update(new Big(price));
for (const price of prices) {
roc.update(price);
fasterROC.update(price);

if (!roc.isStable) {
return;
if (roc.isStable) {
const expected = expectations.shift()!;
expect(roc.getResult().toFixed(2)).toEqual(expected.toFixed(2));
}

const expected = new Big(Number(results[index]));
expect(roc.getResult().toFixed(2)).toEqual(expected.toFixed(2));
});
}

expect(roc.lowest!.toFixed(2)).toBe('0.01');
expect(fasterROC.lowest!.toFixed(2)).toBe('0.01');

expect(roc.highest!.toFixed(2)).toBe('0.04');
expect(fasterROC.highest!.toFixed(2)).toBe('0.04');
});

it('identifies a down-trending asset by a negative ROC', () => {
Expand Down
42 changes: 27 additions & 15 deletions src/ROC/ROC.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Big, {BigSource} from 'big.js';
import {BigIndicatorSeries} from '../Indicator';
import {BigIndicatorSeries, NumberIndicatorSeries} from '../Indicator';

/**
* Rate Of Change Indicator (ROC)
Expand All @@ -11,29 +11,41 @@ import {BigIndicatorSeries} from '../Indicator';
* @see https://www.investopedia.com/terms/r/rateofchange.asp
*/
export class ROC extends BigIndicatorSeries {
private readonly priceHistory: BigSource[] = [];
public readonly prices: Big[] = [];

constructor(public readonly interval: number) {
super();
this.interval = interval;
}

override update(price: BigSource): Big | void {
this.priceHistory.push(price);
if (this.priceHistory.length <= this.interval) {
/**
* The priceHistory needs to have N prices in it before a result can be calculated with the following value. For
* an interval of 5, the first result can be given on the 6th value.
*/
return;
}
this.prices.push(new Big(price));

/**
* Take the price this.interval periods ago.
* The priceHistory needs to have N prices in it before a result can be calculated with the following value. For
* an interval of 5, the first result can be given on the 6th value.
*/
const comparePrice = this.priceHistory.shift() as Big;
if (this.prices.length > this.interval) {
const comparePrice = this.prices.shift()!;

// (Close - Close <interval> periods ago) / (Close <interval> periods ago)
return this.setResult(new Big(price).sub(comparePrice).div(comparePrice));
return this.setResult(new Big(price).sub(comparePrice).div(comparePrice));
}
}
}

export class FasterROC extends NumberIndicatorSeries {
public readonly prices: number[] = [];

constructor(public readonly interval: number) {
super();
}

override update(price: number): void | number {
this.prices.push(price);

if (this.prices.length > this.interval) {
const comparePrice = this.prices.shift()!;

return this.setResult((price - comparePrice) / comparePrice);
}
}
}
28 changes: 28 additions & 0 deletions src/start/startBenchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
BollingerBandsWidth,
CCI,
CG,
DEMA,
DMA,
DX,
EMA,
Expand All @@ -22,13 +23,15 @@ import {
FasterBollingerBandsWidth,
FasterCCI,
FasterCG,
FasterDEMA,
FasterDMA,
FasterDX,
FasterEMA,
FasterMACD,
FasterMAD,
FasterMOM,
FasterPeriod,
FasterROC,
FasterRSI,
FasterSMA,
FasterStochasticRSI,
Expand All @@ -43,6 +46,7 @@ import {
MAD,
MOM,
Period,
ROC,
RSI,
SMA,
StochasticRSI,
Expand Down Expand Up @@ -169,6 +173,18 @@ new Benchmark.Suite('Technical Indicators')
fasterCG.update(price);
}
})
.add('DEMA', () => {
const dema = new DEMA(interval);
for (const price of prices) {
dema.update(price);
}
})
.add('FasterDEMA', () => {
const fasterDEMA = new FasterDEMA(interval);
for (const price of prices) {
fasterDEMA.update(price);
}
})
.add('DMA', () => {
const dma = new DMA(3, 6);
for (const price of prices) {
Expand Down Expand Up @@ -258,6 +274,18 @@ new Benchmark.Suite('Technical Indicators')
fasterPeriod.update(price);
}
})
.add('ROC', () => {
const roc = new ROC(interval);
for (const price of prices) {
roc.update(price);
}
})
.add('FasterROC', () => {
const fasterROC = new FasterROC(interval);
for (const price of prices) {
fasterROC.update(price);
}
})
.add('RSI', () => {
const rsi = new RSI(interval);
for (const price of prices) {
Expand Down

0 comments on commit 1064ebb

Please sign in to comment.