Skip to content

Commit

Permalink
feat: Add Standard Deviation (#337)
Browse files Browse the repository at this point in the history
  • Loading branch information
bennycode authored Oct 24, 2021
1 parent 2029f1c commit b89dbcd
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 4 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ The "trading-signals" library provides a TypeScript implementation for common te
1. Stochastic Oscillator (STOCH)
1. Wilder's Smoothed Moving Average (WSMA)

Utility Methods:

1. Average
1. Standard Deviation
1. Rolling Standard Deviation

## Usage

```typescript
Expand Down
5 changes: 2 additions & 3 deletions src/BBANDS/BollingerBands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {SMA} from '../SMA/SMA';
import {NotEnoughDataError} from '../error';
import {BandsResult} from '../util/BandsResult';
import {Indicator} from '../Indicator';
import {getAverage} from '../util/getAverage';
import {getStandardDeviation} from '../util';

/**
* Bollinger Bands (BBANDS)
Expand Down Expand Up @@ -42,8 +42,7 @@ export class BollingerBands implements Indicator<BandsResult> {
}

const middle = SMA.getResultFromBatch(this.prices);
const squareDiffs = this.prices.map((price: Big) => price.sub(middle).pow(2));
const standardDeviation = getAverage(squareDiffs).sqrt();
const standardDeviation = getStandardDeviation(this.prices, middle);

this.result = {
lower: middle.sub(standardDeviation.times(this.deviationMultiplier)),
Expand Down
8 changes: 7 additions & 1 deletion src/start/startBenchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {BollingerBands} from '../BBANDS/BollingerBands';
import {SMA} from '../SMA/SMA';
import {FasterSMA} from '../SMA/FasterSMA';
import candles from '../test/fixtures/candles/100-candles.json';
import {fasterGetAverage, getAverage} from '../util';
import {fasterGetAverage, getAverage, getFasterStandardDeviation, getStandardDeviation} from '../util';

const interval = 20;
const prices = candles.map(candle => parseInt(candle.close, 10));
Expand Down Expand Up @@ -34,6 +34,12 @@ new Benchmark.Suite('Technical Indicators')
.add('fasterGetAverage', () => {
return fasterGetAverage(prices);
})
.add('getStandardDeviation', () => {
return getStandardDeviation(prices);
})
.add('getFasterStandardDeviation', () => {
return getFasterStandardDeviation(prices);
})
.on('cycle', (event: Event) => {
console.info(String(event.target));
})
Expand Down
30 changes: 30 additions & 0 deletions src/util/getStandardDeviation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {SMA} from '../SMA/SMA';
import {getFasterStandardDeviation, getStandardDeviation} from './getStandardDeviation';

describe('getStandardDeviation', () => {
it('returns the standard deviation', () => {
const prices = [9, 2, 5, 4, 12, 7, 8, 11, 9, 3, 7, 4, 12, 5, 4, 10, 9, 6, 9, 4];
const std = getStandardDeviation(prices);
expect(std.toFixed(2)).toBe('2.98');
});

it('can be used to calculate a "Window Rolling Standard Deviation / Standard Deviation Over Period"', () => {
// Test data taken from:
// https://github.com/TulipCharts/tulipindicators/blob/v0.8.0/tests/untest.txt#L367-L369
const prices = [81.59, 81.06, 82.87, 83.0, 83.61];
const average = SMA.getResultFromBatch(prices);
const stdDev = getStandardDeviation(prices, average);
expect(stdDev.toFixed(2)).toBe('0.95');
});
});

describe('getFasterStandardDeviation', () => {
it('only works with the primitive data type number', () => {
const prices = [9, 2, 5, 4, 12, 7, 8, 11, 9, 3, 7, 4, 12, 5, 4, 10, 9, 6, 9, 4];
const std = getFasterStandardDeviation(prices);
expect(std.toFixed(2)).toBe('2.98');
const fivePrices = [81.59, 81.06, 82.87, 83.0, 83.61];
const stdDev = getStandardDeviation(fivePrices, SMA.getResultFromBatch(fivePrices));
expect(stdDev.toFixed(2)).toBe('0.95');
});
});
22 changes: 22 additions & 0 deletions src/util/getStandardDeviation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {fasterGetAverage, getAverage} from './getAverage';
import Big, {BigSource} from 'big.js';

/**
* Standard deviation calculates how prices for a collection of prices are spread out from the average price of these
* prices.
*
* @see https://www.mathsisfun.com/data/standard-deviation-formulas.html
* @see https://www.youtube.com/watch?v=9-8E8L_77-8
*/
export function getStandardDeviation(values: BigSource[], average?: BigSource): Big {
const middle = average || getAverage(values);
const squaredDifferences = values.map((value: BigSource) => new Big(value).sub(middle).pow(2));
return getAverage(squaredDifferences).sqrt();
}

export function getFasterStandardDeviation(values: number[], average?: number): number {
const middle = average || fasterGetAverage(values);
const squaredDifferences = values.map(value => value - middle).map(value => value * value);
const averageDifference = fasterGetAverage(squaredDifferences);
return Math.sqrt(averageDifference);
}
1 change: 1 addition & 0 deletions src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from './getAverage';
export * from './getFixedArray';
export * from './getMaximum';
export * from './getMinimum';
export * from './getStandardDeviation';
export * from './HighLowClose';

0 comments on commit b89dbcd

Please sign in to comment.