Skip to content

Commit

Permalink
feat(util): Calculate streaks (#725)
Browse files Browse the repository at this point in the history
  • Loading branch information
bennycode authored Dec 6, 2024
1 parent c847d3d commit 0f80739
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 9 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ sma.update('10');
sma.replace('40');

// You can add arbitrary-precision decimals:
sma.update(new Big(30));
sma.update(new Big(30.0009));

// You can get the result in various formats:
console.log(sma.getResult().toFixed(2)); // "40.00"
console.log(sma.getResult().toFixed(4)); // "40.0000"
console.log(sma.getResult().toFixed(2)); // "50.00"
console.log(sma.getResult().toFixed(4)); // "50.0003"
```

### When to use `update(...)`?
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"release:major": "generate-changelog -M -x \"chore,test\" && npm run changelog:commit && npm run docs:release && npm version major",
"release:minor": "generate-changelog -m -x \"chore,test\" && npm run changelog:commit && npm run docs:release && npm version minor",
"release:patch": "generate-changelog -p -x \"chore,test\" && npm run changelog:commit && npm run docs:release && npm version patch",
"start:benchmark": "ts-node ./src/start/startBenchmark.ts",
"start:benchmark": "tsc --noEmit && node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only ./src/start/startBenchmark.ts",
"test": "npm run test:dev -- --coverage",
"test:dev": "vitest run --passWithNoTests",
"test:types": "npm run lint:types"
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export {default as Big, BigSource} from 'big.js';
export {default as Big, type BigSource} from 'big.js';
export * from './ABANDS/AccelerationBands.js';
export * from './AC/AC.js';
export * from './ADX/ADX.js';
Expand Down
36 changes: 36 additions & 0 deletions src/util/getStreaks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {getStreaks} from './getStreaks.js';

describe('getStreaks', () => {
const input = [10, 20, 30, 40, 32, 42, 50, 45, 44, 41, 59, 90, 100];

describe('uptrends', () => {
it('keeps track of upward streak lengths', () => {
const actual = getStreaks(input, 'up');
expect(actual.map(s => s.length)).toStrictEqual([3, 2, 3]);
});

it('keeps track of price increases during an upward streak', () => {
const actual = getStreaks(input, 'up');
expect(actual.map(s => s.percentage)).toStrictEqual([300, 56.25, 143.90243902439025]);
});
});

describe('downtrends', () => {
it('keeps track of downward streak lengths', () => {
const actual = getStreaks(input, 'down');
expect(actual.map(s => s.length)).toStrictEqual([1, 3]);
});

it('keeps track of price decreases during a downward streak', () => {
const actual = getStreaks(input, 'down');
expect(actual.map(s => s.percentage)).toStrictEqual([-20, -18]);
});
});

describe('special cases', () => {
it("doesn't record a streak of 1", () => {
const actual = getStreaks([1], 'up');
expect(actual.map(s => s.length)).toStrictEqual([]);
});
});
});
47 changes: 47 additions & 0 deletions src/util/getStreaks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
export type Streak = {
/** Length of the streak */
length: number;
/** Price change percentage during the streak */
percentage: number;
};

/**
* Tracks the lengths (streaks) of continuous price movements (up or down).
*
* @param prices A series of prices
* @param keepSide If you want to receive only uptrends or downtrends
* @returns An array of objects representing the filtered streaks
*/
export function getStreaks(prices: number[], keepSide: 'up' | 'down'): Streak[] {
const streaks: Streak[] = [];
let currentStreak = 0;

function saveStreak(i: number) {
const endPrice = prices[i - 1];
const startPrice = prices[i - currentStreak - 1];
const percentage = ((endPrice - startPrice) / startPrice) * 100;
streaks.push({length: currentStreak, percentage});
}

for (let i = 1; i < prices.length; i++) {
const isUpward = keepSide === 'up' && prices[i] > prices[i - 1];
const isDownward = keepSide === 'down' && prices[i] < prices[i - 1];
if (isUpward || isDownward) {
currentStreak++;
} else {
// Save the streak if it ends
if (currentStreak > 0) {
saveStreak(i);
}
// Reset the streak
currentStreak = 0;
}
}

// Append the final streak if it exists
if (currentStreak > 0) {
saveStreak(prices.length);
}

return streaks;
}
10 changes: 6 additions & 4 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import {defineConfig} from 'vitest/config';
export default defineConfig({
test: {
coverage: {
branches: 0,
functions: 0,
include: ['**/*.{ts,tsx}', '!**/*.d.ts', '!**/cli.ts', '!**/index.ts', '!**/start*.ts'],
lines: 0,
provider: 'v8',
reporter: ['html', 'lcov', 'text'],
statements: 0,
thresholds: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
environment: 'node',
globals: true,
Expand Down

0 comments on commit 0f80739

Please sign in to comment.