Skip to content

Commit

Permalink
fix(SMMA,RSI,ATR,ADX): Don't cache more prices than necessary to fill…
Browse files Browse the repository at this point in the history
… the interval (#307)
  • Loading branch information
bennycode authored Aug 28, 2021
1 parent 6cbb395 commit 2bb0a63
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 34 deletions.
6 changes: 3 additions & 3 deletions src/ADX/ADX.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Big as BigNumber} from 'big.js';
import {Big} from 'big.js';
import {ADX} from './ADX';

import data from '../test/fixtures/ADX/data.json';
Expand All @@ -15,8 +15,8 @@ describe('ADX', () => {
indicator.update(candle);

if (indicator.isStable) {
const res = new BigNumber(adx14results[index] as number);
expect(indicator.getResult().adx.toFixed(4)).toEqual(res.toFixed(4));
const result = new Big(adx14results[index] || 0);
expect(indicator.getResult().adx.toFixed(4)).toEqual(result.toFixed(4));
}
});
});
Expand Down
17 changes: 17 additions & 0 deletions src/BANDS/BollingerBands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ import data from '../test/fixtures/BB/data.json';
import {NotEnoughDataError} from '../error';

describe('BollingerBands', () => {
describe('prices', () => {
it('does not cache more prices than necessary to fill the interval', () => {
const bb = new BollingerBands(3);
bb.update(1);
bb.update(2);
expect(bb.prices.length).toBe(2);
bb.update(3);
expect(bb.prices.length).toBe(3);
bb.update(4);
expect(bb.prices.length).toBe(3);
bb.update(5);
expect(bb.prices.length).toBe(3);
bb.update(6);
expect(bb.prices.length).toBe(3);
});
});

describe('getResult', () => {
it('calculates Bollinger Bands with interval 20', () => {
const bb = new BollingerBands(20);
Expand Down
2 changes: 1 addition & 1 deletion src/BANDS/BollingerBands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {Indicator} from '../Indicator';
import {getAverage} from '../util/getAverage';

export class BollingerBands implements Indicator<BandsResult> {
public readonly prices: Big[] = [];
private readonly middleSMA: SMA;
private readonly prices: Big[] = [];
private result: BandsResult | undefined;

constructor(public readonly interval: number = 0, public readonly deviationMultiplier: number = 2) {
Expand Down
17 changes: 17 additions & 0 deletions src/CG/CG.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@ import {NotEnoughDataError} from '../error';
import {CG} from './CG';

describe('CG', () => {
describe('prices', () => {
it('does not cache more prices than necessary to fill the interval', () => {
const cg = new CG(3, 6);
cg.update(1);
cg.update(2);
expect(cg.prices.length).toBe(2);
cg.update(3);
expect(cg.prices.length).toBe(3);
cg.update(4);
expect(cg.prices.length).toBe(3);
cg.update(5);
expect(cg.prices.length).toBe(3);
cg.update(6);
expect(cg.prices.length).toBe(3);
});
});

describe('isStable', () => {
it('is stable when the inputs can fill the signal interval ', () => {
const cg = new CG(5, 6);
Expand Down
2 changes: 1 addition & 1 deletion src/CG/CG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {NotEnoughDataError} from '../error';
export class CG extends SimpleIndicator {
public signal: SMA;

private readonly prices: Big[] = [];
public readonly prices: Big[] = [];

get isStable(): boolean {
return this.prices.length >= this.interval && this.signal.isStable;
Expand Down
18 changes: 9 additions & 9 deletions src/ROC/ROC.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Big as BigNumber} from 'big.js';
import {Big} from 'big.js';
import {ROC} from './ROC';
import {NotEnoughDataError} from '../error';

Expand Down Expand Up @@ -47,13 +47,13 @@ describe('ROC', () => {
const roc = new ROC(5);

prices.forEach((price, index) => {
roc.update(new BigNumber(price));
roc.update(new Big(price));

if (!roc.isStable) {
return;
}

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

Expand All @@ -79,12 +79,12 @@ describe('ROC', () => {
const indicator = new ROC(interval);

const mockedPrices = [
new BigNumber('0.00019040'),
new BigNumber('0.00019071'),
new BigNumber('0.00019198'),
new BigNumber('0.00019220'),
new BigNumber('0.00019214'),
new BigNumber('0.00019205'),
new Big('0.00019040'),
new Big('0.00019071'),
new Big('0.00019198'),
new Big('0.00019220'),
new Big('0.00019214'),
new Big('0.00019205'),
];

expect(mockedPrices.length).toBe(interval + 1);
Expand Down
26 changes: 17 additions & 9 deletions src/RSI/RSI.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Big as BigNumber} from 'big.js';
import {Big} from 'big.js';
import {RSI} from './RSI';
import {NotEnoughDataError, SMMA} from '..';

Expand Down Expand Up @@ -30,7 +30,7 @@ describe('RSI', () => {
prices.forEach((price, index) => {
rsi.update(price);
if (rsi.isStable) {
const expected = new BigNumber(rsi2results[index]);
const expected = new Big(rsi2results[index]);
expect(rsi.getResult().toPrecision(12)).toEqual(expected.toPrecision(12));
}
});
Expand All @@ -45,7 +45,7 @@ describe('RSI', () => {
prices.forEach((price, index) => {
rsi.update(price);
if (rsi.isStable) {
const expected = new BigNumber(rsi12results[index]);
const expected = new Big(rsi12results[index]);
expect(rsi.getResult().toPrecision(12)).toEqual(expected.toPrecision(12));
}
});
Expand All @@ -60,7 +60,7 @@ describe('RSI', () => {
prices.forEach((price, index) => {
rsi.update(price);
if (rsi.isStable) {
const expected = new BigNumber(rsi26results[index]);
const expected = new Big(rsi26results[index]);
expect(rsi.getResult().toPrecision(12)).toEqual(expected.toPrecision(12));
}
});
Expand Down Expand Up @@ -99,12 +99,20 @@ describe('RSI', () => {
});

describe('isStable', () => {
it('is stable when the amount of inputs is higher than the required interval', () => {
const rsi = new RSI(14);
rsi.update('62.69000000');
rsi.update('62.71000000');
rsi.update('62.29000000');
// Test vectors taken from: https://tulipindicators.org/rsi
it('is stable when the amount of inputs is bigger than the required interval', () => {
const rsi = new RSI(5);
rsi.update(81.59);
rsi.update(81.06);
rsi.update(82.87);
expect(rsi.isStable).toBeFalse();
rsi.update(83.0);
rsi.update(83.61);
rsi.update(83.15);
expect(rsi.isStable).toBeTrue();
expect(rsi.getResult().toFixed(2)).toBe('72.03');
rsi.update(82.84);
expect(rsi.getResult().toFixed(2)).toBe('64.93');
});
});
});
6 changes: 4 additions & 2 deletions src/RSI/RSI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {MovingAverage} from '../MA/MovingAverage';
import {SimpleIndicator} from '../Indicator';

export class RSI extends SimpleIndicator {
private readonly prices: Big[] = [];
public readonly prices: Big[] = [];
private readonly avgGain: MovingAverage;
private readonly avgLoss: MovingAverage;

Expand Down Expand Up @@ -47,7 +47,9 @@ export class RSI extends SimpleIndicator {
const max = new Big(100);
this.setResult(max.minus(max.div(relativeStrength.add(1))));

this.prices.shift();
while (this.prices.length > this.interval) {
this.prices.shift();
}
}

getResult(): Big {
Expand Down
17 changes: 17 additions & 0 deletions src/SMA/SMA.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ const SMA12results = results.weight_12;
const SMA26results = results.weight_26;

describe('SMA', () => {
describe('prices', () => {
it('does not cache more prices than necessary to fill the interval', () => {
const sma = new SMA(3);
sma.update(1);
sma.update(2);
expect(sma.prices.length).toBe(2);
sma.update(3);
expect(sma.prices.length).toBe(3);
sma.update(4);
expect(sma.prices.length).toBe(3);
sma.update(5);
expect(sma.prices.length).toBe(3);
sma.update(6);
expect(sma.prices.length).toBe(3);
});
});

describe('isStable', () => {
it('knows when there is enough input data', () => {
const sma = new SMA(3);
Expand Down
2 changes: 1 addition & 1 deletion src/SMA/SMA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Big, {BigSource} from 'big.js';
import {MovingAverage} from '../MA/MovingAverage';

export class SMA extends MovingAverage {
private readonly prices: Big[] = [];
public readonly prices: Big[] = [];

update(price: BigSource): void {
this.prices.push(new Big(price));
Expand Down
31 changes: 24 additions & 7 deletions src/SMMA/SMMA.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Big as BigNumber} from 'big.js';
import {Big} from 'big.js';
import {SMMA} from './SMMA';

import prices from '../test/fixtures/prices.json';
Expand All @@ -9,13 +9,30 @@ const smma12results = results.interval_12;
const smma26results = results.interval_26;

describe('SMMA', () => {
describe('prices', () => {
it('does not cache more prices than necessary to fill the interval', () => {
const smma = new SMMA(3);
smma.update(1);
smma.update(2);
expect(smma.prices.length).toBe(2);
smma.update(3);
expect(smma.prices.length).toBe(3);
smma.update(4);
expect(smma.prices.length).toBe(3);
smma.update(5);
expect(smma.prices.length).toBe(3);
smma.update(6);
expect(smma.prices.length).toBe(3);
});
});

describe('getResult', () => {
it('calculates SMMAs with interval 2', () => {
const smma = new SMMA(2);

prices.forEach((price, index) => {
smma.update(new BigNumber(price));
const expected = new BigNumber(smma2results[index]);
smma.update(new Big(price));
const expected = new Big(smma2results[index]);
expect(smma.getResult().toPrecision(12)).toEqual(expected.toPrecision(12));
});

Expand All @@ -27,8 +44,8 @@ describe('SMMA', () => {
const smma = new SMMA(12);

prices.forEach((price, index) => {
smma.update(new BigNumber(price));
const expected = new BigNumber(smma12results[index]);
smma.update(new Big(price));
const expected = new Big(smma12results[index]);
expect(smma.getResult().toPrecision(12)).toEqual(expected.toPrecision(12));
});

Expand All @@ -40,8 +57,8 @@ describe('SMMA', () => {
const smma = new SMMA(26);

prices.forEach((price, index) => {
smma.update(new BigNumber(price));
const expected = new BigNumber(smma26results[index]);
smma.update(new Big(price));
const expected = new Big(smma26results[index]);
expect(smma.getResult().toPrecision(12)).toEqual(expected.toPrecision(12));
});

Expand Down
6 changes: 5 additions & 1 deletion src/SMMA/SMMA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {SMA} from '../';
import {MovingAverage} from '../MA/MovingAverage';

class SMMA extends MovingAverage {
private readonly prices: Big[] = [];
public readonly prices: Big[] = [];
private readonly sma: SMA;

constructor(public readonly interval: number) {
Expand All @@ -27,6 +27,10 @@ class SMMA extends MovingAverage {
.div(this.interval)
);
}

if (this.prices.length > this.interval) {
this.prices.shift();
}
}

getResult(): Big {
Expand Down

0 comments on commit 2bb0a63

Please sign in to comment.