trading-signals
Version:
Technical indicators to run technical analysis with JavaScript / TypeScript.
1,617 lines (1,579 loc) • 60.2 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
AC: () => AC,
ADX: () => ADX,
AO: () => AO,
ATR: () => ATR,
AccelerationBands: () => AccelerationBands,
BaseIndicatorSeries: () => BaseIndicatorSeries,
Big: () => import_big26.default,
BigIndicatorSeries: () => BigIndicatorSeries,
BollingerBands: () => BollingerBands,
BollingerBandsWidth: () => BollingerBandsWidth,
CCI: () => CCI,
CG: () => CG,
DEMA: () => DEMA,
DMA: () => DMA,
DX: () => DX,
EMA: () => EMA,
FasterAC: () => FasterAC,
FasterADX: () => FasterADX,
FasterAO: () => FasterAO,
FasterATR: () => FasterATR,
FasterAccelerationBands: () => FasterAccelerationBands,
FasterBollingerBands: () => FasterBollingerBands,
FasterBollingerBandsWidth: () => FasterBollingerBandsWidth,
FasterCCI: () => FasterCCI,
FasterCG: () => FasterCG,
FasterDEMA: () => FasterDEMA,
FasterDMA: () => FasterDMA,
FasterDX: () => FasterDX,
FasterEMA: () => FasterEMA,
FasterLinearRegression: () => FasterLinearRegression,
FasterMACD: () => FasterMACD,
FasterMAD: () => FasterMAD,
FasterMOM: () => FasterMOM,
FasterMovingAverage: () => FasterMovingAverage,
FasterOBV: () => FasterOBV,
FasterPeriod: () => FasterPeriod,
FasterRMA: () => FasterRMA,
FasterROC: () => FasterROC,
FasterRSI: () => FasterRSI,
FasterSMA: () => FasterSMA,
FasterStochasticOscillator: () => FasterStochasticOscillator,
FasterStochasticRSI: () => FasterStochasticRSI,
FasterTR: () => FasterTR,
FasterWMA: () => FasterWMA,
FasterWSMA: () => FasterWSMA,
LinearRegression: () => LinearRegression,
MACD: () => MACD,
MAD: () => MAD,
MOM: () => MOM,
MovingAverage: () => MovingAverage,
NotEnoughDataError: () => NotEnoughDataError,
NumberIndicatorSeries: () => NumberIndicatorSeries,
OBV: () => OBV,
Period: () => Period,
RMA: () => RMA,
ROC: () => ROC,
RSI: () => RSI,
SMA: () => SMA,
StochasticOscillator: () => StochasticOscillator,
StochasticRSI: () => StochasticRSI,
TR: () => TR,
TechnicalIndicator: () => TechnicalIndicator,
WMA: () => WMA,
WSMA: () => WSMA,
getAverage: () => getAverage,
getFasterAverage: () => getFasterAverage,
getFasterStandardDeviation: () => getFasterStandardDeviation,
getMaximum: () => getMaximum,
getMinimum: () => getMinimum,
getStandardDeviation: () => getStandardDeviation,
getStreaks: () => getStreaks,
pushUpdate: () => pushUpdate
});
module.exports = __toCommonJS(index_exports);
var import_big26 = __toESM(require("big.js"), 1);
// src/error/NotEnoughDataError.ts
var NotEnoughDataError = class extends Error {
constructor(message = "Not enough data") {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
this.name = "NotEnoughDataError";
}
};
// src/Indicator.ts
var TechnicalIndicator = class {
result;
getResult() {
try {
return this.getResultOrThrow();
} catch {
return null;
}
}
getResultOrThrow() {
if (this.result === void 0) {
throw new NotEnoughDataError();
}
return this.result;
}
get isStable() {
return this.result !== void 0;
}
add(input) {
return this.update(input, false);
}
replace(input) {
return this.update(input, true);
}
updates(inputs, replace = false) {
return inputs.map((input) => this.update(input, replace));
}
};
var BaseIndicatorSeries = class extends TechnicalIndicator {
previousHighest;
highest;
previousLowest;
lowest;
previousResult;
};
var BigIndicatorSeries = class extends BaseIndicatorSeries {
setResult(value, replace) {
if (replace) {
this.highest = this.previousHighest;
this.lowest = this.previousLowest;
this.result = this.previousResult;
}
if (this.highest === void 0) {
this.highest = value;
} else if (value.gt(this.highest)) {
this.previousHighest = this.highest;
this.highest = value;
} else {
this.previousHighest = this.highest;
}
if (this.lowest === void 0) {
this.lowest = value;
} else if (value.lt(this.lowest)) {
this.previousLowest = this.lowest;
this.lowest = value;
} else {
this.previousLowest = this.lowest;
}
this.previousResult = this.result;
return this.result = value;
}
};
var NumberIndicatorSeries = class extends BaseIndicatorSeries {
setResult(value, replace) {
if (replace) {
this.highest = this.previousHighest;
this.lowest = this.previousLowest;
this.result = this.previousResult;
}
if (this.highest === void 0) {
this.highest = value;
} else if (value > this.highest) {
this.previousHighest = this.highest;
this.highest = value;
} else {
this.previousHighest = this.highest;
}
if (this.lowest === void 0) {
this.lowest = value;
} else if (value < this.lowest) {
this.previousLowest = this.lowest;
this.lowest = value;
} else {
this.previousLowest = this.lowest;
}
this.previousResult = this.result;
return this.result = value;
}
};
// src/MA/MovingAverage.ts
var MovingAverage = class extends BigIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
}
};
var FasterMovingAverage = class extends NumberIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
}
};
// src/ABANDS/AccelerationBands.ts
var import_big2 = __toESM(require("big.js"), 1);
// src/util/getAverage.ts
var import_big = __toESM(require("big.js"), 1);
function getAverage(values) {
const sum = values.reduce((prev, current) => {
return prev.add(current);
}, new import_big.default(0));
return sum.div(values.length || 1);
}
function getFasterAverage(values) {
return values.length ? values.reduce((sum, x) => sum + x, 0) / values.length : 0;
}
// src/util/pushUpdate.ts
function pushUpdate(array, replace, item, maxLength) {
if (array.length > 0 && replace === true) {
array[array.length - 1] = item;
} else {
array.push(item);
}
if (array.length > maxLength) {
return array.shift();
}
return null;
}
// src/SMA/SMA.ts
var SMA = class extends MovingAverage {
prices = [];
update(price, replace) {
pushUpdate(this.prices, replace, price, this.interval);
if (this.prices.length === this.interval) {
return this.setResult(getAverage(this.prices), replace);
}
return null;
}
};
var FasterSMA = class extends FasterMovingAverage {
prices = [];
update(price, replace) {
pushUpdate(this.prices, replace, price, this.interval);
if (this.prices.length === this.interval) {
return this.setResult(getFasterAverage(this.prices), replace);
}
return null;
}
};
// src/ABANDS/AccelerationBands.ts
var AccelerationBands = class extends TechnicalIndicator {
/**
* Acceleration Bands (ABANDS)
* Type: Volatility
*
* Acceleration bands created by Price Headley are set as an envelope around a moving average. The upper and lower
* bands are of equal distance from the middle band.
*
* Two consecutive closes outside Acceleration Bands suggest an entry point in the direction of the breakout (either
* bullish or bearish). A long position is usually kept till the first close back inside the bands.
*
* @param interval The interval that is being used for the three moving averages which create lower, middle and upper
* bands
* @param width A coefficient specifying the distance between the middle band and upper/lower bands
* @param SmoothingIndicator Which moving average (SMA, EMA, ...) to use
*
* @see https://www.tradingtechnologies.com/xtrader-help/x-study/technical-indicator-definitions/acceleration-bands-abands/
* @see https://www.motivewave.com/studies/acceleration_bands.htm
* @see https://github.com/QuantConnect/Lean/blob/master/Indicators/AccelerationBands.cs
* @see https://github.com/twopirllc/pandas-ta/blob/master/pandas_ta/volatility/accbands.py
*/
constructor(interval, width, SmoothingIndicator = SMA) {
super();
this.interval = interval;
this.width = width;
this.lowerBand = new SmoothingIndicator(interval);
this.middleBand = new SmoothingIndicator(interval);
this.upperBand = new SmoothingIndicator(interval);
}
lowerBand;
middleBand;
upperBand;
get isStable() {
return this.middleBand.isStable;
}
update({ high, low, close }, replace) {
const highPlusLow = new import_big2.default(high).plus(low);
const coefficient = highPlusLow.eq(0) ? new import_big2.default(0) : new import_big2.default(high).minus(low).div(highPlusLow).mul(this.width);
this.lowerBand.update(new import_big2.default(low).mul(new import_big2.default(1).minus(coefficient)), replace);
this.middleBand.update(close, replace);
this.upperBand.update(new import_big2.default(high).mul(new import_big2.default(1).plus(coefficient)), replace);
if (this.isStable) {
return this.result = {
lower: this.lowerBand.getResultOrThrow(),
middle: this.middleBand.getResultOrThrow(),
upper: this.upperBand.getResultOrThrow()
};
}
return null;
}
};
var FasterAccelerationBands = class extends TechnicalIndicator {
constructor(interval, width, SmoothingIndicator = FasterSMA) {
super();
this.interval = interval;
this.width = width;
this.lowerBand = new SmoothingIndicator(interval);
this.middleBand = new SmoothingIndicator(interval);
this.upperBand = new SmoothingIndicator(interval);
}
lowerBand;
middleBand;
upperBand;
update({ high, low, close }, replace) {
const highPlusLow = high + low;
const coefficient = highPlusLow === 0 ? 0 : (high - low) / highPlusLow * this.width;
this.lowerBand.update(low * (1 - coefficient), replace);
this.middleBand.update(close, replace);
this.upperBand.update(high * (1 + coefficient), replace);
if (this.isStable) {
return this.result = {
lower: this.lowerBand.getResultOrThrow(),
middle: this.middleBand.getResultOrThrow(),
upper: this.upperBand.getResultOrThrow()
};
}
return null;
}
get isStable() {
return this.middleBand.isStable;
}
};
// src/AO/AO.ts
var import_big3 = __toESM(require("big.js"), 1);
var AO = class extends BigIndicatorSeries {
constructor(shortInterval, longInterval, SmoothingIndicator = SMA) {
super();
this.shortInterval = shortInterval;
this.longInterval = longInterval;
this.short = new SmoothingIndicator(shortInterval);
this.long = new SmoothingIndicator(longInterval);
}
long;
short;
update({ low, high }, replace) {
const candleSum = new import_big3.default(low).add(high);
const medianPrice = candleSum.div(2);
this.short.update(medianPrice, replace);
this.long.update(medianPrice, replace);
if (this.long.isStable) {
return this.setResult(this.short.getResultOrThrow().sub(this.long.getResultOrThrow()), replace);
}
return null;
}
};
var FasterAO = class extends NumberIndicatorSeries {
constructor(shortInterval, longInterval, SmoothingIndicator = FasterSMA) {
super();
this.shortInterval = shortInterval;
this.longInterval = longInterval;
this.short = new SmoothingIndicator(shortInterval);
this.long = new SmoothingIndicator(longInterval);
}
long;
short;
update({ low, high }, replace) {
const medianPrice = (low + high) / 2;
this.short.update(medianPrice, replace);
this.long.update(medianPrice, replace);
if (this.long.isStable) {
return this.setResult(this.short.getResultOrThrow() - this.long.getResultOrThrow(), replace);
}
return null;
}
};
// src/MOM/MOM.ts
var import_big4 = __toESM(require("big.js"), 1);
var MOM = class extends BigIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
this.historyLength = interval + 1;
this.history = [];
}
history;
historyLength;
update(value, replace) {
pushUpdate(this.history, replace, value, this.historyLength);
if (this.history.length === this.historyLength) {
return this.setResult(new import_big4.default(value).minus(this.history[0]), replace);
}
return null;
}
};
var FasterMOM = class extends NumberIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
this.historyLength = interval + 1;
this.history = [];
}
history;
historyLength;
update(value, replace) {
pushUpdate(this.history, replace, value, this.historyLength);
if (this.history.length === this.historyLength) {
return this.setResult(value - this.history[0], replace);
}
return null;
}
};
// src/AC/AC.ts
var AC = class extends BigIndicatorSeries {
constructor(shortAO, longAO, signalInterval) {
super();
this.shortAO = shortAO;
this.longAO = longAO;
this.signalInterval = signalInterval;
this.ao = new AO(shortAO, longAO);
this.momentum = new MOM(1);
this.signal = new SMA(signalInterval);
}
ao;
momentum;
signal;
update(input, replace) {
const ao = this.ao.update(input, replace);
if (ao) {
this.signal.update(ao, replace);
if (this.signal.isStable) {
const result = this.setResult(ao.sub(this.signal.getResultOrThrow()), replace);
this.momentum.update(result, replace);
return result;
}
}
return null;
}
};
var FasterAC = class extends NumberIndicatorSeries {
constructor(shortAO, longAO, signalInterval) {
super();
this.shortAO = shortAO;
this.longAO = longAO;
this.signalInterval = signalInterval;
this.ao = new FasterAO(shortAO, longAO);
this.momentum = new FasterMOM(1);
this.signal = new FasterSMA(signalInterval);
}
ao;
momentum;
signal;
update(input, replace) {
const ao = this.ao.update(input, replace);
if (ao) {
this.signal.update(ao, replace);
if (this.signal.isStable) {
const result = this.setResult(ao - this.signal.getResultOrThrow(), replace);
this.momentum.update(result, replace);
return result;
}
}
return null;
}
};
// src/WSMA/WSMA.ts
var import_big5 = __toESM(require("big.js"), 1);
var WSMA = class extends MovingAverage {
constructor(interval) {
super(interval);
this.interval = interval;
this.indicator = new SMA(interval);
this.smoothingFactor = new import_big5.default(1).div(this.interval);
}
indicator;
smoothingFactor;
update(price, replace) {
const sma = this.indicator.update(price, replace);
if (replace && this.previousResult) {
const smoothed = new import_big5.default(price).minus(this.previousResult).mul(this.smoothingFactor);
return this.setResult(smoothed.plus(this.previousResult), replace);
} else if (!replace && this.result !== void 0) {
const smoothed = new import_big5.default(price).minus(this.result).mul(this.smoothingFactor);
return this.setResult(smoothed.plus(this.result), replace);
} else if (this.result === void 0 && sma !== null) {
return this.setResult(sma, replace);
}
return null;
}
};
var FasterWSMA = class extends NumberIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
this.indicator = new FasterSMA(interval);
this.smoothingFactor = 1 / this.interval;
}
indicator;
smoothingFactor;
update(price, replace) {
const sma = this.indicator.update(price, replace);
if (replace && this.previousResult !== void 0) {
const smoothed = (price - this.previousResult) * this.smoothingFactor;
return this.setResult(smoothed + this.previousResult, replace);
} else if (!replace && this.result !== void 0) {
const smoothed = (price - this.result) * this.smoothingFactor;
return this.setResult(smoothed + this.result, replace);
} else if (this.result === void 0 && sma !== null) {
return this.setResult(sma, replace);
}
return null;
}
};
// src/TR/TR.ts
var import_big7 = __toESM(require("big.js"), 1);
// src/util/getMaximum.ts
var import_big6 = __toESM(require("big.js"), 1);
function getMaximum(values) {
let max = new import_big6.default(Number.MIN_SAFE_INTEGER);
for (const value of values) {
if (max.lt(value)) {
max = new import_big6.default(value);
}
}
return max;
}
// src/TR/TR.ts
var TR = class extends BigIndicatorSeries {
previousCandle;
secondLastCandle;
update(candle, replace) {
const high = new import_big7.default(candle.high);
const highLow = high.minus(candle.low);
if (this.previousCandle && replace) {
this.previousCandle = this.secondLastCandle;
}
if (this.previousCandle) {
const highClose = high.minus(this.previousCandle.close).abs();
const lowClose = new import_big7.default(candle.low).minus(this.previousCandle.close).abs();
this.secondLastCandle = this.previousCandle;
this.previousCandle = candle;
return this.setResult(getMaximum([highLow, highClose, lowClose]), replace);
}
this.secondLastCandle = this.previousCandle;
this.previousCandle = candle;
return this.setResult(highLow, replace);
}
};
var FasterTR = class extends NumberIndicatorSeries {
previousCandle;
twoPreviousCandle;
update(candle, replace) {
const { high, low } = candle;
const highLow = high - low;
if (this.previousCandle && replace) {
this.previousCandle = this.twoPreviousCandle;
}
if (this.previousCandle) {
const highClose = Math.abs(high - this.previousCandle.close);
const lowClose = Math.abs(low - this.previousCandle.close);
this.twoPreviousCandle = this.previousCandle;
this.previousCandle = candle;
return this.setResult(Math.max(highLow, highClose, lowClose), replace);
}
this.twoPreviousCandle = this.previousCandle;
this.previousCandle = candle;
return this.setResult(highLow, replace);
}
};
// src/ATR/ATR.ts
var ATR = class extends BigIndicatorSeries {
constructor(interval, SmoothingIndicator = WSMA) {
super();
this.interval = interval;
this.tr = new TR();
this.smoothing = new SmoothingIndicator(interval);
}
tr;
smoothing;
update(candle, replace) {
const trueRange = this.tr.update(candle, replace);
this.smoothing.update(trueRange, replace);
if (this.smoothing.isStable) {
return this.setResult(this.smoothing.getResultOrThrow(), replace);
}
return null;
}
};
var FasterATR = class extends NumberIndicatorSeries {
constructor(interval, SmoothingIndicator = FasterWSMA) {
super();
this.interval = interval;
this.tr = new FasterTR();
this.smoothing = new SmoothingIndicator(interval);
}
tr;
smoothing;
update(candle, replace) {
const trueRange = this.tr.update(candle, replace);
this.smoothing.update(trueRange, replace);
if (this.smoothing.isStable) {
return this.setResult(this.smoothing.getResultOrThrow(), replace);
}
return null;
}
};
// src/DX/DX.ts
var import_big8 = __toESM(require("big.js"), 1);
var DX = class extends BigIndicatorSeries {
constructor(interval, SmoothingIndicator = WSMA) {
super();
this.interval = interval;
this.atr = new ATR(this.interval, SmoothingIndicator);
this.movesDown = new SmoothingIndicator(this.interval);
this.movesUp = new SmoothingIndicator(this.interval);
}
movesUp;
movesDown;
previousCandle;
secondLastCandle;
atr;
/** Negative (Minus) Directional Indicator (-DI) */
mdi;
/** Positive (Plus) Directional Indicator (+DI) */
pdi;
updateState(candle, pdm, mdm, replace) {
this.atr.update(candle, replace);
this.movesDown.update(mdm, replace);
this.movesUp.update(pdm, replace);
if (this.previousCandle) {
this.secondLastCandle = this.previousCandle;
}
this.previousCandle = candle;
}
update(candle, replace) {
if (!this.previousCandle) {
this.updateState(candle, 0, 0, replace);
return null;
}
if (this.secondLastCandle && replace) {
this.previousCandle = this.secondLastCandle;
}
const currentHigh = new import_big8.default(candle.high);
const previousHigh = new import_big8.default(this.previousCandle.high);
const currentLow = new import_big8.default(candle.low);
const previousLow = new import_big8.default(this.previousCandle.low);
const higherHigh = currentHigh.minus(previousHigh);
const lowerLow = previousLow.minus(currentLow);
const noHigherHighs = higherHigh.lt(0);
const lowsRiseFaster = higherHigh.lt(lowerLow);
const pdm = noHigherHighs || lowsRiseFaster ? new import_big8.default(0) : higherHigh;
const noLowerLows = lowerLow.lt(0);
const highsRiseFaster = lowerLow.lt(higherHigh);
const mdm = noLowerLows || highsRiseFaster ? new import_big8.default(0) : lowerLow;
this.updateState(candle, pdm, mdm, replace);
if (this.movesUp.isStable) {
this.pdi = this.movesUp.getResultOrThrow().div(this.atr.getResultOrThrow());
this.mdi = this.movesDown.getResultOrThrow().div(this.atr.getResultOrThrow());
const dmDiff = this.pdi.minus(this.mdi).abs();
const dmSum = this.pdi.plus(this.mdi);
if (dmSum.eq(0)) {
return this.setResult(new import_big8.default(0), replace);
}
return this.setResult(dmDiff.div(dmSum).mul(100), replace);
}
return null;
}
};
var FasterDX = class extends NumberIndicatorSeries {
constructor(interval, SmoothingIndicator = FasterWSMA) {
super();
this.interval = interval;
this.atr = new FasterATR(this.interval, SmoothingIndicator);
this.movesDown = new SmoothingIndicator(this.interval);
this.movesUp = new SmoothingIndicator(this.interval);
}
movesUp;
movesDown;
previousCandle;
secondLastCandle;
atr;
mdi;
pdi;
updateState(candle, pdm, mdm, replace) {
this.atr.update(candle, replace);
this.movesUp.update(pdm, replace);
this.movesDown.update(mdm, replace);
if (this.previousCandle) {
this.secondLastCandle = this.previousCandle;
}
this.previousCandle = candle;
}
update(candle, replace) {
if (!this.previousCandle) {
this.updateState(candle, 0, 0, replace);
return null;
}
if (this.secondLastCandle && replace) {
this.previousCandle = this.secondLastCandle;
}
const currentHigh = candle.high;
const previousHigh = this.previousCandle.high;
const currentLow = candle.low;
const previousLow = this.previousCandle.low;
const higherHigh = currentHigh - previousHigh;
const lowerLow = previousLow - currentLow;
const noHigherHighs = higherHigh < 0;
const lowsRiseFaster = higherHigh < lowerLow;
const pdm = noHigherHighs || lowsRiseFaster ? 0 : higherHigh;
const noLowerLows = lowerLow < 0;
const highsRiseFaster = lowerLow < higherHigh;
const mdm = noLowerLows || highsRiseFaster ? 0 : lowerLow;
this.updateState(candle, pdm, mdm, replace);
if (this.movesUp.isStable) {
this.pdi = this.movesUp.getResultOrThrow() / this.atr.getResultOrThrow();
this.mdi = this.movesDown.getResultOrThrow() / this.atr.getResultOrThrow();
const dmDiff = Math.abs(this.pdi - this.mdi);
const dmSum = this.pdi + this.mdi;
if (dmSum === 0) {
return this.setResult(0, replace);
}
return this.setResult(dmDiff / dmSum * 100, replace);
}
return null;
}
};
// src/ADX/ADX.ts
var ADX = class extends BigIndicatorSeries {
constructor(interval, SmoothingIndicator = WSMA) {
super();
this.interval = interval;
this.smoothed = new SmoothingIndicator(this.interval);
this.dx = new DX(this.interval, SmoothingIndicator);
}
dx;
smoothed;
get mdi() {
return this.dx.mdi;
}
get pdi() {
return this.dx.pdi;
}
update(candle, replace) {
const result = this.dx.update(candle, replace);
if (result !== null) {
this.smoothed.update(result, replace);
}
if (this.smoothed.isStable) {
return this.setResult(this.smoothed.getResultOrThrow(), replace);
}
return null;
}
};
var FasterADX = class extends NumberIndicatorSeries {
constructor(interval, SmoothingIndicator = FasterWSMA) {
super();
this.interval = interval;
this.smoothed = new SmoothingIndicator(this.interval);
this.dx = new FasterDX(interval, SmoothingIndicator);
}
dx;
smoothed;
get mdi() {
return this.dx.mdi;
}
get pdi() {
return this.dx.pdi;
}
update(candle, replace) {
const result = this.dx.update(candle, replace);
if (result !== null) {
this.smoothed.update(result, replace);
}
if (this.smoothed.isStable) {
return this.setResult(this.smoothed.getResultOrThrow(), replace);
}
return null;
}
};
// src/BBANDS/BollingerBands.ts
var import_big12 = __toESM(require("big.js"), 1);
// src/util/getMinimum.ts
var import_big9 = __toESM(require("big.js"), 1);
function getMinimum(values) {
let min = new import_big9.default(Number.MAX_SAFE_INTEGER);
for (const value of values) {
if (min.gt(value)) {
min = new import_big9.default(value);
}
}
return min;
}
// src/util/getStandardDeviation.ts
var import_big10 = __toESM(require("big.js"), 1);
function getStandardDeviation(values, average) {
const middle = average || getAverage(values);
const squaredDifferences = values.map((value) => new import_big10.default(value).sub(middle).pow(2));
return getAverage(squaredDifferences).sqrt();
}
function getFasterStandardDeviation(values, average) {
const middle = average || getFasterAverage(values);
const squaredDifferences = values.map((value) => value - middle).map((value) => value * value);
const averageDifference = getFasterAverage(squaredDifferences);
return Math.sqrt(averageDifference);
}
// src/util/getStreaks.ts
function getStreaks(prices, keepSide) {
const streaks = [];
let currentStreak = 0;
function saveStreak(i) {
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 = keepSide === "up" && prices[i] > prices[i - 1];
const isDownward = keepSide === "down" && prices[i] < prices[i - 1];
if (isUpward || isDownward) {
currentStreak++;
} else {
if (currentStreak > 0) {
saveStreak(i);
}
currentStreak = 0;
}
}
if (currentStreak > 0) {
saveStreak(prices.length);
}
return streaks;
}
// src/util/Period.ts
var import_big11 = __toESM(require("big.js"), 1);
var Period = class extends TechnicalIndicator {
constructor(interval) {
super();
this.interval = interval;
this.values = [];
}
values;
/** Highest return value during the current period. */
_highest;
/** Lowest return value during the current period. */
_lowest;
get highest() {
return this._highest;
}
get lowest() {
return this._lowest;
}
update(value, replace) {
pushUpdate(this.values, replace, new import_big11.default(value), this.interval);
if (this.values.length === this.interval) {
this._lowest = getMinimum(this.values);
this._highest = getMaximum(this.values);
return this.result = {
highest: this._highest,
lowest: this._lowest
};
}
return null;
}
};
var FasterPeriod = class extends TechnicalIndicator {
constructor(interval) {
super();
this.interval = interval;
this.values = [];
}
values;
/** Highest return value during the current period. */
_highest;
/** Lowest return value during the current period. */
_lowest;
get highest() {
return this._highest;
}
get lowest() {
return this._lowest;
}
update(value, replace) {
pushUpdate(this.values, replace, value, this.interval);
if (this.values.length === this.interval) {
this._lowest = Math.min(...this.values);
this._highest = Math.max(...this.values);
return this.result = {
highest: this._highest,
lowest: this._lowest
};
}
return null;
}
};
// src/BBANDS/BollingerBands.ts
var BollingerBands = class extends TechnicalIndicator {
/**
* @param interval - The time period to be used in calculating the Middle Band
* @param deviationMultiplier - The number of standard deviations away from the Middle Band that the Upper and Lower
* Bands should be
*/
constructor(interval, deviationMultiplier = 2) {
super();
this.interval = interval;
this.deviationMultiplier = deviationMultiplier;
}
prices = [];
update(price, replace) {
const dropOut = pushUpdate(this.prices, replace, new import_big12.default(price), this.interval);
if (dropOut) {
const middle = getAverage(this.prices);
const standardDeviation = getStandardDeviation(this.prices, middle);
return this.result = {
lower: middle.sub(standardDeviation.times(this.deviationMultiplier)),
middle,
upper: middle.add(standardDeviation.times(this.deviationMultiplier))
};
}
return null;
}
};
var FasterBollingerBands = class extends TechnicalIndicator {
constructor(interval, deviationMultiplier = 2) {
super();
this.interval = interval;
this.deviationMultiplier = deviationMultiplier;
}
prices = [];
update(price, replace) {
const dropOut = pushUpdate(this.prices, replace, price, this.interval);
if (dropOut) {
const middle = getFasterAverage(this.prices);
const standardDeviation = getFasterStandardDeviation(this.prices, middle);
return this.result = {
lower: middle - standardDeviation * this.deviationMultiplier,
middle,
upper: middle + standardDeviation * this.deviationMultiplier
};
}
return null;
}
};
// src/BBW/BollingerBandsWidth.ts
var BollingerBandsWidth = class extends BigIndicatorSeries {
constructor(bollingerBands) {
super();
this.bollingerBands = bollingerBands;
}
update(price, replace) {
const result = this.bollingerBands.update(price, replace);
if (result) {
return this.setResult(result.upper.minus(result.lower).div(result.middle), replace);
}
return null;
}
};
var FasterBollingerBandsWidth = class extends NumberIndicatorSeries {
constructor(bollingerBands) {
super();
this.bollingerBands = bollingerBands;
}
update(price, replace) {
const result = this.bollingerBands.update(price, replace);
if (result) {
return this.setResult((result.upper - result.lower) / result.middle, replace);
}
return null;
}
};
// src/CCI/CCI.ts
var import_big14 = __toESM(require("big.js"), 1);
// src/MAD/MAD.ts
var import_big13 = __toESM(require("big.js"), 1);
var MAD = class _MAD extends BigIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
}
prices = [];
update(price, replace) {
pushUpdate(this.prices, replace, price, this.interval);
if (this.prices.length === this.interval) {
return this.setResult(_MAD.getResultFromBatch(this.prices), replace);
}
return null;
}
static getResultFromBatch(prices, average) {
const mean = average || getAverage(prices);
let sum = new import_big13.default(0);
for (let i = 0; i < prices.length; i++) {
const deviation = new import_big13.default(prices[i]).minus(mean).abs();
sum = sum.plus(deviation);
}
return sum.div(prices.length || 1);
}
};
var FasterMAD = class extends NumberIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
}
prices = [];
update(price, replace) {
pushUpdate(this.prices, replace, price, this.interval);
if (this.prices.length === this.interval) {
const mean = getFasterAverage(this.prices);
let sum = 0;
for (let i = 0; i < this.interval; i++) {
const deviation = Math.abs(this.prices[i] - mean);
sum += deviation;
}
return this.setResult(sum / this.interval, replace);
}
return null;
}
static getResultFromBatch(prices, average) {
const mean = average || getFasterAverage(prices);
let sum = 0;
for (let i = 0; i < prices.length; i++) {
const deviation = Math.abs(prices[i] - mean);
sum += deviation;
}
return sum / prices.length;
}
};
// src/CCI/CCI.ts
var CCI = class extends BigIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
this.sma = new SMA(this.interval);
this.typicalPrices = [];
}
sma;
typicalPrices;
update(candle, replace) {
const typicalPrice = this.cacheTypicalPrice(candle, replace);
this.sma.update(typicalPrice, replace);
if (this.sma.isStable) {
const mean = this.sma.getResultOrThrow();
const meanDeviation = MAD.getResultFromBatch(this.typicalPrices, mean);
const numerator = typicalPrice.minus(mean);
const denominator = new import_big14.default(0.015).mul(meanDeviation);
const result = numerator.div(denominator);
return this.setResult(result, replace);
}
return null;
}
cacheTypicalPrice({ high, low, close }, replace) {
const typicalPrice = new import_big14.default(high).plus(low).plus(close).div(3);
pushUpdate(this.typicalPrices, replace, typicalPrice, this.interval);
return typicalPrice;
}
};
var FasterCCI = class extends NumberIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
this.sma = new FasterSMA(this.interval);
this.typicalPrices = [];
}
sma;
typicalPrices;
update(candle, replace) {
const typicalPrice = this.cacheTypicalPrice(candle, replace);
this.sma.update(typicalPrice, replace);
if (this.sma.isStable) {
const mean = this.sma.getResultOrThrow();
const meanDeviation = FasterMAD.getResultFromBatch(this.typicalPrices, mean);
const numerator = typicalPrice - mean;
const denominator = 0.015 * meanDeviation;
return this.setResult(numerator / denominator, replace);
}
return null;
}
cacheTypicalPrice({ high, low, close }, replace) {
const typicalPrice = (high + low + close) / 3;
pushUpdate(this.typicalPrices, replace, typicalPrice, this.interval);
return typicalPrice;
}
};
// src/CG/CG.ts
var import_big15 = __toESM(require("big.js"), 1);
var CG = class extends BigIndicatorSeries {
constructor(interval, signalInterval) {
super();
this.interval = interval;
this.signalInterval = signalInterval;
this.signal = new SMA(signalInterval);
}
signal;
prices = [];
get isStable() {
return this.signal.isStable;
}
update(price, replace) {
pushUpdate(this.prices, replace, new import_big15.default(price), this.interval);
let nominator = new import_big15.default(0);
let denominator = new import_big15.default(0);
for (let i = 0; i < this.prices.length; ++i) {
const price2 = this.prices[i];
nominator = nominator.plus(price2.mul(i + 1));
denominator = denominator.plus(price2);
}
const cg = denominator.gt(0) ? nominator.div(denominator) : new import_big15.default(0);
this.signal.update(cg, replace);
if (this.signal.isStable) {
return this.setResult(cg, replace);
}
return null;
}
};
var FasterCG = class extends NumberIndicatorSeries {
constructor(interval, signalInterval) {
super();
this.interval = interval;
this.signalInterval = signalInterval;
this.signal = new FasterSMA(signalInterval);
}
signal;
prices = [];
get isStable() {
return this.signal.isStable;
}
update(price, replace) {
pushUpdate(this.prices, replace, price, this.interval);
let nominator = 0;
let denominator = 0;
for (let i = 0; i < this.prices.length; ++i) {
const price2 = this.prices[i];
nominator = nominator + price2 * (i + 1);
denominator = denominator + price2;
}
const cg = denominator > 0 ? nominator / denominator : 0;
this.signal.update(cg, replace);
if (this.signal.isStable) {
return this.setResult(cg, replace);
}
return null;
}
};
// src/EMA/EMA.ts
var import_big16 = __toESM(require("big.js"), 1);
var EMA = class extends MovingAverage {
constructor(interval) {
super(interval);
this.interval = interval;
this.weightFactor = 2 / (this.interval + 1);
}
pricesCounter = 0;
weightFactor;
update(_price, replace) {
if (!replace) {
this.pricesCounter++;
} else if (replace && this.pricesCounter === 0) {
this.pricesCounter++;
}
const price = new import_big16.default(_price);
if (replace && this.previousResult) {
return this.setResult(
price.times(this.weightFactor).add(this.previousResult.times(1 - this.weightFactor)),
replace
);
}
return this.setResult(
price.times(this.weightFactor).add((this.result ?? price).times(1 - this.weightFactor)),
replace
);
}
getResultOrThrow() {
if (this.pricesCounter < this.interval) {
throw new NotEnoughDataError();
}
return this.result;
}
get isStable() {
try {
this.getResultOrThrow();
return true;
} catch {
return false;
}
}
};
var FasterEMA = class extends FasterMovingAverage {
constructor(interval) {
super(interval);
this.interval = interval;
this.weightFactor = 2 / (this.interval + 1);
}
pricesCounter = 0;
weightFactor;
update(price, replace) {
if (!replace) {
this.pricesCounter++;
} else if (replace && this.pricesCounter === 0) {
this.pricesCounter++;
}
if (replace && this.previousResult !== void 0) {
return this.setResult(price * this.weightFactor + this.previousResult * (1 - this.weightFactor), replace);
}
return this.setResult(
price * this.weightFactor + (this.result !== void 0 ? this.result : price) * (1 - this.weightFactor),
replace
);
}
getResultOrThrow() {
if (this.pricesCounter < this.interval) {
throw new NotEnoughDataError();
}
return this.result;
}
get isStable() {
try {
this.getResultOrThrow();
return true;
} catch {
return false;
}
}
};
// src/DEMA/DEMA.ts
var DEMA = class extends BigIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
this.inner = new EMA(interval);
this.outer = new EMA(interval);
}
inner;
outer;
update(price, replace) {
const innerResult = this.inner.update(price, replace);
const outerResult = this.outer.update(innerResult, replace);
return this.setResult(innerResult.times(2).sub(outerResult), replace);
}
get isStable() {
return this.outer.isStable;
}
};
var FasterDEMA = class extends NumberIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
this.inner = new FasterEMA(interval);
this.outer = new FasterEMA(interval);
}
inner;
outer;
update(price, replace) {
const innerResult = this.inner.update(price, replace);
const outerResult = this.outer.update(innerResult, replace);
return this.setResult(innerResult * 2 - outerResult, replace);
}
get isStable() {
return this.outer.isStable;
}
};
// src/DMA/DMA.ts
var DMA = class extends TechnicalIndicator {
short;
long;
constructor(short, long, Indicator = SMA) {
super();
this.short = new Indicator(short);
this.long = new Indicator(long);
}
get isStable() {
return this.long.isStable;
}
update(price, replace) {
this.short.update(price, replace);
this.long.update(price, replace);
if (this.isStable) {
return this.result = {
long: this.long.getResultOrThrow(),
short: this.short.getResultOrThrow()
};
}
return null;
}
};
var FasterDMA = class extends TechnicalIndicator {
short;
long;
constructor(short, long, SmoothingIndicator = FasterSMA) {
super();
this.short = new SmoothingIndicator(short);
this.long = new SmoothingIndicator(long);
}
get isStable() {
return this.long.isStable;
}
update(price, replace) {
this.short.update(price, replace);
this.long.update(price, replace);
if (this.isStable) {
return this.result = {
long: this.long.getResultOrThrow(),
short: this.short.getResultOrThrow()
};
}
return null;
}
};
// src/MACD/MACD.ts
var import_big17 = __toESM(require("big.js"), 1);
var MACD = class extends TechnicalIndicator {
prices = [];
long;
short;
signal;
constructor(config) {
super();
this.long = new config.indicator(config.longInterval);
this.short = new config.indicator(config.shortInterval);
this.signal = new config.indicator(config.signalInterval);
}
update(_price, replace) {
const price = new import_big17.default(_price);
pushUpdate(this.prices, replace, price, this.long.interval);
const short = this.short.update(price, replace);
const long = this.long.update(price, replace);
if (this.prices.length === this.long.interval) {
const macd = short.sub(long);
const signal = this.signal.update(macd, replace);
return this.result = {
histogram: macd.sub(signal),
macd,
signal
};
}
return null;
}
};
var FasterMACD = class extends TechnicalIndicator {
constructor(short, long, signal) {
super();
this.short = short;
this.long = long;
this.signal = signal;
}
prices = [];
update(price, replace) {
pushUpdate(this.prices, replace, price, this.long.interval);
const short = this.short.update(price, replace);
const long = this.long.update(price, replace);
if (this.prices.length === this.long.interval) {
const macd = short - long;
const signal = this.signal.update(macd, replace);
return this.result = {
histogram: macd - signal,
macd,
signal
};
}
return null;
}
};
// src/OBV/OBV.ts
var import_big18 = __toESM(require("big.js"), 1);
var OBV = class extends BigIndicatorSeries {
candles = [];
update(candle, replace) {
pushUpdate(this.candles, replace, candle, 2);
if (this.candles.length === 1) {
return null;
}
const prevCandle = this.candles[this.candles.length - 2];
const prevPrice = prevCandle.close;
const prevResult = this.result ?? new import_big18.default(0);
const currentPrice = new import_big18.default(candle.close);
const nextResult = currentPrice.gt(prevPrice) ? candle.volume : currentPrice.lt(prevPrice) ? -candle.volume : 0;
return this.setResult(prevResult.add(nextResult), replace);
}
};
var FasterOBV = class extends NumberIndicatorSeries {
candles = [];
update(candle, replace) {
pushUpdate(this.candles, replace, candle, 2);
if (this.candles.length === 1) {
return null;
}
const prevCandle = this.candles[this.candles.length - 2];
const prevPrice = prevCandle.close;
const prevResult = this.result ?? 0;
const currentPrice = candle.close;
const nextResult = currentPrice > prevPrice ? candle.volume : currentPrice < prevPrice ? -candle.volume : 0;
return this.setResult(prevResult + nextResult, false);
}
};
// src/RMA/RMA.ts
var import_big19 = __toESM(require("big.js"), 1);
var RMA = class extends MovingAverage {
constructor(interval) {
super(interval);
this.interval = interval;
this.weightFactor = 1 / this.interval;
}
pricesCounter = 0;
weightFactor;
update(_price, replace) {
if (!replace) {
this.pricesCounter++;
} else if (replace && this.pricesCounter === 0) {
this.pricesCounter++;
}
const price = new import_big19.default(_price);
if (replace && this.previousResult) {
return this.setResult(
price.times(this.weightFactor).add(this.previousResult.times(1 - this.weightFactor)),
replace
);
}
return this.setResult(
price.times(this.weightFactor).add((this.result ?? price).times(1 - this.weightFactor)),
replace
);
}
getResultOrThrow() {
if (this.pricesCounter < this.interval) {
throw new NotEnoughDataError();
}
return this.result;
}
get isStable() {
try {
this.getResultOrThrow();
return true;
} catch {
return false;
}
}
};
var FasterRMA = class extends FasterMovingAverage {
constructor(interval) {
super(interval);
this.interval = interval;
this.weightFactor = 1 / this.interval;
}
pricesCounter = 0;
weightFactor;
update(price, replace) {
if (!replace) {
this.pricesCounter++;
} else if (replace && this.pricesCounter === 0) {
this.pricesCounter++;
}
if (replace && this.previousResult !== void 0) {
return this.setResult(price * this.weightFactor + this.previousResult * (1 - this.weightFactor), replace);
}
return this.setResult(
price * this.weightFactor + (this.result !== void 0 ? this.result : price) * (1 - this.weightFactor),
replace
);
}
getResultOrThrow() {
if (this.pricesCounter < this.interval) {
throw new NotEnoughDataError();
}
return this.result;
}
get isStable() {
try {
this.getResultOrThrow();
return true;
} catch {
return false;
}
}
};
// src/ROC/ROC.ts
var import_big20 = __toESM(require("big.js"), 1);
var ROC = class extends BigIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
}
prices = [];
update(price, replace) {
const comparePrice = pushUpdate(this.prices, replace, price, this.interval);
if (comparePrice) {
return this.setResult(new import_big20.default(price).sub(comparePrice).div(comparePrice), replace);
}
return null;
}
};
var FasterROC = class extends NumberIndicatorSeries {
constructor(interval) {
super();
this.interval = interval;
}
prices = [];
update(price, replace) {
const comparePrice = pushUpdate(this.prices, replace, price, this.interval);
if (comparePrice) {
return this.setResult((price - comparePrice) / comparePrice, replace);
}
return null;
}
};
// src/RSI/RSI.ts
var import_big21 = __toESM(require("big.js"), 1);
var RSI = class extends BigIndicatorSeries {
constructor(interval, SmoothingIndicator = WSMA) {
super();
this.interval = interval;
this.avgGain = new SmoothingIndicator(this.interval);
this.avgLoss = new SmoothingIndicator(this.interval);
}
previousPrices = [];
avgGain;
avgLoss;
maxValue = new import_big21.default(100);
update(price, replace) {
pushUpdate(this.previousPrices, replace, price, this.interval);
if (this.previousPrices.length < 2) {
return null;
}
const currentPrice = new import_big21.default(price);
const previousPrice = new import_big21.default(this.previousPrices[this.previousPrices.length - 2]);
if (currentPrice.gt(previousPrice)) {
this.avgLoss.update(new import_big21.default(0), replace);
this.avgGain.update(currentPrice.sub(previousPrice), replace);
} else {
this.avgLoss.update(previousPrice.sub(currentPrice), replace);
this.avgGain.update(new import_big21.default(0), replace);
}
if (this.avgGain.isStable) {
const avgLoss = this.avgLoss.getResultOrThrow();
if (avgLoss.eq(0)) {
return this.setResult(new import_big21.default(100), replace);
}
const relativeStrength = this.avgGain.getResultOrThrow().div(avgLoss);
return this.setResult(this.maxValue.minus(this.maxValue.div(relativeStrength.add(1))), replace);
}
return null;
}
};
var FasterRSI = class extends NumberIndicatorSeries {
constructor(interval, SmoothingIndicator = FasterWSMA) {
super();
this.interval = interval;
this.avgGain = new SmoothingIndicator(this.interval);
this.avgLoss = new SmoothingIndicator(this.interval);
}
previousPrices = [];
avgGain;
avgLoss;
maxValue = 100;
update(price, replace) {
pushUpdate(this.previousPrices, replace, price, this.interval);
if (this.previousPrices.length < 2) {
return null;
}
const currentPrice = price;
const previousPrice = this.previousPrices[this.previousPrices.length - 2];
if (currentPrice > previousPrice) {
this.avgLoss.update(0, replace);
this.avgGain.update(price - previousPrice, replace);
} else {
this.avgLoss.update(previousPrice - currentPrice, replace);
this.avgGain.update(0, replace);
}
if (this.avgGain.isStable) {
const avgLoss = this.avgLoss.getResultOrThrow