@aurox/ohlcv-helpers
Advanced tools
Comparing version 0.5.2 to 0.6.0
@@ -0,1 +1,3 @@ | ||
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import { OHLCVTimeUnit, OHLCVDataItem } from './types'; | ||
@@ -5,3 +7,2 @@ export interface OHLCVAggregatorRecordLimitRetentionOptions { | ||
retentionCount: number; | ||
removeOnOverflow: number; | ||
} | ||
@@ -20,12 +21,30 @@ export interface OHLCVAggregatorTimeWindowRetentionOptions { | ||
} | ||
export declare class OHLCVLiveAggregator { | ||
private emitter; | ||
private timeUnit; | ||
private retention; | ||
private enableHistory; | ||
private autofillGaps; | ||
private historyLoaded; | ||
private preHistoryUpdateBuffer; | ||
private initialLastKnowClosingPrice; | ||
private data; | ||
export interface OHLCVLiveAggregatorEvents { | ||
'update': (lastBar: OHLCVDataItem) => void; | ||
'history-set': () => void; | ||
'history-reset': () => void; | ||
} | ||
export declare interface OHLCVLiveAggregator { | ||
on<U extends keyof OHLCVLiveAggregatorEvents>(event: U, listener: OHLCVLiveAggregatorEvents[U]): this; | ||
off<U extends keyof OHLCVLiveAggregatorEvents>(event: U, listener: OHLCVLiveAggregatorEvents[U]): this; | ||
once<U extends keyof OHLCVLiveAggregatorEvents>(event: U, listener: OHLCVLiveAggregatorEvents[U]): this; | ||
addListener<U extends keyof OHLCVLiveAggregatorEvents>(event: U, listener: OHLCVLiveAggregatorEvents[U]): this; | ||
removeListener<U extends keyof OHLCVLiveAggregatorEvents>(event: U, listener: OHLCVLiveAggregatorEvents[U]): this; | ||
prependListener<U extends keyof OHLCVLiveAggregatorEvents>(event: U, listener: OHLCVLiveAggregatorEvents[U]): this; | ||
prependOnceListener<U extends keyof OHLCVLiveAggregatorEvents>(event: U, listener: OHLCVLiveAggregatorEvents[U]): this; | ||
emit<U extends keyof OHLCVLiveAggregatorEvents>(event: U, ...args: Parameters<OHLCVLiveAggregatorEvents[U]>): boolean; | ||
} | ||
export declare class OHLCVLiveAggregator extends EventEmitter { | ||
private _timeUnit; | ||
private _retention; | ||
private _historyEnabled; | ||
private _autoGapFillEnabled; | ||
private _historyLoaded; | ||
private _preHistoryUpdateBuffer; | ||
private _initialLastKnowClosingPrice; | ||
private _data; | ||
get timeUnit(): OHLCVTimeUnit; | ||
get historyEnabled(): boolean; | ||
get autoGapFillEnabled(): boolean; | ||
get historyLoaded(): boolean; | ||
constructor(options: OHLCVAggregatorOptions); | ||
@@ -42,3 +61,2 @@ private retain; | ||
getLastKnownClosingPrice: () => number | null; | ||
registerUpdateListener: (listener: (lastBar: OHLCVDataItem) => void) => () => void; | ||
} |
@@ -8,46 +8,43 @@ "use strict"; | ||
const utils_1 = require("./utils"); | ||
class OHLCVLiveAggregator { | ||
class OHLCVLiveAggregator extends events_1.EventEmitter { | ||
constructor(options) { | ||
var _a; | ||
this.emitter = new events_1.EventEmitter(); | ||
this.historyLoaded = false; | ||
this.preHistoryUpdateBuffer = null; | ||
this.data = new avl_1.default(undefined, true); | ||
super(); | ||
this._historyLoaded = false; | ||
this._preHistoryUpdateBuffer = null; | ||
this._data = new avl_1.default(undefined, true); | ||
this.resetHistory = () => { | ||
if (!this.enableHistory) { | ||
if (!this._historyEnabled) { | ||
throw new Error('History is disabled'); | ||
} | ||
this.historyLoaded = false; | ||
this._historyLoaded = false; | ||
this.emit('history-reset'); | ||
}; | ||
this.setHistory = (history) => { | ||
if (!this.enableHistory) { | ||
if (!this._historyEnabled) { | ||
throw new Error('History is disabled'); | ||
} | ||
if (this.historyLoaded) { | ||
if (this._historyLoaded) { | ||
throw new Error('History cannot be loaded more than once, to reset the history first call resetHistory then setHistory'); | ||
} | ||
this.historyLoaded = true; | ||
this.data.clear(); | ||
this._historyLoaded = true; | ||
this._data.clear(); | ||
for (const historyItem of history) { | ||
this.applyItem(historyItem); | ||
} | ||
const last = this.data.size > 0 ? this.data.maxNode().data : null; | ||
let lastBar = last; | ||
if (this.preHistoryUpdateBuffer && this.preHistoryUpdateBuffer.length > 0) { | ||
for (const bufferedItem of this.preHistoryUpdateBuffer) { | ||
lastBar = this.applyItem(bufferedItem); | ||
if (this._preHistoryUpdateBuffer && this._preHistoryUpdateBuffer.length > 0) { | ||
for (const bufferedItem of this._preHistoryUpdateBuffer) { | ||
this.applyItem(bufferedItem); | ||
} | ||
} | ||
this.preHistoryUpdateBuffer = null; | ||
this._preHistoryUpdateBuffer = null; | ||
this.retain(); | ||
if (lastBar) { | ||
this.emitter.emit('update', lastBar); | ||
} | ||
this.emit('history-set'); | ||
}; | ||
this.applyUpdate = (update) => { | ||
if (this.enableHistory && !this.historyLoaded) { | ||
if (!this.preHistoryUpdateBuffer) { | ||
this.preHistoryUpdateBuffer = []; | ||
if (this._historyEnabled && !this._historyLoaded) { | ||
if (!this._preHistoryUpdateBuffer) { | ||
this._preHistoryUpdateBuffer = []; | ||
} | ||
this.preHistoryUpdateBuffer.push(update); | ||
this._preHistoryUpdateBuffer.push(update); | ||
return null; | ||
@@ -57,18 +54,18 @@ } | ||
this.retain(); | ||
this.emitter.emit('update', inserted); | ||
this.emit('update', inserted); | ||
return inserted; | ||
}; | ||
this.getHasData = () => { | ||
return this.data.size !== 0; | ||
return this._data.size !== 0; | ||
}; | ||
this.getData = () => { | ||
if (this.autofillGaps) { | ||
if (this._autoGapFillEnabled) { | ||
const result = []; | ||
let lastTimeUnitStart = null; | ||
let lastPrice = null; | ||
this.data.forEach(node => { | ||
this._data.forEach(node => { | ||
const item = node.data; | ||
if (item) { | ||
if (lastTimeUnitStart !== null && lastPrice !== null) { | ||
const gaps = utils_1.getTimeUnitStartsInRange(lastTimeUnitStart, item.timeUnitStart, this.timeUnit, 'exclusive'); | ||
const gaps = utils_1.getTimeUnitStartsInRange(lastTimeUnitStart, item.timeUnitStart, this._timeUnit, 'exclusive'); | ||
for (const gap of gaps) { | ||
@@ -92,6 +89,6 @@ result.push({ | ||
}); | ||
const currentTimeUnitStart = utils_1.startOfTimeUnit(Date.now(), this.timeUnit); | ||
const currentTimeUnitStart = utils_1.startOfTimeUnit(Date.now(), this._timeUnit); | ||
const last = result[result.length - 1]; | ||
if (last && last.timeUnitStart !== currentTimeUnitStart) { | ||
const gaps = utils_1.getTimeUnitStartsInRange(last.timeUnitStart, currentTimeUnitStart, this.timeUnit); | ||
const gaps = utils_1.getTimeUnitStartsInRange(last.timeUnitStart, currentTimeUnitStart, this._timeUnit); | ||
for (const gap of gaps) { | ||
@@ -114,10 +111,10 @@ if (gap !== last.timeUnitStart) { | ||
} | ||
return this.data.values(); | ||
return this._data.values(); | ||
}; | ||
this.getMostRecentOHLCVItem = () => { | ||
return (this.data.size > 0 && this.data.maxNode().data) || null; | ||
return (this._data.size > 0 && this._data.maxNode().data) || null; | ||
}; | ||
this.getCurrentTimeUnitOHLCVItem = () => { | ||
const lastBar = this.data.size > 0 && this.data.maxNode().data; | ||
const currentTimeUnitStart = utils_1.startOfTimeUnit(Date.now(), this.timeUnit); | ||
const lastBar = this._data.size > 0 && this._data.maxNode().data; | ||
const currentTimeUnitStart = utils_1.startOfTimeUnit(Date.now(), this._timeUnit); | ||
if (lastBar) { | ||
@@ -140,3 +137,3 @@ if (lastBar.timeUnitStart === currentTimeUnitStart) { | ||
} | ||
else if (this.initialLastKnowClosingPrice !== null) { | ||
else if (this._initialLastKnowClosingPrice !== null) { | ||
return { | ||
@@ -146,6 +143,6 @@ timeUnitStart: currentTimeUnitStart, | ||
timeClose: currentTimeUnitStart, | ||
open: this.initialLastKnowClosingPrice, | ||
high: this.initialLastKnowClosingPrice, | ||
low: this.initialLastKnowClosingPrice, | ||
close: this.initialLastKnowClosingPrice, | ||
open: this._initialLastKnowClosingPrice, | ||
high: this._initialLastKnowClosingPrice, | ||
low: this._initialLastKnowClosingPrice, | ||
close: this._initialLastKnowClosingPrice, | ||
volume: 0, | ||
@@ -157,4 +154,4 @@ }; | ||
this.getLastKnownClosingPrice = () => { | ||
if (this.data.size > 0) { | ||
const lastEntry = this.data.maxNode().data; | ||
if (this._data.size > 0) { | ||
const lastEntry = this._data.maxNode().data; | ||
if (lastEntry) { | ||
@@ -164,35 +161,35 @@ return lastEntry.close; | ||
} | ||
return this.initialLastKnowClosingPrice; | ||
return this._initialLastKnowClosingPrice; | ||
}; | ||
this.registerUpdateListener = (listener) => { | ||
this.emitter.addListener('update', listener); | ||
return () => { | ||
this.emitter.removeListener('update', listener); | ||
}; | ||
}; | ||
this.timeUnit = options.timeUnit; | ||
this.retention = options.retention || null; | ||
this.enableHistory = options.enableHistory; | ||
this.autofillGaps = (_a = options.autofillGaps) !== null && _a !== void 0 ? _a : false; | ||
if (options.enableHistory) { | ||
this.preHistoryUpdateBuffer = []; | ||
} | ||
this.initialLastKnowClosingPrice = typeof options.lastKnowClosingPrice === 'number' ? options.lastKnowClosingPrice : null; | ||
this._timeUnit = options.timeUnit; | ||
this._retention = options.retention || null; | ||
this._historyEnabled = options.enableHistory; | ||
this._autoGapFillEnabled = (_a = options.autofillGaps) !== null && _a !== void 0 ? _a : false; | ||
this._initialLastKnowClosingPrice = typeof options.lastKnowClosingPrice === 'number' ? options.lastKnowClosingPrice : null; | ||
} | ||
get timeUnit() { | ||
return this._timeUnit; | ||
} | ||
get historyEnabled() { | ||
return this._historyEnabled; | ||
} | ||
get autoGapFillEnabled() { | ||
return this._autoGapFillEnabled; | ||
} | ||
get historyLoaded() { | ||
return this._historyLoaded; | ||
} | ||
retain() { | ||
if (!this.retention) { | ||
if (!this._retention) { | ||
return; | ||
} | ||
if (this.retention.method === 'record-limit' && this.data.size > this.retention.retentionCount) { | ||
const reducedSize = this.retention.retentionCount - this.retention.removeOnOverflow; | ||
if (reducedSize > 0) { | ||
while (this.data.size > reducedSize) { | ||
this.data.pop(); | ||
} | ||
if (this._retention.method === 'record-limit' && this._data.size > this._retention.retentionCount) { | ||
while (this._data.size > 0 && this._data.size > this._retention.retentionCount) { | ||
this._data.pop(); | ||
} | ||
} | ||
if (this.retention.method === 'time-window') { | ||
const minimumAllowedTimestamp = Date.now() - this.retention.retentionWindow; | ||
while (this.data.size > 0 && this.data.min() < minimumAllowedTimestamp) { | ||
this.data.pop(); | ||
if (this._retention.method === 'time-window') { | ||
const minimumAllowedTimestamp = Date.now() - this._retention.retentionWindow; | ||
while (this._data.size > 0 && this._data.min() < minimumAllowedTimestamp) { | ||
this._data.pop(); | ||
} | ||
@@ -202,4 +199,4 @@ } | ||
applyItem(update) { | ||
const targetTimeUnit = utils_1.startOfTimeUnit(update.timeUnitStart, this.timeUnit); | ||
const old = this.data.find(targetTimeUnit); | ||
const targetTimeUnit = utils_1.startOfTimeUnit(update.timeUnitStart, this._timeUnit); | ||
const old = this._data.find(targetTimeUnit); | ||
let inserted; | ||
@@ -210,3 +207,3 @@ if (old && old.data) { | ||
if (update.timeClose >= old.data.timeClose) { | ||
this.data.remove(targetTimeUnit); | ||
this._data.remove(targetTimeUnit); | ||
inserted = { | ||
@@ -222,3 +219,3 @@ timeUnitStart: targetTimeUnit, | ||
}; | ||
this.data.insert(targetTimeUnit, inserted); | ||
this._data.insert(targetTimeUnit, inserted); | ||
} | ||
@@ -240,3 +237,3 @@ else { | ||
}; | ||
this.data.insert(targetTimeUnit, inserted); | ||
this._data.insert(targetTimeUnit, inserted); | ||
} | ||
@@ -243,0 +240,0 @@ return inserted; |
{ | ||
"name": "@aurox/ohlcv-helpers", | ||
"version": "0.5.2", | ||
"version": "0.6.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
39211
825