Cainer Trend

Since we have the cainer arrows is anyone willing to make the cainer trend that goes with it that’s on TOS?

Looks like the Cainer Arrow indicator got removed in the last update, Might be a bug.

Here is the js code for the cainer arrows:

const predef = require("./tools/predef");
const meta = require("./tools/meta");
const medianPrice = require(’./tools/medianPrice’);

const p = require("./tools/plotting");
const SMA = require("./tools/SMA");
const EMA = require("./tools/EMA");
const MMA = require("./tools/MMA");
const MovingHigh = require("./tools/MovingHigh");
const MovingLow = require("./tools/MovingLow");
const trueRange = require("./tools/trueRange");

class CainerArrows {
init() {
//EMA Code
this.emaSlow = EMA(this.props.emaLength);
this.smaVol = SMA(30);
this.slow = 0;
this.volavg = 0;

    //Kama Code
    this.erLength = this.props.efficiencyRatioLength;
    this.fastSf = 2.0 / (this.props.fastLength + 1);
    this.slowSf = 2.0 / (this.props.slowLength + 1);
    this.lastKama = undefined;
    
    //Choose what price type we're using
    switch (this.props.priceType) {
        case 'high':
            this.getPriceVal = (d) => d.high();
            break;
        case 'low':
            this.getPriceVal = (d) => d.low();
            break;
        case 'open':
            this.getPriceVal = (d) => d.open();
            break;
        case 'hl2':
            this.getPriceVal = (d) => medianPrice(d);
            break;
        case 'close':
        default:
            this.getPriceVal = (d) => d.close();
    }
    
    //Plot Code
    this.atrMovingAverage = MMA(100);
}
//Kama Code
sumDifferences(data) {
    let result = 0;
    for (let i = 1; i < data.length; i++) {
        result += Math.abs(this.getPriceVal(data[i]) - this.getPriceVal(data[i-1]));
    }
    return result;
}

map(d, i, history) {
    if (!history.prior()) {
        return;
    }
   
    //Plot Code
    const atr = this.atrMovingAverage(trueRange(d, history.prior()));
    
    //KAMA Code
    if (history.back(this.erLength) === undefined) {
        return;
    }
    
    const direction = this.getPriceVal(d) - this.getPriceVal(history.back(this.erLength));
    const vol = this.sumDifferences(history.data.slice(i - this.erLength, i + 1));
    const er = vol !== 0 ? Math.abs(direction / vol) : 0.000001;
    const ct = Math.pow((er * (this.fastSf - this.slowSf)) + this.slowSf, 2);

    if (this.lastKama === undefined) {
        this.lastKama = this.getPriceVal(history.prior());
    }
    
    const result = this.lastKama + (ct * (this.getPriceVal(d) - this.lastKama));
    
    
    //Setup price variables
    const close = d.close();
    const v = d.value();
    const vt = d.volume();
    

    const prior = history.prior();
    
    //Cainer Code. Buy = close > kama and > 34 EMA and previous close not true, Sell opposite
    const c1 = (prior.close() < this.lastKama) || (prior.close() < this.slow) || ((prior.volume() / this.volavg) <= 0.15);
    const c2 = (prior.close() > this.lastKama) || (prior.close() > this.slow) || ((prior.volume() / this.volavg) <= 0.15);
    
    this.slow = this.emaSlow(v);
    this.volavg = this.smaVol(vt);
    
    const c3 = (close > result) && (close > this.slow) && ((vt / this.volavg) > 0.15);
    const c4 = (close < result) && (close < this.slow) && ((vt / this.volavg) > 0.15);
    
    
    this.lastKama = result;
    
    const buy = c1 && c3;
    const sell = c2 && c4;
    
    return {
        buy: buy ? d.low() : undefined,
        sell: sell ? d.high() : undefined,
        atr: atr
    };
}
filter(d, i) {
    return i > Math.max(this.props.fastLength, this.props.slowLength, this.erLength);
}

}
function customPlotter(canvas, indicatorInstance, history) {

for(let i = 1; i < history.data.length; i++) {
    const item = history.get(i);
    
    if (item.buy !== undefined || item.sell !== undefined) {
        
        const props = indicatorInstance.props;
        const prior = history.get(i - 1);
        
        const center = p.x.get(item);
        const left = p.x.between(p.x.get(prior), center, -0.5);
        const right = p.x.between(p.x.get(prior), center, 2.5);
        const height = item.atr / 5;
        
        let point = 0;
        let base = 0;
        let color = 0;
        
        if (item.buy !== undefined) {
            point = item.buy - height * 1.5;
            base = point - height;
            color = props.upColor;
        }
        
        if (item.sell !== undefined) {
            point = item.sell + height * 1.5;
            base = point + height;
            color = props.downColor;
        }

        
        // The triangle path
        const path = p.createPath();
        path.moveTo(left, base);
        path.lineTo(center, point);
        path.lineTo(right, base);
        path.lineTo(left, base);
        path.lineTo(center, point);
        
        // Draw the perimeter
        canvas.drawPath(
            path.end(),
            {
                color: color,
                width: 3,
                opacity: props.opacity / 100.0
            });       

        canvas.drawLine(
            p.offset(center, base - (point - base)),
            p.offset(center, base),
            {
                color: color,
                relativeWidth: 1,
                opacity: props.opacity / 100.0
            });
    }
}

}

module.exports = {
name: “CainerArrows”,
description: “CainerArrows”,
calculator: CainerArrows,
inputType: meta.InputType.BARS,
params: {
priceType: predef.paramSpecs.enum({
high: ‘High’,
low: ‘Low’,
open: ‘Open’,
close: ‘Close’,
hl2: ‘(H+L)/2’
}, ‘close’),
emaLength: predef.paramSpecs.period(34),
fastLength: predef.paramSpecs.period(2),
slowLength: predef.paramSpecs.period(30),
efficiencyRatioLength: predef.paramSpecs.period(10),
upColor: predef.paramSpecs.color(“green”),
downColor: predef.paramSpecs.color(“red”),
opacity: predef.paramSpecs.number(100, 1, 0)
},
plotter: [
predef.plotters.custom(customPlotter)
],
tags: [“Caine”]
};