@extasyio/indicators
Version:
Streaming technical indicators for extasy-trading-core-libs
277 lines (268 loc) • 7.23 kB
JavaScript
"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
});