From 3675ea913a2b4daec8d4b661138ede1ddbf00b54 Mon Sep 17 00:00:00 2001 From: Benny Neugebauer Date: Sat, 23 Nov 2024 00:27:39 +0100 Subject: [PATCH 1/7] feat(util): Calculate upward streaks --- src/util/calculateUpwardStreaks.test.ts | 9 +++++++++ src/util/calculateUpwardStreaks.ts | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/util/calculateUpwardStreaks.test.ts create mode 100644 src/util/calculateUpwardStreaks.ts diff --git a/src/util/calculateUpwardStreaks.test.ts b/src/util/calculateUpwardStreaks.test.ts new file mode 100644 index 000000000..f0b5d8f3b --- /dev/null +++ b/src/util/calculateUpwardStreaks.test.ts @@ -0,0 +1,9 @@ +import {calculateUpwardStreaks} from './calculateUpwardStreaks.js'; + +describe('calculateUpwardStreaks', () => { + it('keeps an array of upward combinations', () => { + const input = [10, 20, 30, 40, 32, 42, 50, 45, 44, 41, 59, 90, 100]; + const actual = calculateUpwardStreaks(input); + expect(actual).toStrictEqual([3, 2, 3]); + }); +}); diff --git a/src/util/calculateUpwardStreaks.ts b/src/util/calculateUpwardStreaks.ts new file mode 100644 index 000000000..be41b7a33 --- /dev/null +++ b/src/util/calculateUpwardStreaks.ts @@ -0,0 +1,25 @@ +export function calculateUpwardStreaks(prices: number[]) { + const streaks: number[] = []; + let currentStreak = 0; + + for (let i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + // Increment streak if the current price is higher + currentStreak++; + } else { + // Save the streak if it ends + if (currentStreak > 0) { + streaks.push(currentStreak); + } + // Reset the streak + currentStreak = 0; + } + } + + // Append the final streak if it exists + if (currentStreak > 0) { + streaks.push(currentStreak); + } + + return streaks; +} From af445c7defa88bdee2d092412ece29c17edbee65 Mon Sep 17 00:00:00 2001 From: Benny Neugebauer Date: Sat, 23 Nov 2024 00:33:44 +0100 Subject: [PATCH 2/7] use up and down --- src/util/calculateUpwardStreaks.test.ts | 9 --------- src/util/getStreaks.test.ts | 15 +++++++++++++++ .../{calculateUpwardStreaks.ts => getStreaks.ts} | 7 ++++--- 3 files changed, 19 insertions(+), 12 deletions(-) delete mode 100644 src/util/calculateUpwardStreaks.test.ts create mode 100644 src/util/getStreaks.test.ts rename src/util/{calculateUpwardStreaks.ts => getStreaks.ts} (65%) diff --git a/src/util/calculateUpwardStreaks.test.ts b/src/util/calculateUpwardStreaks.test.ts deleted file mode 100644 index f0b5d8f3b..000000000 --- a/src/util/calculateUpwardStreaks.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {calculateUpwardStreaks} from './calculateUpwardStreaks.js'; - -describe('calculateUpwardStreaks', () => { - it('keeps an array of upward combinations', () => { - const input = [10, 20, 30, 40, 32, 42, 50, 45, 44, 41, 59, 90, 100]; - const actual = calculateUpwardStreaks(input); - expect(actual).toStrictEqual([3, 2, 3]); - }); -}); diff --git a/src/util/getStreaks.test.ts b/src/util/getStreaks.test.ts new file mode 100644 index 000000000..068b59c9c --- /dev/null +++ b/src/util/getStreaks.test.ts @@ -0,0 +1,15 @@ +import {getStreaks} from './getStreaks.js'; + +describe('getStreaks', () => { + const input = [10, 20, 30, 40, 32, 42, 50, 45, 44, 41, 59, 90, 100]; + + it('keeps an array of upward combinations', () => { + const actual = getStreaks(input, 'up'); + expect(actual).toStrictEqual([3, 2, 3]); + }); + + it('keeps an array of upward combinations', () => { + const actual = getStreaks(input, 'down'); + expect(actual).toStrictEqual([1, 3]); + }); +}); diff --git a/src/util/calculateUpwardStreaks.ts b/src/util/getStreaks.ts similarity index 65% rename from src/util/calculateUpwardStreaks.ts rename to src/util/getStreaks.ts index be41b7a33..8197abdb7 100644 --- a/src/util/calculateUpwardStreaks.ts +++ b/src/util/getStreaks.ts @@ -1,10 +1,11 @@ -export function calculateUpwardStreaks(prices: number[]) { +export function getStreaks(prices: number[], side: 'up' | 'down') { const streaks: number[] = []; let currentStreak = 0; for (let i = 1; i < prices.length; i++) { - if (prices[i] > prices[i - 1]) { - // Increment streak if the current price is higher + const isUpward = side === 'up' && prices[i] > prices[i - 1]; + const isDownward = side === 'down' && prices[i] < prices[i - 1]; + if (isUpward || isDownward) { currentStreak++; } else { // Save the streak if it ends From 6c2b9553329df891f785dd24fcde19e4fe10b853 Mon Sep 17 00:00:00 2001 From: Benny Neugebauer Date: Mon, 25 Nov 2024 15:30:05 +0100 Subject: [PATCH 3/7] add percentage --- src/util/getStreaks.test.ts | 18 ++++++++++++++---- src/util/getStreaks.ts | 21 +++++++++++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/util/getStreaks.test.ts b/src/util/getStreaks.test.ts index 068b59c9c..c42fbb260 100644 --- a/src/util/getStreaks.test.ts +++ b/src/util/getStreaks.test.ts @@ -3,13 +3,23 @@ import {getStreaks} from './getStreaks.js'; describe('getStreaks', () => { const input = [10, 20, 30, 40, 32, 42, 50, 45, 44, 41, 59, 90, 100]; - it('keeps an array of upward combinations', () => { + it('keeps track of upward streak lengths', () => { const actual = getStreaks(input, 'up'); - expect(actual).toStrictEqual([3, 2, 3]); + expect(actual.map(s => s.length)).toStrictEqual([3, 2, 3]); }); - it('keeps an array of upward combinations', () => { + 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]); + }); + + 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).toStrictEqual([1, 3]); + expect(actual.map(s => s.percentage)).toStrictEqual([-20, -18]); }); }); diff --git a/src/util/getStreaks.ts b/src/util/getStreaks.ts index 8197abdb7..ae41be262 100644 --- a/src/util/getStreaks.ts +++ b/src/util/getStreaks.ts @@ -1,7 +1,20 @@ -export function getStreaks(prices: number[], side: 'up' | 'down') { - const streaks: number[] = []; +export type Streak = { + length: number; + /** Price change percentage during the streak */ + percentage: number; +}; + +export function getStreaks(prices: number[], side: '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 = side === 'up' && prices[i] > prices[i - 1]; const isDownward = side === 'down' && prices[i] < prices[i - 1]; @@ -10,7 +23,7 @@ export function getStreaks(prices: number[], side: 'up' | 'down') { } else { // Save the streak if it ends if (currentStreak > 0) { - streaks.push(currentStreak); + saveStreak(i); } // Reset the streak currentStreak = 0; @@ -19,7 +32,7 @@ export function getStreaks(prices: number[], side: 'up' | 'down') { // Append the final streak if it exists if (currentStreak > 0) { - streaks.push(currentStreak); + saveStreak(prices.length); } return streaks; From 485ea9165f188cec1390022d455b2828a85f2de7 Mon Sep 17 00:00:00 2001 From: Benny Neugebauer Date: Tue, 3 Dec 2024 11:50:15 +0100 Subject: [PATCH 4/7] how to organize test files --- src/util/getStreaks.test.ts | 37 ++++++++++++++++++++++++------------- src/util/getStreaks.ts | 14 +++++++++++--- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/util/getStreaks.test.ts b/src/util/getStreaks.test.ts index c42fbb260..c2f8d4057 100644 --- a/src/util/getStreaks.test.ts +++ b/src/util/getStreaks.test.ts @@ -3,23 +3,34 @@ import {getStreaks} from './getStreaks.js'; describe('getStreaks', () => { const input = [10, 20, 30, 40, 32, 42, 50, 45, 44, 41, 59, 90, 100]; - it('keeps track of upward streak lengths', () => { - const actual = getStreaks(input, 'up'); - expect(actual.map(s => s.length)).toStrictEqual([3, 2, 3]); - }); + 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]); + 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]); + }); }); - it('keeps track of downward streak lengths', () => { - const actual = getStreaks(input, 'down'); - expect(actual.map(s => s.length)).toStrictEqual([1, 3]); + 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]); + }); }); - 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([3, 2, 3]); + }); }); }); diff --git a/src/util/getStreaks.ts b/src/util/getStreaks.ts index ae41be262..dca7d0810 100644 --- a/src/util/getStreaks.ts +++ b/src/util/getStreaks.ts @@ -1,10 +1,18 @@ export type Streak = { + /** Length of the streak */ length: number; /** Price change percentage during the streak */ percentage: number; }; -export function getStreaks(prices: number[], side: 'up' | 'down'): Streak[] { +/** + * 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; @@ -16,8 +24,8 @@ export function getStreaks(prices: number[], side: 'up' | 'down'): Streak[] { } for (let i = 1; i < prices.length; i++) { - const isUpward = side === 'up' && prices[i] > prices[i - 1]; - const isDownward = side === 'down' && prices[i] < prices[i - 1]; + const isUpward = keepSide === 'up' && prices[i] > prices[i - 1]; + const isDownward = keepSide === 'down' && prices[i] < prices[i - 1]; if (isUpward || isDownward) { currentStreak++; } else { From d09b488aab1bdeaf923c89029b339b36e1e4b45f Mon Sep 17 00:00:00 2001 From: Benny Neugebauer Date: Tue, 3 Dec 2024 11:52:52 +0100 Subject: [PATCH 5/7] put back coverage limit --- src/util/getStreaks.test.ts | 2 +- vitest.config.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util/getStreaks.test.ts b/src/util/getStreaks.test.ts index c2f8d4057..e2ab677c6 100644 --- a/src/util/getStreaks.test.ts +++ b/src/util/getStreaks.test.ts @@ -30,7 +30,7 @@ describe('getStreaks', () => { describe('special cases', () => { it("doesn't record a streak of 1", () => { const actual = getStreaks([1], 'up'); - expect(actual.map(s => s.length)).toStrictEqual([3, 2, 3]); + expect(actual.map(s => s.length)).toStrictEqual([]); }); }); }); diff --git a/vitest.config.ts b/vitest.config.ts index c0a3e2978..b5761c145 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,13 +3,13 @@ import {defineConfig} from 'vitest/config'; export default defineConfig({ test: { coverage: { - branches: 0, - functions: 0, + branches: 100, + functions: 100, include: ['**/*.{ts,tsx}', '!**/*.d.ts', '!**/cli.ts', '!**/index.ts', '!**/start*.ts'], - lines: 0, + lines: 100, provider: 'v8', reporter: ['html', 'lcov', 'text'], - statements: 0, + statements: 100, }, environment: 'node', globals: true, From 1852bc50b32b2dbfda2e09e0ed3ff613c02f75e8 Mon Sep 17 00:00:00 2001 From: Benny Neugebauer Date: Fri, 6 Dec 2024 10:22:37 +0100 Subject: [PATCH 6/7] fix benchmark run --- README.md | 6 +++--- package.json | 2 +- src/index.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4f72f4eaf..ef5aa999f 100644 --- a/README.md +++ b/README.md @@ -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(...)`? diff --git a/package.json b/package.json index a648a4cef..2420faad3 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/index.ts b/src/index.ts index e9417a15d..9b78ef3a1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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'; From c60011c3e087bc8b0438636dde438e02eb0ca66d Mon Sep 17 00:00:00 2001 From: Benny Neugebauer Date: Fri, 6 Dec 2024 12:51:50 +0100 Subject: [PATCH 7/7] fix thresholds --- vitest.config.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/vitest.config.ts b/vitest.config.ts index b5761c145..e7bac83fb 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,13 +3,15 @@ import {defineConfig} from 'vitest/config'; export default defineConfig({ test: { coverage: { - branches: 100, - functions: 100, include: ['**/*.{ts,tsx}', '!**/*.d.ts', '!**/cli.ts', '!**/index.ts', '!**/start*.ts'], - lines: 100, provider: 'v8', reporter: ['html', 'lcov', 'text'], - statements: 100, + thresholds: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, }, environment: 'node', globals: true,