Choppiness Index and the Detrended Price Oscillator strategy
This strategy uses the Choppiness Index and the Detrended Price Oscillator (DPO) to generate entry and exit signals.
The Choppiness Index measures trendiness on a 0–100 scale, with lower values indicating choppy markets and higher values indicating trending conditions. The DPO removes the longer-term trend to focus on short-term price swings.
In this example, the rules avoid buying in choppy conditions and avoid selling in strong trends by combining CI and DPO thresholds.
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 choppiness index
function choppinessIndex(period = 14) {
const high = gb.data.candlesHigh.slice(-period);
const low = gb.data.candlesLow.slice(-period);
const close = gb.data.candlesClose.slice(-period);
const atr = gb.data.atr.slice(-period);
const range = high.map((h, i) => h - low[i]);
const tr = range.map((r, i) => Math.max(high[i] - low[i], Math.abs(high[i] - close[i - 1]), Math.abs(low[i] - close[i - 1])));
const atrSum = atr.reduce((acc, val) => acc + val, 0);
const trSum = tr.reduce((acc, val) => acc + val, 0);
const ci = 100 * Math.log10(atrSum / trSum) / Math.log10(period);
return ci;
}
// calculate detrended price oscillator
function detrendedPriceOscillator(period = 14) {
const close = gb.data.candlesClose.slice(-period);
const sma = close.reduce((acc, val) => acc + val, 0) / period;
const detrended = close.map((c) => c - sma);
const dpo = detrended[Math.floor(period / 2)];
return dpo;
}
if (enoughTimePassed) {
// calculate indicators
const ci = choppinessIndex();
const dpo = detrendedPriceOscillator();
// log indicators
console.log(`Choppiness Index: ${ci}`);
console.log(`Detrended Price Oscillator: ${dpo}`);
// set buy and sell conditions
const buyConditions = ci < 38 && dpo > 0 && !gb.data.gotBag;
const sellConditions = ci > 62 && dpo < 0 && gb.data.gotBag;
// log trading conditions
console.log(`Buy Conditions: ${buyConditions}`);
console.log(`Sell Conditions: ${sellConditions}`);
// 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();
} else if (sellConditions) {
gb.method.sellMarket(gb.data.quoteBalance, gb.data.pairName);
setTimestamp();
}
}
// Code is machine generated, review it and run in simulator mode first