Klinger & Mass Index strategy
The Mass Index looks for trend reversals by analyzing the range between high and low prices and comparing moving averages of that range. The Klinger Oscillator is a volume-based indicator that compares money flow over time using short- and long-term volume averages. This strategy combines both indicators so the Klinger signal can confirm Mass Index reversal signals before entries or exits.
tip
This example strategy is machine generated using Gunbot AI. Review its behavior carefully in a simulated bot instance before using parts of this code in production.
// initialize customStratStore within pairLedger object
gb.data.pairLedger.customStratStore = gb.data.pairLedger.customStratStore || {};
// forced wait time reduces risk of double orders
function checkTime() {
return !gb.data.pairLedger.customStratStore.timeCheck || typeof gb.data.pairLedger.customStratStore.timeCheck !== "number"
? (gb.data.pairLedger.customStratStore.timeCheck = Date.now(), false)
: (Date.now() - gb.data.pairLedger.customStratStore.timeCheck > 8000);
}
const enoughTimePassed = checkTime();
// set timestamp for checkTime in next round
const setTimestamp = () => gb.data.pairLedger.customStratStore.timeCheck = Date.now();
// calculate Mass Index
function massIndex(highs, lows) {
const nineEMA = ema(9, highs, lows);
const twentySixEMA = ema(26, highs, lows);
const diff = nineEMA.map((val, i) => val - twentySixEMA[i]);
const ratio = diff.map((val, i) => val / twentySixEMA[i]);
const emaRatio = ema(9, ratio, ratio);
const sum = emaRatio.reduce((acc, val) => acc + val, 0);
return 25 / sum;
}
// calculate Klinger Oscillator
function klingerOscillator() {
const volumeEMA = ema(34, gb.data.candlesVolume, gb.data.candlesVolume);
const moneyFlow = gb.data.candlesClose.map((val, i) => val * gb.data.candlesVolume[i]);
const cmf = moneyFlow.map((val, i) => {
if (i === 0) return 0;
const prevClose = gb.data.candlesClose[i - 1];
const prevVolume = gb.data.candlesVolume[i - 1];
const moneyFlowMultiplier = (val > (prevClose * prevVolume)) ? 1 : (val < (prevClose * prevVolume)) ? -1 : 0;
return moneyFlowMultiplier * gb.data.candlesVolume[i];
});
const cmfEMA = ema(13, cmf, cmf);
const klinger = cmfEMA.map((val, i) => val - volumeEMA[i]);
return klinger;
}
// calculate exponential moving average
function ema(period, highs, lows) {
const emaArr = [];
const multiplier = 2 / (period + 1);
let ema = highs[0];
for (let i = 1; i < highs.length; i++) {
ema = (highs[i] - ema) * multiplier + ema;
emaArr.push(ema);
}
return emaArr;
}
if (enoughTimePassed) {
// calculate indicators
const massIndexValue = massIndex(gb.data.candlesHigh, gb.data.candlesLow);
const klingerOscillatorValue = klingerOscillator();
// log indicators
console.log("Mass Index:", massIndexValue);
console.log("Klinger Oscillator:", klingerOscillatorValue[klingerOscillatorValue.length - 1]);
// set buy and sell conditions
const buyConditions = massIndexValue < 26 && klingerOscillatorValue[klingerOscillatorValue.length - 1] > 0 && !gb.data.gotBag;
const sellConditions = massIndexValue > 27 && klingerOscillatorValue[klingerOscillatorValue.length - 1] < 0 && gb.data.gotBag;
// fire orders when conditions are met
if (buyConditions) {
const buyAmount = parseFloat(gb.data.pairLedger.whatstrat.TRADING_LIMIT) / gb.data.bid;
gb.method.buyMarket(buyAmount, gb.data.pairName);
setTimestamp();
console.log("Buy order placed");
} else if (sellConditions) {
gb.method.sellMarket(gb.data.quoteBalance, gb.data.pairName);
setTimestamp();
console.log("Sell order placed");
}
}
// end of strategy code
// Code is machine generated, review it and run in simulator mode first