Values not updating on time frame change for custom indicator with graphics module

I have a custom indicator that uses some values like the standard deviation of close and other calculated values.

I have a version of this that loads in a new window which doesn’t use graphics module and outputs fine. When you change time frames (1D/5m/etc) for that one, you get expected results, where values like standard deviation and others update as expected.

However on a more complicated version of it that uses the graphics module and outputs the values on the last bar, it does not update when you change time frames. So if you go from the daily bar to a 5 minute bar, the values will still reflect those of the daily bar, where the less complicated one will have the correct values corresponding to the current time frame.

Currently the only way to get it to update after changing times seems to be to re-save the indicator code or remove and re-add the indicator through the UI.

Uploading examples of daily and hourly time frame with both indicators shown. Source code below.

Daily chart, both indicators (one in new area, one as overlay) match:

If you change to the hourly, the values above and their plots still correspond to the daily bar, even though the chart has changed to hourly:

  • Is there something I am missing to force it to update when the time frame is changed?

Here is the source of the overlay/graphics module version, which has the issue:

const predef = require("./tools/predef");
const meta = require("./tools/meta");
const MovingHigh = require("./tools/MovingHigh");
const MovingLow = require("./tools/MovingLow");
const MMA = require("./tools/MMA");
const StdDev = require("./tools/StdDev");
const trueRange = require("./tools/trueRange");
const g = require("./tools/graphics");
const du = g.du;
const op = g.op;
const px = g.px;

const barScapeLines = function (openValue, description, value, color, textSize, decimals) {
    
    const startPosition = 40;
    const length = 20;
    const padding = 10;
    
    return {
        tag: 'Container',
        key: description+'Container',
        children: [
            {
                 tag: 'LineSegments',
                 key: 'lines',
                 lines: [
                     {
                         tag: 'Line',
                         a: {
                             x: op(du(0), '+', px(startPosition)),
                             y: du(openValue+value),
                         },
                         b: {
                             x: op(du(0), '+', px(startPosition+length)),
                             y: du(openValue+value)
                         },
                         infiniteStart: false,
                         infiniteEnd: false,
                         
                     },
                     {
                         tag: 'Line',
                         a: {
                             x: op(du(0), '+', px(startPosition)),
                             y: du(openValue-value),
                         },
                         b: {
                             x: op(du(0), '+', px(startPosition+length)),
                             y: du(openValue-value)
                         },
                         infiniteStart: false,
                         infiniteEnd: false,
                         
                     }
                 ],
                 lineStyle: {
                     lineWidth: 2,
                     color: color
                 }
            },
            {
                tag: "Text",
                key: description+"H",
                text: `${description} ${value.toFixed(decimals)}`,
                point: {
                    x: op(du(0), '+', px(startPosition+length+padding)),
                    y: op(du(openValue+value), '+', px(0))
                },
                style: { fontSize: textSize, fontWeight: "bold", fill: color},
                textAlignment: "rightMiddle"
            },
            {
                tag: "Text",
                key: description+"L",
                text: `${description} ${value.toFixed(decimals)}`,
                point: {
                    x: op(du(0), '+', px(startPosition+length+padding)),
                    y: op(du(openValue-value), '+', px(0))
                },
                style: { fontSize: textSize, fontWeight: "bold", fill: color},
                textAlignment: "rightMiddle"
            },
        ]
    }

};

class BarScape {
    init() {
        this.pullbackAverage = MMA(this.props.barScapePeriod);
        this.pullbackHigh = MovingHigh(this.props.barScapePeriod);
        this.bodyAverage = MMA(this.props.barScapePeriod);
        this.bodyHigh = MovingHigh(this.props.barScapePeriod);
        this.stdDev = StdDev(this.props.barScapePeriod);
    }

    map(d, i, history) {
        
        const open = d.open();
        const close = d.close();
        const high = d.high();
        const low = d.low();
        
        const up = close > open ? true : false;
        const down = close < open ? true : false;
        const top = close > open ? close : open;
        const bottom = close > open ? open : close;
        const body = top-bottom;
        const pullback = up ? bottom-low : high-top;

        const averageBody = this.bodyAverage(body);
        const highBody = this.bodyHigh(body);
        const averagePullback = this.pullbackAverage(pullback);
        const highPullback = this.pullbackHigh(pullback);
        const standardDev = this.stdDev(close);

        // display settings
        const decimals = this.props.decimals;
        const textSize = this.props.textSize;
        const highPullbackColor = this.props.highPullbackColor;
        const avgPullbackColor = this.props.avgPullbackColor;
        const highBodyColor = this.props.highBodyColor;
        const avgBodyColor = this.props.avgBodyColor;
        const stdDevColor = this.props.stdDevColor;

        return {
            
          graphics: d.isLast() && {
                items: [
                    barScapeLines (open, "OPEN", 0, "#BBBBBB", textSize, decimals),
                    barScapeLines (open, "STDEV", standardDev, stdDevColor, textSize, decimals),
                    barScapeLines (open, "PB_AVG", averagePullback, avgPullbackColor, textSize, decimals),
                    barScapeLines (open, "PB_HIGH", highPullback, highPullbackColor, textSize, decimals),
                    barScapeLines (open, "B_AVG", averageBody, avgBodyColor, textSize, decimals),
                    barScapeLines (open, "B_HIGH", highBody, highBodyColor, textSize, decimals),
                ]
            }
        
        };

        
    }

    filter(d,i) {
        return i > this.props.barScapePeriod;
    }
}


module.exports = {
    name: "BarScape",
    title: "BarScape",
    description: "BarScape",
    calculator: BarScape,
    params: {
        barScapePeriod: predef.paramSpecs.period(20),
        decimals: predef.paramSpecs.number(7),
        textSize: predef.paramSpecs.number(11),
        stdDevColor: predef.paramSpecs.color("#fc03df"),
        avgBodyColor: predef.paramSpecs.color("gray"),
        highBodyColor: predef.paramSpecs.color("green"),
        avgPullbackColor: predef.paramSpecs.color("orange"),
        highPullbackColor: predef.paramSpecs.color("red")
    },
    inputType: meta.InputType.BARS,
    plots: {

    },
    plotter: [

    ],
    schemeStyles: {

    },
    tags: ["Brian"],

};

One update:

It seems the horizontal placement of this doesn’t really work right either. In the code editor view that I have set up using du(0) places it at the last bar more or less (with the padding), but if I use that code in the normal chart view it places it somewhere far off to the left on the chart. However, if I change that to pass i in from the map function, and then use du(i) instead, then the horizontal placement has strange behavior for any time frame but the first one pretty much. Changing from daily to 5m for example kicks it far off to the right off the screen.

I am evidently missing something here, or just doing it wrong.

Can you please try these two things - add global: true to the body of the Container object (it will still save old renders even with d.isLast(), global ensures a single instance), and also try changing the key 'lines' to something else - I was doing some debugging the other day and realized that native lines use the key 'lines'. Not sure if it will mess up graphics or not, but key should be unique for re-render purposes. 'barscape-lines' would suffice to test this theory.

Thank you for the ideas.

Here’s the updated code with those suggestions added. I used the description in that key as I had the other places. Still has the same behavior. And the horizontal placement does get wonky at times too, though I can’t really figure out what determines when/how the horizontal placement issues happen. It isn’t as consistent as I thought, even in testing this I got it off to the left some or off to the right some.

const predef = require("./tools/predef");
const meta = require("./tools/meta");
const MovingHigh = require("./tools/MovingHigh");
const MovingLow = require("./tools/MovingLow");
const MMA = require("./tools/MMA");
const StdDev = require("./tools/StdDev");
const trueRange = require("./tools/trueRange");
const g = require("./tools/graphics");
const du = g.du;
const op = g.op;
const px = g.px;

const barScapeLines = function (openValue, description, value, color, textSize, decimals) {
    
    const startPosition = 40;
    const length = 20;
    const padding = 10;
    
    return {
        tag: 'Container',
        key: description+'Container',
        global: true,
        children: [
            {
                 tag: 'LineSegments',
                 key: description+'-barscape-lines',
                 lines: [
                     {
                         tag: 'Line',
                         a: {
                             x: op(du(0), '+', px(startPosition)),
                             y: du(openValue+value),
                         },
                         b: {
                             x: op(du(0), '+', px(startPosition+length)),
                             y: du(openValue+value)
                         },
                         infiniteStart: false,
                         infiniteEnd: false,
                         
                     },
                     {
                         tag: 'Line',
                         a: {
                             x: op(du(0), '+', px(startPosition)),
                             y: du(openValue-value),
                         },
                         b: {
                             x: op(du(0), '+', px(startPosition+length)),
                             y: du(openValue-value)
                         },
                         infiniteStart: false,
                         infiniteEnd: false,
                         
                     }
                 ],
                 lineStyle: {
                     lineWidth: 2,
                     color: color
                 }
            },
            {
                tag: "Text",
                key: description+"H",
                text: `${description} ${value.toFixed(decimals)}`,
                point: {
                    x: op(du(0), '+', px(startPosition+length+padding)),
                    y: op(du(openValue+value), '+', px(0))
                },
                style: { fontSize: textSize, fontWeight: "bold", fill: color},
                textAlignment: "rightMiddle"
            },
            {
                tag: "Text",
                key: description+"L",
                text: `${description} ${value.toFixed(decimals)}`,
                point: {
                    x: op(du(0), '+', px(startPosition+length+padding)),
                    y: op(du(openValue-value), '+', px(0))
                },
                style: { fontSize: textSize, fontWeight: "bold", fill: color},
                textAlignment: "rightMiddle"
            },
        ]
    }

};

class BarScape {
    init() {
        this.pullbackAverage = MMA(this.props.barScapePeriod);
        this.pullbackHigh = MovingHigh(this.props.barScapePeriod);
        this.bodyAverage = MMA(this.props.barScapePeriod);
        this.bodyHigh = MovingHigh(this.props.barScapePeriod);
        this.stdDev = StdDev(this.props.barScapePeriod);
    }

    map(d, i, history) {
        
        const open = d.open();
        const close = d.close();
        const high = d.high();
        const low = d.low();
        
        const up = close > open ? true : false;
        const down = close < open ? true : false;
        const top = close > open ? close : open;
        const bottom = close > open ? open : close;
        const body = top-bottom;
        const pullback = up ? bottom-low : high-top;

        const averageBody = this.bodyAverage(body);
        const highBody = this.bodyHigh(body);
        const averagePullback = this.pullbackAverage(pullback);
        const highPullback = this.pullbackHigh(pullback);
        const standardDev = this.stdDev(close);

        // display settings
        const decimals = this.props.decimals;
        const textSize = this.props.textSize;
        const highPullbackColor = this.props.highPullbackColor;
        const avgPullbackColor = this.props.avgPullbackColor;
        const highBodyColor = this.props.highBodyColor;
        const avgBodyColor = this.props.avgBodyColor;
        const stdDevColor = this.props.stdDevColor;

        return {
            
          graphics: d.isLast() && {
                items: [
                    //barScapeLines (open, "OPEN", 0, "#BBBBBB", textSize, decimals),
                    barScapeLines (open, "STDEV", standardDev, stdDevColor, textSize, decimals),
                    barScapeLines (open, "PB_AVG", averagePullback, avgPullbackColor, textSize, decimals),
                    barScapeLines (open, "PB_HIGH", highPullback, highPullbackColor, textSize, decimals),
                    barScapeLines (open, "B_AVG", averageBody, avgBodyColor, textSize, decimals),
                    barScapeLines (open, "B_HIGH", highBody, highBodyColor, textSize, decimals),
                ]
            }
        
        };

        
    }

    filter(d,i) {
        return i > this.props.barScapePeriod;
    }
}


module.exports = {
    name: "BarScape",
    title: "BarScape",
    description: "BarScape",
    calculator: BarScape,
    params: {
        barScapePeriod: predef.paramSpecs.period(20),
        decimals: predef.paramSpecs.number(7),
        textSize: predef.paramSpecs.number(11),
        stdDevColor: predef.paramSpecs.color("#fc03df"),
        avgBodyColor: predef.paramSpecs.color("gray"),
        highBodyColor: predef.paramSpecs.color("green"),
        avgPullbackColor: predef.paramSpecs.color("orange"),
        highPullbackColor: predef.paramSpecs.color("red")
    },
    inputType: meta.InputType.BARS,
    plots: {

    },
    plotter: [

    ],
    schemeStyles: {

    },
    tags: ["Brian"],

};

What I’ve done for now to work around the issue is make a version with just lines (multiline plot) so it’s like a channel and one with dots. Either case it is enough to have the same info laid out, though not as ideal as having the numbers and lines marked. If you (or anyone) happen to have ideas on how to make the other way work let me know. I think I have issues with understanding the horizontal layout too, and so that other path for now is just not going to work well. A table layout (similar to tradingview’s tables) would also work, and I have a version of that, but it has the horizontal placement issue on that one (not vertical, since in that case it doesn’t try to plot specific places vertically), and also suffers from the “not updating” thing, where the numbers don’t update with the time frame.

This isn’t the kind of solution you’re looking for, but just a heads up, I’ve found a quick way to “refresh” an indicator: add a second chart tab to the pane displaying your chart. When you want to refresh the indicator, switch to that other chart and then switch back to your original chart.

Thanks for that info. For now I’ve been using tradingview in the cases where I use this most, and hope to find out how to make the tradovate indicator behave better in time. The downside there is that there is no auto-break-even or trailing stops (not to mention no preset brackets), but, there are trade-offs everywhere I think. No perfect solutions.