fast-technical-indicators
Version:
High-performance technical indicators with zero dependencies - compatible with technicalindicators package
91 lines (90 loc) • 3.27 kB
JavaScript
import { ema } from '../moving-averages/ema';
export function forceindex(input) {
const { period = 13, close, volume } = input;
if (close.length !== volume.length || close.length < 2) {
return [];
}
// Calculate raw force index values
const rawForceIndex = [];
for (let i = 1; i < close.length; i++) {
const priceChange = close[i] - close[i - 1];
const forceValue = priceChange * volume[i];
rawForceIndex.push(forceValue);
}
if (period === 1) {
return rawForceIndex;
}
// Apply EMA smoothing
const smoothedForceIndex = ema({ period, values: rawForceIndex });
return smoothedForceIndex;
}
export class ForceIndex {
constructor(input) {
this.closeValues = [];
this.volumeValues = [];
this.initialized = false;
this.period = input.period || 13;
if (this.period === 1) {
return; // No EMA needed for period 1
}
// Create inline EMA calculator
const multiplier = 2 / (this.period + 1);
this.emaCalculator = {
values: [],
period: this.period,
multiplier,
previousEMA: undefined,
initialized: false,
nextValue: function (value) {
this.values.push(value);
if (!this.initialized) {
if (this.values.length === this.period) {
const sum = this.values.reduce((acc, val) => acc + val, 0);
this.previousEMA = sum / this.period;
this.initialized = true;
return this.previousEMA;
}
return undefined;
}
const currentEMA = (value - this.previousEMA) * this.multiplier + this.previousEMA;
this.previousEMA = currentEMA;
return currentEMA;
}
};
}
nextValue(close, volume) {
this.closeValues.push(close);
this.volumeValues.push(volume);
if (this.closeValues.length < 2) {
return undefined;
}
// Calculate raw force index
const currentClose = close;
const previousClose = this.closeValues[this.closeValues.length - 2];
const priceChange = currentClose - previousClose;
const forceValue = priceChange * volume;
if (this.period === 1) {
return forceValue;
}
// Apply EMA smoothing
return this.emaCalculator.nextValue(forceValue);
}
getResult() {
if (this.closeValues.length < 2) {
return [];
}
const currentClose = this.closeValues[this.closeValues.length - 1];
const previousClose = this.closeValues[this.closeValues.length - 2];
const currentVolume = this.volumeValues[this.volumeValues.length - 1];
const priceChange = currentClose - previousClose;
const forceValue = priceChange * currentVolume;
if (this.period === 1) {
return [forceValue];
}
if (this.emaCalculator.previousEMA !== undefined) {
return [this.emaCalculator.previousEMA];
}
return [];
}
}
ForceIndex.calculate = forceindex;