UNPKG

@extasyio/indicators

Version:

Streaming technical indicators for extasy-trading-core-libs

277 lines (268 loc) 7.23 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { ATR: () => ATR, Bollinger: () => Bollinger, EMA: () => EMA, RSI: () => RSI, RingBuffer: () => RingBuffer, SMA: () => SMA, WMA: () => WMA }); module.exports = __toCommonJS(index_exports); // src/util/ring.ts var RingBuffer = class { constructor(size) { this.size = size; if (size <= 0) throw new RangeError("Size must be positive"); this.data = new Array(size).fill(0); } data; index = 0; filled = false; /** * Push a value into the buffer. * @returns overwritten element once the buffer is full, otherwise `null` */ push(value) { const overwritten = this.filled ? this.data[this.index] : null; this.data[this.index] = value; this.index = (this.index + 1) % this.size; if (this.index === 0) this.filled = true; return overwritten; } /** Iterate over currently stored elements. */ forEach(cb) { const len = this.filled ? this.size : this.index; for (let i = 0; i < len; i++) cb(this.data[i]); } /** Current number of elements in the buffer. */ length() { return this.filled ? this.size : this.index; } /** Clear buffer contents. */ reset() { this.index = 0; this.filled = false; } }; // src/sma.ts var SMA = class { constructor(period) { this.period = period; if (period <= 0) throw new RangeError("Period must be positive"); this.buf = new RingBuffer(period); } buf; sum = 0; update(value) { const dropped = this.buf.push(value); if (dropped !== null) this.sum -= dropped; this.sum += value; if (this.buf.length() < this.period) return null; return this.sum / this.period; } reset() { this.buf.reset(); this.sum = 0; } }; // src/ema.ts var EMA = class { constructor(period) { this.period = period; if (period <= 0) throw new RangeError("Period must be positive"); this.alpha = 2 / (period + 1); } alpha; ema = null; sum = 0; counter = 0; update(value) { this.counter += 1; if (this.counter <= this.period) { this.sum += value; if (this.counter < this.period) return null; this.ema = this.sum / this.period; return this.ema; } this.ema = this.alpha * value + (1 - this.alpha) * this.ema; return this.ema; } reset() { this.ema = null; this.sum = 0; this.counter = 0; } }; // src/wma.ts var WMA = class { constructor(period) { this.period = period; if (period <= 0) throw new RangeError("Period must be positive"); this.buf = new RingBuffer(period); this.weightSum = period * (period + 1) / 2; } buf; weightSum; update(value) { this.buf.push(value); if (this.buf.length() < this.period) return null; const values = []; this.buf.forEach((v) => values.push(v)); let acc = 0; for (let i = this.period - 1, w = this.period; i >= 0; i--, w--) { acc += values[i] * w; } return acc / this.weightSum; } reset() { this.buf.reset(); } }; // src/rsi.ts var RSI = class { constructor(period = 14) { this.period = period; if (period <= 0) throw new RangeError("Period must be positive"); } avgGain = 0; avgLoss = 0; prev = null; counter = 0; update(value) { if (this.prev === null) { this.prev = value; return null; } const diff = value - this.prev; const gain = diff > 0 ? diff : 0; const loss = diff < 0 ? -diff : 0; this.prev = value; this.counter += 1; if (this.counter <= this.period) { this.avgGain += gain; this.avgLoss += loss; if (this.counter < this.period) return null; this.avgGain /= this.period; this.avgLoss /= this.period; } else { this.avgGain = (this.avgGain * (this.period - 1) + gain) / this.period; this.avgLoss = (this.avgLoss * (this.period - 1) + loss) / this.period; } if (this.avgGain === 0 && this.avgLoss === 0) return 50; if (this.avgLoss === 0) return 100; if (this.avgGain === 0) return 0; const rs = this.avgGain / this.avgLoss; return 100 - 100 / (1 + rs); } reset() { this.avgGain = this.avgLoss = 0; this.prev = null; this.counter = 0; } }; // src/bollinger.ts var Bollinger = class { constructor(period = 20, k = 2) { this.period = period; this.k = k; if (period <= 0) throw new RangeError("Period must be positive"); if (k <= 0) throw new RangeError("k must be positive"); this.buf = new RingBuffer(period); } buf; mean = 0; m2 = 0; // sum of squares of diff from mean counter = 0; update(value) { const dropped = this.buf.push(value); this.counter += 1; const delta = value - this.mean; this.mean += delta / this.counter; const delta2 = value - this.mean; this.m2 += delta * delta2; if (dropped !== null) { const oldMean = (this.mean * this.counter - value + dropped) / this.counter; const oldDelta = dropped - oldMean; const oldDelta2 = dropped - this.mean; this.m2 -= oldDelta * oldDelta2; } if (this.counter < this.period) return null; const variance = this.m2 / this.period; const sigma = Math.sqrt(variance); return { middle: this.mean, upper: this.mean + this.k * sigma, lower: this.mean - this.k * sigma }; } reset() { this.buf.reset(); this.mean = this.m2 = 0; this.counter = 0; } }; // src/atr.ts var ATR = class { constructor(period = 14) { this.period = period; if (period <= 0) throw new RangeError("Period must be positive"); } prevClose = null; atr = null; sumTR = 0; counter = 0; update(c) { const trRaw = this.prevClose === null ? c.high - c.low : Math.max( c.high - c.low, Math.abs(c.high - this.prevClose), Math.abs(c.low - this.prevClose) ); this.prevClose = c.close; this.counter += 1; if (this.counter <= this.period) { this.sumTR += trRaw; if (this.counter < this.period) return null; this.atr = this.sumTR / this.period; return this.atr; } this.atr = (this.atr * (this.period - 1) + trRaw) / this.period; return this.atr; } reset() { this.prevClose = null; this.atr = null; this.sumTR = 0; this.counter = 0; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ATR, Bollinger, EMA, RSI, RingBuffer, SMA, WMA });