@aurox/ohlcv-helpers
Advanced tools
Comparing version 0.3.1 to 0.4.0
@@ -36,3 +36,3 @@ "use strict"; | ||
}; | ||
let pointsInRange = []; | ||
const pointsInRange = []; | ||
avl.range(currentRangeStart, currentRangeEnd, node => { | ||
@@ -39,0 +39,0 @@ if (node.data) { |
@@ -10,9 +10,16 @@ "use strict"; | ||
switch (timeUnit.granularity) { | ||
case 'second': return timeUnit.period === 1 ? 'S' : `${timeUnit.period}S`; | ||
case 'minute': return `${timeUnit.period}`; | ||
case 'hour': return `${timeUnit.period * 60}`; | ||
case 'day': return timeUnit.period === 1 ? 'D' : `${timeUnit.period}D`; | ||
case 'week': return timeUnit.period === 1 ? 'W' : `${timeUnit.period}W`; | ||
case 'month': return timeUnit.period === 1 ? 'M' : `${timeUnit.period}M`; | ||
case 'year': return `${timeUnit.period * 12}M`; | ||
case 'second': | ||
return timeUnit.period === 1 ? 'S' : `${timeUnit.period}S`; | ||
case 'minute': | ||
return `${timeUnit.period}`; | ||
case 'hour': | ||
return `${timeUnit.period * 60}`; | ||
case 'day': | ||
return timeUnit.period === 1 ? 'D' : `${timeUnit.period}D`; | ||
case 'week': | ||
return timeUnit.period === 1 ? 'W' : `${timeUnit.period}W`; | ||
case 'month': | ||
return timeUnit.period === 1 ? 'M' : `${timeUnit.period}M`; | ||
case 'year': | ||
return `${timeUnit.period * 12}M`; | ||
} | ||
@@ -22,9 +29,16 @@ throw new Error('Invalid granularity'); | ||
switch (timeUnit.granularity) { | ||
case 'second': return `${timeUnit.period}SEC`; | ||
case 'minute': return `${timeUnit.period}MIN`; | ||
case 'hour': return `${timeUnit.period}HRS`; | ||
case 'day': return `${timeUnit.period}DAY`; | ||
case 'week': return `${timeUnit.period * 7}DAY`; | ||
case 'month': return `${timeUnit.period}MTH`; | ||
case 'year': return `${timeUnit.period}YRS`; | ||
case 'second': | ||
return `${timeUnit.period}SEC`; | ||
case 'minute': | ||
return `${timeUnit.period}MIN`; | ||
case 'hour': | ||
return `${timeUnit.period}HRS`; | ||
case 'day': | ||
return `${timeUnit.period}DAY`; | ||
case 'week': | ||
return `${timeUnit.period * 7}DAY`; | ||
case 'month': | ||
return `${timeUnit.period}MTH`; | ||
case 'year': | ||
return `${timeUnit.period}YRS`; | ||
} | ||
@@ -59,5 +73,8 @@ throw new Error('Invalid granularity'); | ||
switch ((granularity || '').toUpperCase()) { | ||
case 'S': return { period, granularity: 'second' }; | ||
case 'D': return { period, granularity: 'day' }; | ||
case 'W': return { period, granularity: 'week' }; | ||
case 'S': | ||
return { period, granularity: 'second' }; | ||
case 'D': | ||
return { period, granularity: 'day' }; | ||
case 'W': | ||
return { period, granularity: 'week' }; | ||
case 'M': | ||
@@ -83,5 +100,8 @@ if (period >= 12 && period % 12 === 0) { | ||
switch ((granularity || '').toUpperCase()) { | ||
case 'SEC': return { period, granularity: 'second' }; | ||
case 'MIN': return { period, granularity: 'minute' }; | ||
case 'HRS': return { period, granularity: 'hour' }; | ||
case 'SEC': | ||
return { period, granularity: 'second' }; | ||
case 'MIN': | ||
return { period, granularity: 'minute' }; | ||
case 'HRS': | ||
return { period, granularity: 'hour' }; | ||
case 'DAY': | ||
@@ -88,0 +108,0 @@ if (period >= 7 && period % 7 === 0) { |
import { OHLCVTimeUnitGranularity, OHLCVTimeUnit } from './types'; | ||
export declare function getGranularityDuration(granularity: OHLCVTimeUnitGranularity): number; | ||
export declare function getTimeUnitDuration(timeUnit: OHLCVTimeUnit): number; | ||
export declare function startOfGranularity(timestamp: number, granularity: OHLCVTimeUnitGranularity): number; | ||
export declare function startOfTimeUnit(timestamp: number, timeUnit: OHLCVTimeUnit): number; | ||
export declare function startOfClosestTimeUnit(timestamp: number, timeUnit: OHLCVTimeUnit): number; | ||
export declare function getTimeUnitStartsInRange(start: number, end: number, timeUnit: OHLCVTimeUnit, inclusion?: 'inclusive' | 'exclusive'): number[]; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getTimeUnitStartsInRange = exports.startOfTimeUnit = exports.getTimeUnitDuration = exports.getGranularityDuration = void 0; | ||
exports.getTimeUnitStartsInRange = exports.startOfClosestTimeUnit = exports.startOfTimeUnit = exports.startOfGranularity = exports.getTimeUnitDuration = exports.getGranularityDuration = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -26,29 +26,46 @@ const moment_1 = tslib_1.__importDefault(require("moment")); | ||
function getTimeUnitDuration(timeUnit) { | ||
return getGranularityDuration(timeUnit.granularity) * timeUnit.period; | ||
return Math.round(getGranularityDuration(timeUnit.granularity) * timeUnit.period); | ||
} | ||
exports.getTimeUnitDuration = getTimeUnitDuration; | ||
function startOfTimeUnit(timestamp, timeUnit) { | ||
const timeUnitDuration = getTimeUnitDuration(timeUnit); | ||
const intervalStartTimestamp = timeUnit.period === 1 ? timestamp : Math.floor(timestamp / timeUnitDuration) * timeUnitDuration; | ||
switch (timeUnit.granularity) { | ||
function startOfGranularity(timestamp, granularity) { | ||
switch (granularity) { | ||
case 'second': | ||
return moment_1.default(intervalStartTimestamp).utc().startOf('second').valueOf(); | ||
return moment_1.default(timestamp).utc().startOf('second').valueOf(); | ||
case 'minute': | ||
return moment_1.default(intervalStartTimestamp).utc().startOf('minute').valueOf(); | ||
return moment_1.default(timestamp).utc().startOf('minute').valueOf(); | ||
case 'hour': | ||
return moment_1.default(intervalStartTimestamp).utc().startOf('hour').valueOf(); | ||
return moment_1.default(timestamp).utc().startOf('hour').valueOf(); | ||
case 'day': | ||
return moment_1.default(intervalStartTimestamp).utc().startOf('day').valueOf(); | ||
return moment_1.default(timestamp).utc().startOf('day').valueOf(); | ||
case 'week': | ||
return moment_1.default(intervalStartTimestamp).utc().startOf('isoWeek').valueOf(); | ||
return moment_1.default(timestamp).utc().startOf('isoWeek').valueOf(); | ||
case 'month': | ||
return moment_1.default(intervalStartTimestamp).utc().startOf('month').valueOf(); | ||
return moment_1.default(timestamp).utc().startOf('month').valueOf(); | ||
case 'year': | ||
return moment_1.default(intervalStartTimestamp).utc().startOf('year').valueOf(); | ||
return moment_1.default(timestamp).utc().startOf('year').valueOf(); | ||
} | ||
} | ||
exports.startOfGranularity = startOfGranularity; | ||
function startOfTimeUnit(timestamp, timeUnit) { | ||
const timeUnitDuration = getTimeUnitDuration(timeUnit); | ||
const intervalStartTimestamp = timeUnit.period === 1 ? timestamp : Math.floor(timestamp / timeUnitDuration) * timeUnitDuration; | ||
return startOfGranularity(intervalStartTimestamp, timeUnit.granularity); | ||
} | ||
exports.startOfTimeUnit = startOfTimeUnit; | ||
function startOfClosestTimeUnit(timestamp, timeUnit) { | ||
const timeUnitDuration = getTimeUnitDuration(timeUnit); | ||
const timestamp1 = timestamp; | ||
const timestamp2 = Math.round(timestamp + Math.round(timeUnitDuration / 2)); | ||
const intervalStartTimestamp1 = timeUnit.period === 1 ? timestamp1 : Math.floor(timestamp1 / timeUnitDuration) * timeUnitDuration; | ||
const intervalStartTimestamp2 = timeUnit.period === 1 ? timestamp2 : Math.floor(timestamp2 / timeUnitDuration) * timeUnitDuration; | ||
const answer1 = startOfGranularity(intervalStartTimestamp1, timeUnit.granularity); | ||
const answer2 = startOfGranularity(intervalStartTimestamp2, timeUnit.granularity); | ||
const distance1 = Math.abs(answer1 - timestamp); | ||
const distance2 = Math.abs(answer2 - timestamp); | ||
return distance1 <= distance2 ? answer1 : answer2; | ||
} | ||
exports.startOfClosestTimeUnit = startOfClosestTimeUnit; | ||
function getTimeUnitStartsInRange(start, end, timeUnit, inclusion = 'inclusive') { | ||
const initialTimeUnitStart = startOfTimeUnit(start, timeUnit); | ||
const finalTimeUnitStart = startOfTimeUnit(end, timeUnit); | ||
const initialTimeUnitStart = startOfClosestTimeUnit(start, timeUnit) === start ? start : startOfTimeUnit(start, timeUnit); | ||
const finalTimeUnitStart = startOfClosestTimeUnit(end, timeUnit) === end ? end : startOfTimeUnit(end, timeUnit); | ||
const timeUnitDuration = getTimeUnitDuration(timeUnit); | ||
@@ -59,9 +76,13 @@ const result = []; | ||
} | ||
let currentTimeUnitStart = initialTimeUnitStart + timeUnitDuration; | ||
let currentTimeUnitStart = startOfClosestTimeUnit(Math.round(initialTimeUnitStart + timeUnitDuration), timeUnit); | ||
while (currentTimeUnitStart < finalTimeUnitStart) { | ||
const correctedTimeUnit = startOfTimeUnit(Math.round(currentTimeUnitStart), timeUnit); | ||
if (result.length === 0 || correctedTimeUnit !== result[result.length - 1]) { | ||
result.push(correctedTimeUnit); | ||
if (result.length === 0 || currentTimeUnitStart !== result[result.length - 1]) { | ||
result.push(currentTimeUnitStart); | ||
} | ||
currentTimeUnitStart += timeUnitDuration; | ||
const nextTimeUnit = startOfClosestTimeUnit(Math.round(currentTimeUnitStart + timeUnitDuration), timeUnit); | ||
// This prevents cases where there are no intervals between start and end and the closets time unit stays the same | ||
if (nextTimeUnit === currentTimeUnitStart) { | ||
break; | ||
} | ||
currentTimeUnitStart = nextTimeUnit; | ||
} | ||
@@ -68,0 +89,0 @@ if (inclusion === 'inclusive') { |
@@ -9,2 +9,6 @@ "use strict"; | ||
describe('getTimeUnitStartsInRange', () => { | ||
it('Should return empty array when exclusive with no units in between', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 0, d: 2 }).valueOf(), { granularity: 'day', period: 1 }, 'exclusive'); | ||
chai_1.expect(range).to.deep.equal([]); | ||
}); | ||
it('Should the correct time units in a given inclusive range', () => { | ||
@@ -16,46 +20,127 @@ const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 0, d: 2 }).valueOf(), { | ||
const expectedOutput = [ | ||
new Date('2020-01-01T00:00:00.000Z').getTime(), | ||
new Date('2020-01-01T04:00:00.000Z').getTime(), | ||
new Date('2020-01-01T08:00:00.000Z').getTime(), | ||
new Date('2020-01-01T12:00:00.000Z').getTime(), | ||
new Date('2020-01-01T16:00:00.000Z').getTime(), | ||
new Date('2020-01-01T20:00:00.000Z').getTime(), | ||
new Date('2020-01-02T00:00:00.000Z').getTime(), | ||
'2020-01-01T00:00:00.000Z', | ||
'2020-01-01T04:00:00.000Z', | ||
'2020-01-01T08:00:00.000Z', | ||
'2020-01-01T12:00:00.000Z', | ||
'2020-01-01T16:00:00.000Z', | ||
'2020-01-01T20:00:00.000Z', | ||
'2020-01-02T00:00:00.000Z', | ||
]; | ||
chai_1.expect(range).to.deep.equal(expectedOutput); | ||
chai_1.expect(range.map(n => new Date(n).toISOString())).to.deep.equal(expectedOutput); | ||
}); | ||
it('Should the correct time units in a given exclusive range', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 0, d: 2 }).valueOf(), { | ||
granularity: 'hour', | ||
period: 4, | ||
}, 'exclusive'); | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 0, d: 2 }).valueOf(), { granularity: 'hour', period: 4 }, 'exclusive'); | ||
const expectedOutput = [ | ||
new Date('2020-01-01T04:00:00.000Z').getTime(), | ||
new Date('2020-01-01T08:00:00.000Z').getTime(), | ||
new Date('2020-01-01T12:00:00.000Z').getTime(), | ||
new Date('2020-01-01T16:00:00.000Z').getTime(), | ||
new Date('2020-01-01T20:00:00.000Z').getTime(), | ||
'2020-01-01T04:00:00.000Z', | ||
'2020-01-01T08:00:00.000Z', | ||
'2020-01-01T12:00:00.000Z', | ||
'2020-01-01T16:00:00.000Z', | ||
'2020-01-01T20:00:00.000Z', | ||
]; | ||
chai_1.expect(range).to.deep.equal(expectedOutput); | ||
chai_1.expect(range.map(n => new Date(n).toISOString())).to.deep.equal(expectedOutput); | ||
}); | ||
it('Should work as expected with daily intervals', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 0, d: 5 }).valueOf(), { | ||
granularity: 'day', | ||
period: 1, | ||
}, 'exclusive'); | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 0, d: 5 }).valueOf(), { granularity: 'day', period: 1 }, 'exclusive'); | ||
const expectedOutput = ['2020-01-02T00:00:00.000Z', '2020-01-03T00:00:00.000Z', '2020-01-04T00:00:00.000Z']; | ||
chai_1.expect(range.map(n => new Date(n).toISOString())).to.deep.equal(expectedOutput); | ||
}); | ||
it('Should work as expected with daily intervals with period of 8', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 2, d: 17 }).valueOf(), { granularity: 'day', period: 8 }, 'exclusive'); | ||
const expectedOutput = [ | ||
new Date('2020-01-02T00:00:00.000Z').getTime(), | ||
new Date('2020-01-03T00:00:00.000Z').getTime(), | ||
new Date('2020-01-04T00:00:00.000Z').getTime(), | ||
'2020-01-03T00:00:00.000Z', | ||
'2020-01-11T00:00:00.000Z', | ||
'2020-01-19T00:00:00.000Z', | ||
'2020-01-27T00:00:00.000Z', | ||
'2020-02-04T00:00:00.000Z', | ||
'2020-02-12T00:00:00.000Z', | ||
'2020-02-20T00:00:00.000Z', | ||
'2020-02-28T00:00:00.000Z', | ||
'2020-03-07T00:00:00.000Z', | ||
]; | ||
chai_1.expect(range).to.deep.equal(expectedOutput); | ||
chai_1.expect(range.map(n => new Date(n).toISOString())).to.deep.equal(expectedOutput); | ||
}); | ||
it('Should return empty array when exclusive with no units in between', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 0, d: 2 }).valueOf(), { | ||
granularity: 'day', | ||
period: 1, | ||
}, 'exclusive'); | ||
chai_1.expect(range).to.deep.equal([]); | ||
it('Should work as expected with weekly intervals', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 2, d: 17 }).valueOf(), { granularity: 'week', period: 1 }, 'inclusive'); | ||
const expectedOutput = [ | ||
'2019-12-30T00:00:00.000Z', | ||
'2020-01-06T00:00:00.000Z', | ||
'2020-01-13T00:00:00.000Z', | ||
'2020-01-20T00:00:00.000Z', | ||
'2020-01-27T00:00:00.000Z', | ||
'2020-02-03T00:00:00.000Z', | ||
'2020-02-10T00:00:00.000Z', | ||
'2020-02-17T00:00:00.000Z', | ||
'2020-02-24T00:00:00.000Z', | ||
'2020-03-02T00:00:00.000Z', | ||
'2020-03-09T00:00:00.000Z', | ||
'2020-03-16T00:00:00.000Z', | ||
]; | ||
chai_1.expect(range.map(n => new Date(n).toISOString())).to.deep.equal(expectedOutput); | ||
}); | ||
it('Should work as expected with weekly intervals with period of 3', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2020, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 2, d: 17 }).valueOf(), { granularity: 'week', period: 3 }, 'inclusive'); | ||
const expectedOutput = [ | ||
'2019-12-16T00:00:00.000Z', | ||
'2020-01-06T00:00:00.000Z', | ||
'2020-01-27T00:00:00.000Z', | ||
'2020-02-17T00:00:00.000Z', | ||
'2020-03-09T00:00:00.000Z', | ||
]; | ||
chai_1.expect(range.map(n => new Date(n).toISOString())).to.deep.equal(expectedOutput); | ||
}); | ||
it('Should work as expected with monthly intervals', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2018, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 6, d: 1 }).valueOf(), { granularity: 'month', period: 1 }, 'inclusive'); | ||
const expectedOutput = [ | ||
'2018-01-01T00:00:00.000Z', | ||
'2018-02-01T00:00:00.000Z', | ||
'2018-03-01T00:00:00.000Z', | ||
'2018-04-01T00:00:00.000Z', | ||
'2018-05-01T00:00:00.000Z', | ||
'2018-06-01T00:00:00.000Z', | ||
'2018-07-01T00:00:00.000Z', | ||
'2018-08-01T00:00:00.000Z', | ||
'2018-09-01T00:00:00.000Z', | ||
'2018-10-01T00:00:00.000Z', | ||
'2018-11-01T00:00:00.000Z', | ||
'2018-12-01T00:00:00.000Z', | ||
'2019-01-01T00:00:00.000Z', | ||
'2019-02-01T00:00:00.000Z', | ||
'2019-03-01T00:00:00.000Z', | ||
'2019-04-01T00:00:00.000Z', | ||
'2019-05-01T00:00:00.000Z', | ||
'2019-06-01T00:00:00.000Z', | ||
'2019-07-01T00:00:00.000Z', | ||
'2019-08-01T00:00:00.000Z', | ||
'2019-09-01T00:00:00.000Z', | ||
'2019-10-01T00:00:00.000Z', | ||
'2019-11-01T00:00:00.000Z', | ||
'2019-12-01T00:00:00.000Z', | ||
'2020-01-01T00:00:00.000Z', | ||
'2020-02-01T00:00:00.000Z', | ||
'2020-03-01T00:00:00.000Z', | ||
'2020-04-01T00:00:00.000Z', | ||
'2020-05-01T00:00:00.000Z', | ||
'2020-06-01T00:00:00.000Z', | ||
'2020-07-01T00:00:00.000Z', | ||
]; | ||
chai_1.expect(range.map(n => new Date(n).toISOString())).to.deep.equal(expectedOutput); | ||
}); | ||
it('Should work as expected with quarterly intervals', () => { | ||
const range = utils_1.getTimeUnitStartsInRange(moment_1.default.utc({ y: 2018, M: 0, d: 1 }).valueOf(), moment_1.default.utc({ y: 2020, M: 6, d: 1 }).valueOf(), { granularity: 'month', period: 3 }, 'inclusive'); | ||
const expectedOutput = [ | ||
'2018-01-01T00:00:00.000Z', | ||
'2018-04-01T00:00:00.000Z', | ||
'2018-07-01T00:00:00.000Z', | ||
'2018-10-01T00:00:00.000Z', | ||
'2019-01-01T00:00:00.000Z', | ||
'2019-04-01T00:00:00.000Z', | ||
'2019-07-01T00:00:00.000Z', | ||
'2019-10-01T00:00:00.000Z', | ||
'2020-01-01T00:00:00.000Z', | ||
'2020-04-01T00:00:00.000Z', | ||
'2020-07-01T00:00:00.000Z', | ||
]; | ||
chai_1.expect(range.map(n => new Date(n).toISOString())).to.deep.equal(expectedOutput); | ||
}); | ||
}); | ||
}); |
{ | ||
"name": "@aurox/ohlcv-helpers", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"description": "", | ||
@@ -20,16 +20,22 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@types/node": "^14.0.14", | ||
"@types/node": "^14.0.27", | ||
"avl": "^1.4.4", | ||
"moment": "^2.27.0", | ||
"tslib": "^2.0.0" | ||
"tslib": "^2.0.1" | ||
}, | ||
"devDependencies": { | ||
"@types/chai": "^4.2.11", | ||
"@types/mocha": "^7.0.2", | ||
"@types/chai": "^4.2.12", | ||
"@types/mocha": "^8.0.2", | ||
"@typescript-eslint/eslint-plugin": "^3.9.0", | ||
"@typescript-eslint/parser": "^3.9.0", | ||
"chai": "^4.2.0", | ||
"mocha": "^8.0.1", | ||
"eslint": "^7.6.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-prettier": "^3.1.4", | ||
"mocha": "^8.1.1", | ||
"prettier": "^2.0.5", | ||
"rimraf": "^3.0.2", | ||
"ts-node": "^8.10.2", | ||
"typescript": "^3.9.5" | ||
"typescript": "^3.9.7" | ||
} | ||
} |
36464
762
13
Updated@types/node@^14.0.27
Updatedtslib@^2.0.1