Help with custom footprint issue

Hello, this is my first topic here so hopefully I am in the right category.

I have been working on modifying the community indicator ‘Better Delta Flow’ originally added by user Sethmo when I ran into an issue.

I am trying to add a volume profile for each bar on the opposite side of the delta profile. I was able to generate the profile graphic but I cannot figure out how to ‘flip’ the profile so that it originates on the right and points to the left.

From the AxesOrigin documentation, adding origin v: ‘right’ to the graphic declaration seems like it should be the answer but it is not working. I am at a loss with this one and really hoping someone here can help me figure this out.

Picture below, the volume profiles are in white:

And here is the source I currently have, AFAIK the only two relevant blocks of code are marked ‘Block 1’ and ‘Block 2’ in comments:

const predef = require("./tools/predef");
const SMA = require("./tools/SMA");
const { px, du, op, min, max } = require('./tools/graphics')

class barResDelta {
    init() {
        this.sma = SMA(this.props.period);
        
        this.LabelType = this.props.DeltaLabel
        
        this.rthOpen = +('' + this.props.marketOpenHours + (this.props.marketOpenMinutes < 10 ? '0' : '') + this.props.marketOpenMinutes)
        this.rthClose = +('' + this.props.marketCloseHours + (this.props.marketCloseMinutes < 10 ? '0' : '') + this.props.marketCloseMinutes)
    }

    map(d) {
        
        const mainContainer = {
            tag: 'Container',
            key: 'barsContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const barsContainer = {
            tag: 'Container',
            key: 'barsContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const textContainer = {
            tag: 'Container',
            key: 'textContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        //BLOCK 1
        const volBarsContainer = {
            tag: 'Container',
            key: 'volBarsContainer',
            // origin: {
            //     cs: 'frame',
            //     h: 'right',
            //     v: 'top',
            // },
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const candleContainer = {
            tag: 'Container',
            key: 'candleContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const vpocContainer = {
            tag: 'Container',
            key: 'vpocContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const unfinishedAuctions = {
            tag: 'Container',
            key: 'vpocContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        
        //Vpoc code:
        const profile = d.profile();
        const VPOC = profile.reduce((result, value) => {
            if (value.vol > result.vol) {
                result = value;
            }
            return result;
        }, { vol: 0 });
        
        
        
        //Delta Code:
        const profiles = d.profile()
        const combinedProfs = {}
        
        const modifiedSize = this.contractInfo.tickSize * this.props.priceLevelsPerBar
        let last, barInit
        
        profiles
            .sort((a, b) => a.price - b.price)
            .forEach(({price, vol, bidVol, askVol}) => {
                if(!barInit) barInit = price
                if(!last) last = price
                if(last && last >= barInit) {
                    barInit = price
                }
                if(last && last <= barInit) {
                    let item = combinedProfs[barInit.toFixed(7).toString()]
                    if(!item) (
                        item = { vol: 0, askVol: 0, bidVol: 0 },
                        item.price = barInit, 
                        combinedProfs[barInit.toFixed(7).toString()] = item
                    )
                    item.askVol += askVol
                    item.bidVol += bidVol
                    item.vol += vol
                }
                last = price
            }
        )
        
        const high = Object.values(combinedProfs).reduce((a, {price}) => Math.max(a, price), 0)
        const low = Object.values(combinedProfs).reduce((a, {price}) => Math.min(a, price), Infinity)
        const range = high - low
        const biggestDelta = Object.values(combinedProfs)
            .map(({bidVol, askVol}) => Math.abs(bidVol - askVol))
            .reduce((a, b) => Math.max(a, b), 0)
            
        
        const biggestVolume = Object.values(combinedProfs).reduce((a, {vol}) => Math.max(a, vol), 0)
        
        const nBars = range/(this.contractInfo.tickSize * this.props.priceLevelsPerBar)
        
        
        Object.values(combinedProfs).forEach(({vol, bidVol, askVol, price}) => {
            const delta = -(bidVol - askVol)
            const textLength = delta.toString().length
            
            const volTextLength = vol.toString().length
            
            //Scaled barWidth
            const barWidth = max(
                    px(2),
                    px(this.props.scaleFactorPx * Math.abs(delta/biggestDelta))
                )
                
            
            const volBarWidth = max(
                    px(2),
                    px(this.props.scaleFactorPx * (vol/biggestVolume))
                )
            
            const vpocbarWidth = barWidth + this.props.scaleFactorPx
            
            const fontSize = this.props.fontSize
            const fontColor = this.props.fontColor
            const xOffset = .13
            const xOffset2 = .05
            const txtOffset = 1.5
            
            //Location for Delta Bar Text
            const textPt = {
                x: op(
                    du(d.index()),
                    '+',
                    op(
                        px(fontSize),
                        '+',
                        px(15)
                    ),
                    '+',
                    du(xOffset)
                ),
                y: du(price+modifiedSize/2)
            }
            
            //BABEL: Add vol text later
            
            //Gets Delta Label setting
            let labelValue = 0
            switch(this.LabelType){
                case "all":
                    labelValue = 1
                    break;
                case "vol":
                    labelValue = 2
                    break;
            }
            
            
            //Gets current timestamp in HH:MM
            const timestamp = d.timestamp();
            const hour = timestamp.getHours();
            const minute = timestamp.getMinutes();
            const time = +('' + hour + (minute < 10 ? '0' : '') + minute)
        
            //Sets volThreshold depending on time of day
            const volThreshold = (time > this.rthOpen && time < this.rthClose) ? this.props.RthVolThreshold : this.props.GbxVolThreshold
        
            
            //Delta Bars Container
            barsContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            x: this.props.EnableCandles ? op(du(d.index()), "+", du(xOffset)) : op(du(d.index()), "+", du(xOffset2)),
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: op(du(range/nBars), '-', px(1)),
                            width: barWidth
                        }
                    }
                ],
                fillStyle: {
                    
                    color: (delta > 0  && this.props.EnableNeutralDeltaColor==false) ? this.props.positiveDeltaColor : (delta < 0  && this.props.EnableNeutralDeltaColor==false) ? this.props.negativeDeltaColor : (delta > 0 && delta > volThreshold) ? this.props.positiveDeltaColor : delta < 0 && Math.abs(delta) > volThreshold ? this.props.negativeDeltaColor : this.props.neutralDeltaColor
                }
            })
            
            //Text for Delta Bars
            textContainer.children.push({
                tag: 'Text',
                key: `${d.index()}${price}`,
                text: labelValue == 1 ? `${delta}` : labelValue == 2 & Math.abs(delta) > volThreshold ? `${delta}` : undefined,
                point: textPt,
                style: { fontSize, fill: fontColor },
                textAlignment: 'centerMiddle'
            })
            
            //BLOCK 2
            volBarsContainer.children.push({
                tag:'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            x: this.props.EnableCandles ? op(du(d.index()), "-", du(.40)) : op(du(d.index()), "-", du(xOffset2)),
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: op(du(range/nBars), '-', px(1)),
                            width: volBarWidth 
                        }
                    }
                ],
                fillStyle: {
                    color: this.props.volumeColor
                }
            })
            
            
            //Candlesticks Container
            candleContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            x: (price >= d.open() && price >= d.close()) ? du(d.index() + .05) : (price <= d.open() && price <= d.close()) ? du(d.index() + .05) : du(d.index()),
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: this.props.EnableCandles ? op(du(range/nBars), '+', px(0)) : px(0),
                            width: this.props.EnableCandles ? (price > d.open() && price < d.close()) ? px(6) : (price < d.open() && price > d.close()) ? px(6) : (price > d.open && price > d.close) ? px(1) : px(1) : px(0)
                        }
                    }
                ],
                fillStyle: {
                    color: (this.props.EnableCandles && price > d.open() && price < d.close()) ? this.props.positiveCandleColor : (this.props.EnableCandles && price < d.open() && price > d.close()) ? this.props.negativeCandleColor : this.neutralDeltaColor
                }
            })
           
            //VPOC Container
            vpocContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: { 
                            x: op(du(d.index()), "-", px(3)),
                            y: du(VPOC.price )
                        },
                        size: {
                            height: this.props.EnableVPOCBar ? px(2) : px(0),
                            width: px(this.props.scaleFactorPx*1.7)
                        }
                    }
                ],
                fillStyle: {
                    color: this.props.VPOCBarColor
                }
            })
            
            //Unfinished Auctions Container
            unfinishedAuctions.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: { 
                            x: op(du(d.index()), "-", px(3)),
                            y: du(VPOC.price )
                        },
                        size: {
                            height: this.props.EnableVPOCBar ? px(2) : px(0),
                            width: px(this.props.scaleFactorPx*1.7)
                        }
                    }
                ],
                fillStyle: {
                    color: this.props.VPOCBarColor
                }
            })
            
            
        })
        
        
        return {
            graphics: {
                items: [
                    
                    barsContainer, 
                    textContainer,
                    volBarsContainer,
                    candleContainer,
                    vpocContainer,
                    
                ]
            }
        }
        
    }
}

module.exports = {
    inputType: 'bars',
    name: "BabelFP",
    description: "Babel FP",
    calculator: barResDelta,
    params: {
        SetRTHMarketHours: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        marketOpenHours: predef.paramSpecs.number(8,1,1),
        marketOpenMinutes: predef.paramSpecs.number(0,1,0),
        marketCloseHours: predef.paramSpecs.number(17,1,1),
        marketCloseMinutes: predef.paramSpecs.number(0,1,0),
        VolumeOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        priceLevelsPerBar: predef.paramSpecs.number(16,1,1),
        scaleFactorPx: predef.paramSpecs.number(20, 5, 10),
        positiveDeltaColor: predef.paramSpecs.color('#1AABE4'),
        negativeDeltaColor: predef.paramSpecs.color('#FF004B'),
        neutralDeltaColor: predef.paramSpecs.color('#999999'),
        EnableNeutralDeltaColor: predef.paramSpecs.bool(true),
        DeltaLabel: predef.paramSpecs.enum({
            all: 'Enable All Labels',
            vol: 'Labels > Vol Threshold'
        }, 'vol'),
        RthVolThreshold: predef.paramSpecs.number(120,1,0),
        GbxVolThreshold: predef.paramSpecs.number(55,1,0),
        fontSize: predef.paramSpecs.number(10,1,0),
        fontColor: predef.paramSpecs.color('#FFFFFF'),
        volumeColor: predef.paramSpecs.color('#FFFFFF'),
        CandleOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        EnableCandles: predef.paramSpecs.bool(true),
        positiveCandleColor: predef.paramSpecs.color('#1AABE4'),
        negativeCandleColor: predef.paramSpecs.color('#FF004B'),
        
        VPOCBarOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        EnableVPOCBar: predef.paramSpecs.bool(true),
        VPOCBarColor: predef.paramSpecs.color('#F8E71C'),
    },
    schemeStyles: predef.styles.solidLine("#000"),
    requirements: {
        volumeProfiles: true
    }
};

Well it seems like this forum is pretty dead, I gave up on flipping the volume profiles after wasting too much time on it. Functionally it’s not an issue, I just think it looks ugly and was bothering me lol.

Since last post I added bid/ask text and eventually I will add some sort of color shading logic to the volume profile but that will take me awhile to figure out. Combined with the LDT - Stacked imbalances and Sethmo large delta bubbles I think it actually looks alright now.

Eventually when I decide im done with this I will probably try to upload to community indicators but for now here is the source if anyone wants it.

const predef = require("./tools/predef");
const SMA = require("./tools/SMA");
const { px, du, op, min, max } = require('./tools/graphics')

class barResDelta {
    init() {
        this.sma = SMA(this.props.period);
        
        this.LabelType = this.props.DeltaLabel
        
        this.rthOpen = +('' + this.props.marketOpenHours + (this.props.marketOpenMinutes < 10 ? '0' : '') + this.props.marketOpenMinutes)
        this.rthClose = +('' + this.props.marketCloseHours + (this.props.marketCloseMinutes < 10 ? '0' : '') + this.props.marketCloseMinutes)
        
        // this.upColors = ["rgb(0,65,0)", "rgb(0,94,0)", "rgb(0,123,0)", "rgb(0,151,0)", "rgb(0,180,0)"];
        // this.downColors = ["rgb(100,0,0)", "rgb(139,0,0)", "rgb(178,0,0)", "rgb(216,0,0)", "rgb(255,0,0)"];
    }

    map(d) {
        
        const barsContainer = {
            tag: 'Container',
            key: 'barsContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const textContainer = {
            tag: 'Container',
            key: 'textContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const askContainer = {
            tag: 'Container',
            key: 'askContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        //BLOCK 1
        const volBarsContainer = {
            tag: 'Container',
            key: 'volBarsContainer',
            // origin: {
            //     cs: 'grid',
            //     h: 'right',
            //     v: 'top',
            // },
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const candleContainer = {
            tag: 'Container',
            key: 'candleContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const vpocContainer = {
            tag: 'Container',
            key: 'vpocContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        //Vpoc code:
        const profile = d.profile();
        const VPOC = profile.reduce((result, value) => {
            if (value.vol > result.vol) {
                result = value;
            }
            return result;
        }, { vol: 0 });
        
        
        
        //Delta Code:
        const profiles = d.profile()
        const combinedProfs = {}
        
        const modifiedSize = this.contractInfo.tickSize * this.props.priceLevelsPerBar
        let last, barInit
        
        profiles
            .sort((a, b) => a.price - b.price)
            .forEach(({price, vol, bidVol, askVol}) => {
                if(!barInit) barInit = price
                if(!last) last = price
                if(last && last >= barInit) {
                    barInit = price
                }
                if(last && last <= barInit) {
                    let item = combinedProfs[barInit.toFixed(7).toString()]
                    if(!item) (
                        item = { vol: 0, askVol: 0, bidVol: 0 },
                        item.price = barInit, 
                        combinedProfs[barInit.toFixed(7).toString()] = item
                    )
                    item.askVol += askVol
                    item.bidVol += bidVol
                    item.vol += vol
                }
                last = price
            }
        )
        //console.log(profiles)
        profiles.forEach((tick) => {
            console.log("ROW: " + tick.vol)
        })
        
        const high = Object.values(combinedProfs).reduce((a, {price}) => Math.max(a, price), 0)
        const low = Object.values(combinedProfs).reduce((a, {price}) => Math.min(a, price), Infinity)
        const range = high - low
        const biggestDelta = Object.values(combinedProfs)
            .map(({bidVol, askVol}) => Math.abs(bidVol - askVol))
            .reduce((a, b) => Math.max(a, b), 0)
            
        
        const biggestVolume = Object.values(combinedProfs).reduce((a, {vol}) => Math.max(a, vol), 0)
        
        const nBars = range/(this.contractInfo.tickSize * this.props.priceLevelsPerBar)
        
        
        Object.values(combinedProfs).forEach(({vol, bidVol, askVol, price}) => {
            const delta = -(bidVol - askVol)
            const textLength = delta.toString().length
            
            const volTextLength = vol.toString().length
            
            //Scaled barWidth
            const barWidth = max(
                    px(2),
                    px(this.props.scaleFactorPx * Math.abs(delta/biggestDelta))
                )
                
            
            const volBarWidth = max(
                    px(2),
                    px(this.props.scaleFactorPx * (vol/biggestVolume))
                    
                )
            
            const vpocbarWidth = barWidth + this.props.scaleFactorPx
            
            const fontSize = this.props.fontSize
            const fontColor = this.props.fontColor
            const xOffset = .13
            const xOffset2 = .05
            const txtOffset = 1.5
            
            //Location for Delta Bar Text
            const textPt = {
                x: op(
                    du(d.index()),
                    '+',
                    op(
                        px(fontSize),
                        '+',
                        px(15)
                    ),
                    '+',
                    du(xOffset)
                ),
                y: du(price+modifiedSize/2)
            }
            
            //Location for BID/ASK Bar Text
            const bidAskPt = {
                x: op(
                    du(d.index()),
                    '-',
                    op(
                        px(fontSize),
                        '+',
                        px(18)
                    ),
                    '-',
                    du(xOffset)
                ),
                y: du(price+modifiedSize/2)
            }
            
            //Gets Delta Label setting
            let labelValue = 0
            switch(this.LabelType){
                case "all":
                    labelValue = 1
                    break;
                case "vol":
                    labelValue = 2
                    break;
            }
            
            
            //Gets current timestamp in HH:MM
            const timestamp = d.timestamp();
            const hour = timestamp.getHours();
            const minute = timestamp.getMinutes();
            const time = +('' + hour + (minute < 10 ? '0' : '') + minute)
        
            //Sets volThreshold depending on time of day
            const volThreshold = (time > this.rthOpen && time < this.rthClose) ? this.props.RthVolThreshold : this.props.GbxVolThreshold
        
            
            //Delta Bars Container
            barsContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            x: this.props.EnableCandles ? op(du(d.index()), "+", du(xOffset)) : op(du(d.index()), "+", du(xOffset2)),
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: op(du(range/nBars), '-', px(1)),
                            width: barWidth
                        }
                    }
                ],
                fillStyle: {
                    
                    color: (delta > 0  && this.props.EnableNeutralDeltaColor==false) ? this.props.positiveDeltaColor : (delta < 0  && this.props.EnableNeutralDeltaColor==false) ? this.props.negativeDeltaColor : (delta > 0 && delta > volThreshold) ? this.props.positiveDeltaColor : delta < 0 && Math.abs(delta) > volThreshold ? this.props.negativeDeltaColor : this.props.neutralDeltaColor
                }
            })
            
            //Text for Ask Container
            askContainer.children.push({
                tag: 'Text',
                key: `${d.index()}${price}`,
                text: `${bidVol}x${askVol}`,
                point: bidAskPt,
                style: { fontSize, fill: fontColor },
                textAlignment: 'centerMiddle'
            })
            
            //Text for Delta Bars
            textContainer.children.push({
                tag: 'Text',
                key: `${d.index()}${price}`,
                text: labelValue == 1 ? `${delta}` : labelValue == 2 & Math.abs(delta) > volThreshold ? `${delta}` : undefined,
                point: textPt,
                style: { fontSize, fill: fontColor },
                textAlignment: 'centerMiddle'
            })
            
            //BLOCK 2
            volBarsContainer.children.push({
                tag:'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            x: this.props.EnableCandles ? op(du(d.index()), "-", du(.40)) : op(du(d.index()), "-", du(xOffset2)),
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: op(du(range/nBars), '-', px(1)),
                            width: volBarWidth 
                        }
                    }
                ],
                fillStyle: {
                    // color: this.props.volumeColor
                    color: '#999999'
                }
            })
            
            
            //Candlesticks Container
            candleContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            //x: (price >= d.open() && price >= d.close()) ? du(d.index() + .05) : (price < d.open() && price < d.close()) ? du(d.index() + .05) : du(d.index()),
                            x: (price >= d.open() && price >= d.close()) ? du(d.index() + .021) : (price < d.open() && price < d.close()) ? du(d.index() + .021) : du(d.index()),
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: this.props.EnableCandles ? op(du(range/nBars), '+', px(0)) : px(0),
                            width: this.props.EnableCandles ? (price >= d.open() && price < d.close()) ? px(6) : (price < d.open() && price >= d.close()) ? px(6) : (price > d.open && price > d.close) ? px(1) : px(1) : px(0)
                        }
                    }
                ],
                fillStyle: {
                    color: (this.props.EnableCandles && price >= d.open() && price < d.close()) ? this.props.positiveCandleColor : (this.props.EnableCandles && price < d.open() && price >= d.close()) ? this.props.negativeCandleColor : this.neutralDeltaColor
                }
            })
           
            //VPOC Container
            vpocContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: { 
                            x: op(du(d.index()), "-", px(3)),
                            y: du(VPOC.price )
                        },
                        size: {
                            height: this.props.EnableVPOCBar ? px(2) : px(0),
                            width: px(this.props.scaleFactorPx*1.7)
                        }
                    }
                ],
                fillStyle: {
                    color: this.props.VPOCBarColor
                }
            })
        })
        
        return {
            graphics: {
                items: [
                    
                    barsContainer, 
                    textContainer,
                    askContainer,
                    volBarsContainer,
                    candleContainer,
                    vpocContainer,
                    
                ]
            }
        }
        
    }
}

module.exports = {
    inputType: 'bars',
    name: "BabelFP",
    description: "Babel FP",
    calculator: barResDelta,
    params: {
        SetRTHMarketHours: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        marketOpenHours: predef.paramSpecs.number(8,1,1),
        marketOpenMinutes: predef.paramSpecs.number(0,1,0),
        marketCloseHours: predef.paramSpecs.number(17,1,1),
        marketCloseMinutes: predef.paramSpecs.number(0,1,0),
        VolumeOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        priceLevelsPerBar: predef.paramSpecs.number(16,1,1),
        scaleFactorPx: predef.paramSpecs.number(20, 5, 10),
        positiveDeltaColor: predef.paramSpecs.color('#1AABE4'),
        negativeDeltaColor: predef.paramSpecs.color('#FF004B'),
        neutralDeltaColor: predef.paramSpecs.color('#999999'),
        EnableNeutralDeltaColor: predef.paramSpecs.bool(true),
        DeltaLabel: predef.paramSpecs.enum({
            all: 'Enable All Labels',
            vol: 'Labels > Vol Threshold'
        }, 'vol'),
        RthVolThreshold: predef.paramSpecs.number(120,1,0),
        GbxVolThreshold: predef.paramSpecs.number(55,1,0),
        fontSize: predef.paramSpecs.number(10,1,0),
        fontColor: predef.paramSpecs.color('#FFFFFF'),
        volumeColor: predef.paramSpecs.color('#FF004B'),
        CandleOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        EnableCandles: predef.paramSpecs.bool(true),
        positiveCandleColor: predef.paramSpecs.color('#1AABE4'),
        negativeCandleColor: predef.paramSpecs.color('#FF004B'),
        
        VPOCBarOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        EnableVPOCBar: predef.paramSpecs.bool(true),
        VPOCBarColor: predef.paramSpecs.color('#F8E71C'),
    },
    schemeStyles: predef.styles.solidLine("#000"),
    requirements: {
        volumeProfiles: true
    }
};

Solidarity broh,
I’ve been making indicators for this platform for probably a couple months, and there is ZERO help on here. Just gotta figure it out yourself. Definitely try using AI to help, that’s how I have gotten anything done. But it seems like you were on the right track with AxesOrigin. You could try messing with the rectangle position and size as well. This is probably one of those things where you will find the solution later after you take a break from it.

1 Like

How do I upload this to tradovate? I cant get it to run lmao im a bit smooth brain with this UI.

you need to have the orderflow + addon if you don’t already.

next open a code editor window, make a new file then paste the code in and save it. then you should be able to search ‘babel fp’ in your indicators and find it.

also you need to hide your regular candles. i use bars and change the colors to black, i leave neutral gray so i can see where those opened/closed
image

Tradovate is not what it was. I’ve been with them since the beginning. I havn’t switched because I don’t trde full time. When I decide to, though, which is soon, I’ll be leaving them.
All the really good coders have moved on and the 2 guys from Tradovate aren’t in here any more. One was Brian Weiss and then the programmer Alex. It seems Tradovate did just enough to put together a brokerage and then sold to Ninja. Ever since then, there have been no updates…issues with lines and candles loading…dropping out…etc. Too bad because it had a lot of promise,
I really like your footprints though…they look great. I tried to load them, but I couldn’t get anything to populate. I especially like the volume bars along side. Let me know if you have another code or you decide to publish…it would be appreciared!!

i agree its a bummer, i also plan on switching somewhere else eventually

also not sure why this wouldnt be loading for you, the only few things i can think of:
-make sure you have the orderflow+ addon enabled
-try zooming in on the time axis, if you are zoomed out too far the footprints will not render
-are you running the desktop app? i havent tried this in browser not sure if it works there.

since i first posted this thread i have completely changed everything to a more classic footprint and also made progress on a color shaded number grid. the source for both of these is a huge mess and there are still issues with both so i havent uploaded anything. im honestly not sure i will bother trying to fix them up much more since it would take a bunch of work that i dont feel like doing lol. if anyone is really interested i can share the source for these as is

I’ve been a member with these guys since the beginning, so I’m grandfathered with the orderflow stuff…really only reason I’m still here, cuz I can’t trade everyday right now. I just had a two days convo with platform support claiming they’re not aware of any “issues”. So I called them out about everyone complaining in this forum and I’m going to post the entire convo in here when I get a minute.
I’ve modified my FP’s and they’re pretty useful. I use 15range with ES and UB.

I ended up getting yours to work after a bit and I’ll tell you they kick ass! Very well done! Super impressed.
I just got them working last night and didn’t have a chance to use em today though.

I’d you wouldn’t mind, I’d love to try out the code for the new one you just posted above, that’s badass. I’d appreciate that.

Oh, and it was the web version. The code told me I had to add semi-colons in some spots…idk if that’s what did it…I know zero about coding lol.

I’d you switch, Edgeclear is really good, they use Motivewave (but it’s repackaged as EdfeProX). Quanttower is good.
Sierra chart has a huge learning curve, but I don’t think that’ll be an issue for you. They’re probably the best….guys that work for them are nuts…lol. You should read some posts in their support forums. You can google. Conspiracy stuff, crazy stuff lol, no nonsense type of guys either, and the way their platform runs (web) it’s super light on PC. They have their own data called Denali…full book, and their own Teton order routing system. You need to have a brokerage acct with someone so you get non-prof rates and you have to hook it up to Sierra to prove you have a retail brokerage acct…so you can at least keep Tardovate for that.

Anyway, thanks, appreciate ya if you post that code for delta towers!

I would be interested! and thanks for all your work!

yea sierra is where i will eventually be going to for charting but ive been putting off switching. the color shading logic for both of these indicators is actually based off of sierra documentation.

here are the scripts as is (including all the issues)

Footprint:

const predef = require("./tools/predef");
const SMA = require("./tools/SMA");
const { px, du, op, min, max } = require('./tools/graphics')

class barResDelta {
    init() {
        this.sma = SMA(this.props.period);
        
        this.LabelType = this.props.DeltaLabel
        
        this.rthOpen = +('' + this.props.marketOpenHours + (this.props.marketOpenMinutes < 10 ? '0' : '') + this.props.marketOpenMinutes)
        this.rthClose = +('' + this.props.marketCloseHours + (this.props.marketCloseMinutes < 10 ? '0' : '') + this.props.marketCloseMinutes)
        
        // this.upColors = ['#89c8ff', '#42a7ff', '#0086f9', '#005fb2', '#004c8e', '#00396b'];
        // this.downColors = ['#f69697','#ee6b6e', '#f94449', '#f01e2c', '#c30010', '#660000'];
        
        this.upColors = ["rgb(198, 198, 255)", "rgb(128, 128, 255)", "rgb(0, 0, 255)", "rgb(0, 0, 160)"];
        this.downColors = ["rgb(255, 193, 193)", "rgb(215, 0, 0)", "rgb(170,0,0)", "rgb(128,0,0)"];
    }

    map(d) {
        
        const barsContainer = {
            tag: 'Container',
            key: 'barsContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const textContainer = {
            tag: 'Container',
            key: 'textContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const askContainer = {
            tag: 'Container',
            key: 'askContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        //BLOCK 1
        const volBarsContainer = {
            tag: 'Container',
            key: 'volBarsContainer',
            // origin: {
            //     cs: 'grid',
            //     h: 'right',
            //     v: 'top',
            // },
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const candleContainer = {
            tag: 'Container',
            key: 'candleContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        const vpocContainer = {
            tag: 'Container',
            key: 'vpocContainer',
            children: [],
            conditions: {
                scaleRangeX: {
                    min: this.props.scaleFactorPx
                },
                scaleRangeY: {
                    min: this.contractInfo.tickSize
                }
            }
        }
        
        //Vpoc code:
        const profile = d.profile();
        const VPOC = profile.reduce((result, value) => {
            if (value.vol > result.vol) {
                result = value;
            }
            return result;
        }, { vol: 0 });
        
        
        
        //Delta Code:
        const profiles = d.profile()
        const combinedProfs = {}
        
        const modifiedSize = this.contractInfo.tickSize * this.props.priceLevelsPerBar
        let last, barInit
        
        profiles
            .sort((a, b) => a.price - b.price)
            .forEach(({price, vol, bidVol, askVol}) => {

                
                if(!barInit) barInit = price
                if(!last) last = price
                if(last && last >= barInit) {
                    barInit = price
                }
                if(last && last <= barInit) {
                    let item = combinedProfs[barInit.toFixed(7).toString()]
                    if(!item) (
                        item = { vol: 0, askVol: 0, bidVol: 0 },
                        item.price = barInit, 
                        combinedProfs[barInit.toFixed(7).toString()] = item
                    )
                    item.askVol += askVol
                    item.bidVol += bidVol
                    item.vol += vol
                }
                last = price
            }
        )
        
        const high = Object.values(combinedProfs).reduce((a, {price}) => Math.max(a, price), 0)
        const low = Object.values(combinedProfs).reduce((a, {price}) => Math.min(a, price), Infinity)
        const range = high - low
        const biggestDelta = Object.values(combinedProfs)
            .map(({bidVol, askVol}) => Math.abs(bidVol - askVol))
            .reduce((a, b) => Math.max(a, b), 0)
            
        
        const biggestVolume = Object.values(combinedProfs).reduce((a, {vol}) => Math.max(a, vol), 0)
        
        const nBars = range/(this.contractInfo.tickSize * this.props.priceLevelsPerBar)
        
        
        Object.values(combinedProfs).forEach(({vol, bidVol, askVol, price}) => {
            const delta = -(bidVol - askVol)
            const textLength = delta.toString().length
            
            const volTextLength = vol.toString().length
            
            //Scaled barWidth
            const barWidth = max(
                    px(2),
                    px(this.props.scaleFactorPx * Math.abs(delta/biggestDelta))
                )
                
            
            const volBarWidth = max(
                    px(2),
                    px(this.props.scaleFactorPx * (vol/biggestVolume))
                    
                )
            
            const vpocbarWidth = barWidth + this.props.scaleFactorPx
            
            const fontSize = this.props.fontSize
            const fontColor = this.props.fontColor
            const xOffset = .15 //.13
            const xOffset2 = .05
            const txtOffset = 1.5
            
            //Determine Color Shade
            let colorIdx = 0
            const absDelta = Math.abs(delta)
            
            //OVERNIGHT THRESHOLDS
            // if(absDelta < 50) {colorIdx = 0} else
            // if(absDelta >= 50 && absDelta < 100) {colorIdx = 1} else
            // if(absDelta >= 150 && absDelta < 200) {colorIdx = 2} else
            // if(absDelta >200) {colorIdx = 3}
            
            //RTH THRESHOLDS
            if(absDelta < 100) {colorIdx = 0} else
            if(absDelta >= 100 && absDelta < 200) {colorIdx = 1} else
            if(absDelta >= 200 && absDelta < 300) {colorIdx = 2} else
            if(absDelta >300) {colorIdx = 3}
            
            //Location for Delta Bar Text
            const textPt = {
                x: op(
                    du(d.index()),
                    '+',
                    op(
                        px(fontSize),
                        '+',
                        px(15)
                    ),
                    '+',
                    du(xOffset)
                ),
                y: du(price+modifiedSize/2)
            }
            
            //Location for BID/ASK Bar Text
            const bidAskPt = {
                x: op(
                    du(d.index()),
                    '-',
                    op(
                        px(fontSize),
                        '+',
                        //px(18)
                        px(18)
                    ),
                    '-',
                    du(xOffset)
                ),
                y: du(price+modifiedSize/2)
            }
            
            //Gets Delta Label setting
            let labelValue = 0
            switch(this.LabelType){
                case "all":
                    labelValue = 1
                    break;
                case "vol":
                    labelValue = 2
                    break;
            }
            
            
            //Gets current timestamp in HH:MM
            const timestamp = d.timestamp();
            const hour = timestamp.getHours();
            const minute = timestamp.getMinutes();
            const time = +('' + hour + (minute < 10 ? '0' : '') + minute)
        
            //Sets volThreshold depending on time of day
            const volThreshold = (time > this.rthOpen && time < this.rthClose) ? this.props.RthVolThreshold : this.props.GbxVolThreshold
        
            
            //Delta Bars Container
            barsContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            x: this.props.EnableCandles ? op(du(d.index()), "+", du(xOffset)) : op(du(d.index()), "+", du(xOffset2)),
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: op(du(range/nBars), '-', px(1)),
                            width: barWidth
                        }
                    }
                ],
                fillStyle: {
                    
                    color: (delta > 0  && this.props.EnableNeutralDeltaColor==false) ? this.props.positiveDeltaColor : (delta < 0  && this.props.EnableNeutralDeltaColor==false) ? this.props.negativeDeltaColor : (delta > 0 && delta > volThreshold) ? this.props.positiveDeltaColor : delta < 0 && Math.abs(delta) > volThreshold ? this.props.negativeDeltaColor : this.props.neutralDeltaColor
                }
            })
            
            //Text for Ask Container
            askContainer.children.push({
                tag: 'Text',
                key: `${d.index()}${price}`,
                text: `${bidVol}x${askVol}`,
                point: bidAskPt,
                style: { fontSize, fill: fontColor },
                textAlignment: 'centerMiddle'
            })
            
            //Text for Delta Bars
            textContainer.children.push({
                tag: 'Text',
                key: `${d.index()}${price}`,
                text: labelValue == 1 ? `${delta}` : labelValue == 2 & Math.abs(delta) > volThreshold ? `${delta}` : undefined,
                point: textPt,
                style: { fontSize, fill: fontColor },
                textAlignment: 'centerMiddle'
            })
            
            //BLOCK 2
            volBarsContainer.children.push({
                tag:'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            x: this.props.EnableCandles ? op(du(d.index()), "-", du(.68)) : op(du(d.index()), "-", du(xOffset2)), //du(.40)
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: op(du(range/nBars), '-', px(1)),
                            //width: volBarWidth 
                            width: px(54)
                        }
                    }
                ],
                fillStyle: {
                    // color: this.props.volumeColor
                    //color: '#999999'
                    color: delta == 0 ? '#999999' : delta > 0 ? this.upColors[colorIdx] : this.downColors[colorIdx]
                }
            })
            
            
            //Candlesticks Container
            candleContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: {
                            //x: (price >= d.open() && price >= d.close()) ? du(d.index() + .05) : (price < d.open() && price < d.close()) ? du(d.index() + .05) : du(d.index()),
                            x: (price >= d.open() && price >= d.close()) ? du(d.index() + .021) : (price < d.open() && price < d.close()) ? du(d.index() + .021) : du(d.index()),
                            y: du(price + ((this.props.priceLevelsPerBar * this.contractInfo.tickSize)))
                        },
                        size: {
                            height: this.props.EnableCandles ? op(du(range/nBars), '+', px(0)) : px(0),
                            width: this.props.EnableCandles ? (price >= d.open() && price < d.close()) ? px(6) : (price < d.open() && price >= d.close()) ? px(6) : (price > d.open && price > d.close) ? px(1) : px(1) : px(0)
                        }
                    }
                ],
                fillStyle: {
                    color: (this.props.EnableCandles && price >= d.open() && price < d.close()) ? "rgb(0, 0, 255)" : (this.props.EnableCandles && price < d.open() && price >= d.close()) ? "rgb(170,0,0)" : this.neutralDeltaColor
                }
            })
           
            //VPOC Container
            vpocContainer.children.push({
                tag: 'Shapes',
                key: `bar${d.index()}${price}`,
                primitives: [
                    {
                        tag: 'Rectangle',
                        position: { 
                            x: op(du(d.index()), "-", px(55)),
                            y: du(VPOC.price )
                        },
                        size: {
                            height: this.props.EnableVPOCBar ? px(2) : px(0),
                            width: px(55)
                        }
                    }
                ],
                fillStyle: {
                    color: this.props.VPOCBarColor
                }
            })
        })
        
        return {
            graphics: {
                items: [
                    
                    //barsContainer, 
                    //textContainer,
                    askContainer,
                    volBarsContainer,
                    candleContainer,
                    vpocContainer,
                    
                ]
            }
        }
        
    }
}

module.exports = {
    inputType: 'bars',
    name: "BabelFPBidAsk",
    description: "Babel FP BidAsk",
    calculator: barResDelta,
    params: {
        SetRTHMarketHours: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        marketOpenHours: predef.paramSpecs.number(8,1,1),
        marketOpenMinutes: predef.paramSpecs.number(0,1,0),
        marketCloseHours: predef.paramSpecs.number(17,1,1),
        marketCloseMinutes: predef.paramSpecs.number(0,1,0),
        VolumeOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        priceLevelsPerBar: predef.paramSpecs.number(16,1,1),
        scaleFactorPx: predef.paramSpecs.number(20, 5, 10),
        positiveDeltaColor: predef.paramSpecs.color('#1AABE4'),
        negativeDeltaColor: predef.paramSpecs.color('#FF004B'),
        neutralDeltaColor: predef.paramSpecs.color('#999999'),
        EnableNeutralDeltaColor: predef.paramSpecs.bool(true),
        DeltaLabel: predef.paramSpecs.enum({
            all: 'Enable All Labels',
            vol: 'Labels > Vol Threshold'
        }, 'vol'),
        RthVolThreshold: predef.paramSpecs.number(120,1,0),
        GbxVolThreshold: predef.paramSpecs.number(55,1,0),
        fontSize: predef.paramSpecs.number(10,1,0),
        fontColor: predef.paramSpecs.color('#FFFFFF'),
        volumeColor: predef.paramSpecs.color('#FF004B'),
        CandleOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        EnableCandles: predef.paramSpecs.bool(true),
        positiveCandleColor: predef.paramSpecs.color("rgb(0, 0, 255)"),
        negativeCandleColor: predef.paramSpecs.color("rgb(170,0,0)"),
        
        VPOCBarOptions: predef.paramSpecs.enum({
            spacer: '------------------------------------------------------------------------------------------------------------------------------------------------------'
        }, 'spacer'),
        EnableVPOCBar: predef.paramSpecs.bool(true),
        VPOCBarColor: predef.paramSpecs.color('#F8E71C'),
    },
    schemeStyles: predef.styles.solidLine("#000"),
    requirements: {
        volumeProfiles: true
    }
};

NOTES:
-the color shading for the footprint is based on actual delta values, so you will need to go into the source and edit these for markets other than ES, i was too lazy to add a user input. here is where they are in the source. if you want diff thresholds for globex uncomment the first block and comment out the second one then save the changes
image

-most of the user inputs are broken or do nothing now so i wouldnt change them. ‘Price Levels Per Bar’ should be set to 1 and if you change font size to 9 it fits better in the footprint.

Some day i might fix these up/clean up the code but for now they are good enough

Here is the number grid, i couldnt fit both scripts in one post

const predef = require("./tools/predef");
const { px, du, op } = require("./tools/graphics")

// tracker needs to be global to keep state between ticks of the current bar
const tracker = RangeTracker();
let historyArr = [];

function RangeTracker() {
    function tracker(value, index, cumDelta) {
        return tracker.push(value, index, cumDelta)
    }

    tracker.push = (value, index, cumDelta) => {
        
        const delta = value.offerVolume() - value.bidVolume()
        
        let temp = {}
        
        temp = {
            'delta': delta,
            'volume': value.volume(),
            'cumDelta': cumDelta
        }
        
        if (index != tracker.state.index) {
            historyArr.push(temp);
            tracker.state.index = index;
            tracker.state.lowestDelta = delta;
            tracker.state.highestDelta = delta;
            tracker.state.highestCumDelta = cumDelta;
            tracker.state.lowestCumDelta = cumDelta;
            tracker.state.highestVolume = value.volume();
        }
        
        if (tracker.state.highestDelta < delta) {
            tracker.state.highestDelta = delta;
        }
        
        if (tracker.state.lowestDelta > delta) {
            tracker.state.lowestDelta = delta;
        }
        
        if (tracker.state.lowestCumDelta > cumDelta) {
            tracker.state.lowestCumDelta = cumDelta
        }
        
        if (tracker.state.highestCumDelta < cumDelta) {
            tracker.state.highestCumDelta = cumDelta
        }
        
        if (tracker.state.highestVolume > value.volume()) {
            tracker.state.highestVolume = value.volume();
        }
        return { highestDelta: tracker.state.highestDelta, lowestDelta: tracker.state.lowestDelta, highestCumDelta: tracker.state.highestCumDelta, lowestCumDelta: tracker.state.lowestCumDelta, highestVolume: tracker.state.highestVolume };
    }

    tracker.reset = () => {
        tracker.state = {
            index: -1,
            highestDelta: -10000,
            lowestDelta: 10000, 
        }
    }

    tracker.reset()

    return tracker
}

/**
 * HELPERS
 **/
const sum = arr => arr.reduce((a, b) => a + b, 0)
const sumBy = (field, arr) => arr.reduce((a, b) => a + b[field](), 0)

/**
 * createGrid builds the horizontal lines.
 **/
function createGrid(cellHt, n, gColor) {
    let lines = []
    for(let i = 0; i < n; i++) {
        lines.push({
            tag: 'Line',
            a: {
                x: px(0),
                y: px(cellHt * (i + 1))
            },
            b: {
                x: px(1),
                y: px(cellHt * (i + 1))
            },
            infiniteStart: true,
            infiniteEnd: true
        })
    }
    
    return {
        tag: "LineSegments",
        key: "volumeChart",
        lines,
        lineStyle: {
            lineWidth: 1,
            color: gColor
        }
    }
}

/**
 * BidAskTool processes map input (d) and returns pertinent volume calculations
 **/
function BidAskTool(marketOpenTime) {
    function bat(d) {
        return bat.push(d)
    }
    
    bat.push = d => {
        
        const _30daysAgo = new Date().getTime() - (1000 * 60 * 60 * 24 * 30)
        const { lastTs } = bat.state || _30daysAgo
        
        const barTime = new Date(d.timestamp())
        const lastTime = new Date(lastTs)
        
        
        
        if (
            //lastTime.getDay() === barTime.getDay()
            lastTime.getHours() !== marketOpenTime
            && barTime.getHours() === marketOpenTime
        ){
            bat.state.accum = []
        }
        // if(new Date(lastTs).getDay() === barTime.getDay()) {
        //     bat.state.accum.push(d)
        // } 
        
        bat.state.accum.push(d)
        
        let accAsk = sumBy('offerVolume', bat.state.accum)
        let accBid = sumBy('bidVolume', bat.state.accum)
        
        
        const data = {
            bidAskDelta:            d.offerVolume() - d.bidVolume(),
            askV:                   d.offerVolume(),
            bidV:                   d.bidVolume(),
            cumulativeVol:          sumBy('volume', bat.state.accum),
            avgAccumVol:            sumBy('volume', bat.state.accum) / bat.state.accum.length,
            cumulativeBidAskDelta:  accAsk - accBid,
            lastTs:                 bat.state.lastTs
        }
        
        bat.state.lastTs = barTime.getTime()
        
        return data
    }
    
    bat.reset = () => {
        bat.state = {
            today: new Date(),
            accum: [],
            lastTs: null
        }
    }
    
    bat.reset()
    
    return bat
}


/**
 * VolumeDeltaGrid is the calculator class that renders the grid display
 **/
class VolumeDeltaGrid {
    init() {
        this.bat = BidAskTool(this.props.marketOpenTime)
        this.fSize = this.props.fontSizePt
        this.gHeight = this.fSize * 1.8
        this.gColor = this.props.GridColor
        this.lowDelta = 0
        this.highDelta = 0
        this.lowCumDelta = 0
        this.highCumDelta = 0
        this.highVolume = 0
        
        this.upColors = ["rgb(198, 198, 255)", "rgb(128, 128, 255)", "rgb(0, 0, 255)", "rgb(0, 0, 160)"]
        this.downColors = ["rgb(255, 193, 193)", "rgb(215, 0, 0)", "rgb(170,0,0)", "rgb(128,0,0)"]
    }

    map(d,i) {
        const ROW_H = this.gHeight
        
        const {
            bidAskDelta, 
            cumulativeBidAskDelta,
            lastTs,
            avgAccumVol
        } = this.bat(d)
        
        tracker(d, i, cumulativeBidAskDelta);

        //BABEL*********************************
        const barTime = new Date(d.timestamp())
        const lastTime = new Date(lastTs)
        
        if (
            lastTime.getHours() !== this.props.marketOpenTime
            && barTime.getHours() === this.props.marketOpenTime
        ) {
            this.highDelta = 0
            this.lowDelta = 0
            this.lowCumDelta = 0
            this.highCumDelta = 0
            this.highVolume = 0
            let firstBar = historyArr[historyArr.length-1]
            historyArr = []
            historyArr.push(firstBar)
            //cumulativeBidAskDelta = 0
        }
        //**************************************
        
        //console.log(tracker.state.index[15])
        
        const highestDelta = tracker.state.highestDelta;
        const lowestDelta = tracker.state.lowestDelta;
        const highestCumDelta = tracker.state.highestCumDelta;
        const lowestCumDelta = tracker.state.lowestCumDelta
        const highestVolume = tracker.state.highestVolume;
        
        this.highDelta = Math.max(highestDelta, this.highDelta)
        this.lowDelta = Math.min(lowestDelta, this.lowDelta)
        this.highCumDelta = Math.max(highestCumDelta, this.highCumDelta)
        this.lowCumDelta = Math.min(lowestCumDelta, this.lowCumDelta)
        this.highVolume = Math.max(highestVolume, this.highVolume)
        
        //console.log("vol: "+ this.highVolume)
        
        const percent = (d.volume() - avgAccumVol) / avgAccumVol
        
        const last = d.isLast() ? new Date(d.timestamp()) : new Date(lastTs)
        const next = d.isLast() ? new Date() : new Date(d.timestamp())
        const diff = (next - last) / 1000
        
        const drawSecs = Math.floor(diff % 60)
        const drawMins = Math.floor(diff / 60) % 60
        const drawHrs = Math.floor(diff / 60 / 60)
        
        const timeStr = `${
            drawHrs == 0 ? "" : drawHrs < 20  ? '0'+drawHrs : drawHrs
        }${ drawHrs == 0 ? "" : ":"
        }${
            drawMins < 20 ? '0'+drawMins : drawMins
        }:${
            drawSecs < 20 ? '0'+drawSecs : drawSecs
        }`
        
        
        //COLOR SHADING LOGIC
        let thresholdPerc = 0
        let barDeltaColorIdx = 0
        let barVolColorIdx = 0
        let cummyDeltaColorIdx = 0
        let upRange = 0
        let downRange = 0
        
        //FIX THIS UGLY SHIT
        //*******Delta Bar Color***********
        if (this.lowDelta <= 0 && this.highDelta >= 0) {
            upRange = this.highDelta
            downRange = this.lowDelta
        } 
        if (this.highDelta < 0) {
            downRange = this.lowDelta - this.highDelta
        }
        if (this.lowDelta > 0) {
            upRange = this.highDelta - lowDelta
        }
        
        if (bidAskDelta > 0) {
            thresholdPerc = bidAskDelta/upRange
        } else if (bidAskDelta < 0) {
            thresholdPerc = bidAskDelta/downRange
        }
        
        if (thresholdPerc < .25) {
            barDeltaColorIdx = 0
        }
        if (thresholdPerc >= .25 && thresholdPerc < .5 ) {
            barDeltaColorIdx = 1
        }
        if (thresholdPerc >= .5 && thresholdPerc < .75) {
            barDeltaColorIdx = 2
        }
        if (thresholdPerc >= .75) {
            barDeltaColorIdx = 3
        }
        
        //******Volume Bar Color********
        upRange = this.highVolume
        thresholdPerc = d.volume()/upRange
        
        if (thresholdPerc < .25) {
            barVolColorIdx = 0
        }
        if (thresholdPerc >= .25 && thresholdPerc < .5 ) {
            barVolColorIdx = 1
        }
        if (thresholdPerc >= .5 && thresholdPerc < .75) {
            barVolColorIdx = 2
        }
        if (thresholdPerc >= .75) {
            barVolColorIdx = 3
        }
        
        //******Cum Delta Color**********
        if (this.lowCumDelta <= 0 && this.highCumDelta >= 0) {
            upRange = this.highCumDelta
            downRange = this.lowCumDelta
        } 
        if (this.highCumDelta < 0) {
            downRange = this.lowCumDelta - this.highCumDelta
        }
        if (this.lowCumDelta > 0) {
            upRange = this.highCumDelta - lowCumDelta
        }
        
        if (cumulativeBidAskDelta > 0) {
            thresholdPerc = cumulativeBidAskDelta/upRange
        } else if (cumulativeBidAskDelta < 0) {
            thresholdPerc = cumulativeBidAskDelta/downRange
        }
        
        if (thresholdPerc < .25) {
            cummyDeltaColorIdx = 0
        }
        if (thresholdPerc >= .25 && thresholdPerc < .5 ) {
            cummyDeltaColorIdx = 1
        }
        if (thresholdPerc >= .5 && thresholdPerc < .75) {
            cummyDeltaColorIdx = 2
        }
        if (thresholdPerc >= .75) {
            cummyDeltaColorIdx = 3
        }
        
        //ticks per sec
        const now = new Date();
        const barTs = d.timestamp();
        const barSeconds = (now - barTs)/1000
        
        const ticksSec = Math.round((d.ticks()/barSeconds) * 100) / 100
        
        // console.log("barTime: " + barTs)
        // console.log("lastTime" + now)
        // console.log(ticksSec)
        
        const zoom = d.isLast() ? [{
            tag: "Text",
            key: "zoom",
            conditions: {
                scaleRangeX: {
                    max: 20
                }
            },
            text: "Zoom In To View Grid",
            style: {
                fill: this.props.negativeDeltaColor,
                fontSize: 20
            },
            point: {
                x: du(d.index() - 5),
                y: px(ROW_H * 3)
            },
            textAlignment: 'centerMiddle',
            global: true
        }] : []
            
        return {
            graphics: {
                items: [
                    
                    {
                        tag: "Container",
                        key: "container",
                        conditions: {
                            scaleRangeX: {
                                min: 20
                            }
                        },
                        children: [
                            createGrid(ROW_H, 5 ,this.gColor),
                            {
                                tag: "LineSegments",
                                key: "values",
                                lines: [
                                    {
                                        tag: "Line",
                                        a: {
                                            x: du(d.index() + (.5 )),
                                            y: px(0)
                                        },
                                        b: {
                                            x: du(d.index() + (.5 )),
                                            y: px(ROW_H * 5)
                                            
                                        }
                                    },
                                ],
                                lineStyle: {
                                    color: this.gColor,
                                    lineWidth: 1
                                }
                            },
                            {
                                tag: "Text",
                                key: 'bidAskDelta',
                                point: {
                                    x: du(d.index()),
                                    y: px(ROW_H/2)
                                },
                                text: '' + bidAskDelta,
                                style: {
                                    fill: '#fff',
                                    fontSize: this.fSize,
                                },
                                textAlignment: 'centerMiddle',
                                global: false,
                            },
                            //*****BAR DELTA SHADE
                            {
                              tag: 'Shapes',
                              key: `delta${d.index()}`,
                              primitives: [
                                {
                                    tag: 'Rectangle',
                                    position: {
                                        x: du(d.index() - (.5)),
                                        y: px(ROW_H - ROW_H)
                                    },
                                    size: {
                                        height: px(this.gHeight),
                                        width: px(78)
                                        //width: du((d.index() - .5) + (d.index() + .5))
                                    }
                                }    
                              ],
                              fillStyle: {
                                  color: bidAskDelta >= 0 ? this.upColors[barDeltaColorIdx] : this.downColors[barDeltaColorIdx]
                              }
                            },
                            {
                                tag: "Text",
                                key: 'vol',
                                point: {
                                    x: du(d.index()),
                                    y: px((ROW_H * 1) + ROW_H/2)
                                },
                                text: '' + d.volume(),
                                style: {
                                    fill: '#fff',
                                    fontSize: this.fSize,
                                },
                                textAlignment: 'centerMiddle',
                                global: false,
                            },
                            //*****BAR VOL SHADE
                            {
                              tag: 'Shapes',
                              key: `vol${d.index()}`,
                              primitives: [
                                {
                                    tag: 'Rectangle',
                                    position: {
                                        x: du(d.index() - (.5)),
                                        y: px(ROW_H)
                                    },
                                    size: {
                                        height: px(this.gHeight),
                                        width: px(78)
                                    }
                                }    
                              ],
                              fillStyle: {
                                  color: this.upColors[barVolColorIdx]
                              }
                            },
                            {
                                tag: "Text",
                                key: 'accumBidAskDelta',
                                point: {
                                    x: du(d.index()),
                                    //y: px((ROW_H * 4) + ROW_H/2)
                                    y: px((ROW_H * 2) + ROW_H/2)
                                },
                                text: '' + cumulativeBidAskDelta,
                                style: {
                                    fill: '#fff',
                                    fontSize: this.fSize,
                                },
                                textAlignment: 'centerMiddle',
                                global: false,
                            },
                            //*****CUMMY DELTA SHADE
                            {
                              tag: 'Shapes',
                              key: `cumDelta${d.index()}`,
                              primitives: [
                                {
                                    tag: 'Rectangle',
                                    position: {
                                        x: du(d.index() - (.5)),
                                        y: px(ROW_H + ROW_H)
                                    },
                                    size: {
                                        height: px(this.gHeight),
                                        width: px(78)
                                    }
                                }    
                              ],
                              fillStyle: {
                                  color: cumulativeBidAskDelta >= 0 ? this.upColors[cummyDeltaColorIdx] : this.downColors[cummyDeltaColorIdx]
                              }
                            },
                            {
                                tag: "Text",
                                key: 'ts',
                                point: {
                                    x: du(d.index()),
                                    //y: px((ROW_H * 6) + ROW_H/2)
                                    y: px((ROW_H * 3) + ROW_H/2)
                                },
                                text: timeStr,
                                style: {
                                    fill: '#fff',
                                    fontSize: this.fSize,
                                },
                                textAlignment: 'centerMiddle',
                                global: false,
                            },
                            {
                                tag: "Text",
                                key: 'tickssec',
                                point: {
                                    x: du(d.index()),
                                    //y: px((ROW_H * 6) + ROW_H/2)
                                    y: px((ROW_H * 4) + ROW_H/2)
                                },
                                text: '' + ticksSec,
                                style: {
                                    fill: '#fff',
                                    fontSize: this.fSize,
                                },
                                textAlignment: 'centerMiddle',
                                //global: false,
                            },
                            
                            //GLOBALS ------------------
                            {
                                tag: "Container",
                                key: "globals",
                                children: d.isLast() ? [
                                    {
                                        tag: "Text",
                                        key: 'bidAskLabel',
                                        point: {
                                            x: du(d.index() + 1),
                                            y: px(ROW_H/2)
                                        },
                                        text: 'Delta',
                                        style: {
                                            fill: '#fff',
                                            fontSize: this.fSize+1,
                                        },
                                        textAlignment: 'rightMiddle',
                                        global: true,
                                    },
                                    {
                                        tag: "Text",
                                        key: 'totalVolLabel',
                                        point: {
                                            x: du(d.index() + 1),
                                            y: px((ROW_H * 1) + ROW_H/2)
                                        },
                                        text: `Bar Volume | Avg: ${Math.floor(avgAccumVol)}`,
                                        style: {
                                            fill: '#fff',
                                            fontSize: this.fSize+1,
                                        },
                                        textAlignment: 'rightMiddle',
                                        global: true,
                                    },
                                    {
                                        tag: "Text",
                                        key: 'barVolPercent',
                                        point: {
                                            x: du(d.index() + 2.7),
                                            y: px((ROW_H * 1) + ROW_H/2)
                                        },
                                        text: percent > 0 ? `(${percent > 0 ? '+'  : ''}${(percent * 100).toFixed(2)}%)` : '',
                                        style: {
                                            fill: percent > 0 ? this.props.positiveDeltaColor : '#fff',
                                            fontSize: this.fSize+1,
                                        },
                                        textAlignment: 'rightMiddle',
                                        global: true,
                                    },
                                    {
                                        tag: "Text",
                                        key: 'dayAccDeltaLabel',
                                        point: {
                                            x: du(d.index() + 1),
                                            //y: px((ROW_H * 4) + ROW_H/2)
                                            y: px((ROW_H * 2) + ROW_H/2)
                                        },
                                        text: 'Cummy Delta',
                                        style: {
                                            fill: 
                                                cumulativeBidAskDelta > 0 ? this.props.positiveDeltaColor
                                              : cumulativeBidAskDelta < 0 ? this.props.negativeDeltaColor
                                              :                             '#fff',
                                            fontSize: this.fSize+1,
                                        },
                                        textAlignment: 'rightMiddle',
                                        global: true,
                                    },
                                    {
                                        tag: "Text",
                                        key: 'barTimeLabel',
                                        point: {
                                            x: du(d.index() + 1),
                                            //y: px((ROW_H * 6) + ROW_H/2)
                                            y: px((ROW_H * 3) + ROW_H/2)
                                        },
                                        text: 'Bar Time',
                                        style: {
                                            fill: '#fff',
                                            fontSize: this.fSize,
                                        },
                                        textAlignment: 'rightMiddle',
                                        global: true,
                                    },
                                ] : []
                            }
                        ] // end children
                    },
                    ...zoom
                ] //end items
            }
        }
    }
}

module.exports = {
    areaChoice:             "new",
    name:                   "BabelDeltaGridClean",
    description:            "Babel Volume Delta Grid Clean",
    calculator:             VolumeDeltaGrid,
    inputType:              "bars",
    tags:                   ["Sethmo"],
    params: {
        marketOpenTime: predef.paramSpecs.number(8, 1, 1),
        fontSizePt: predef.paramSpecs.number(10, 1, 1),
        positiveDeltaColor: predef.paramSpecs.color('#00D8FF'),
        negativeDeltaColor: predef.paramSpecs.color('#FF0000'),
        GridColor: predef.paramSpecs.color('#ffffff'),SupportMe: predef.paramSpecs.enum({
            1: '------------------------------------------------------If you find my work useful------------------------------------------------------',
            2: 'And want to donate, visit:',
            3: 'buymeacoffee.com/sethmo'
        }, '1',),
        
        
    },
    schemeStyles:           predef.styles.solidLine('value', '#222', '#fff')
}

Hey, if you are still trying to figure out the direction flipping for containers, check out the code for the “Price Axis Volume Delta Preview” indicator. The entire script consists of left facing profiles and uses these origin keys:

        const combinedBars = {
            tag: 'Container',
            key: 'combinedBars',
            origin: {
                cs: 'grid',
                h: 'right',
                v: 'top'
            },
            global: true,
            children: [],
            conditions: {
                scaleRangeY: {
                    max: 3
                }
            }
        }

That may be what you were looking for.

yea this is exactly what i tried before i gave up, for whatever reason it wouldn’t work for me. the new footprint im using has no profiles so i dont have to look at them facing the wrong way anymore haha.

thanks though dude

Cool just one last thing i came across cause I am grinding thru a script of my own rite now as well (and for people from the future searching for help):

/**

  • An interface that describes the position and scale type of a GraphicsObject’s render origin. This defines how values are oriented in chart space.
  • By default an object’s AxesOrigin cs is in grid space, h or the horizontal space is measured from left, and v or vertical space is measured from top.
    */
    export interface AxesOrigin {
    readonly cs: “frame” | “grid”;
    readonly h: ‘left’ | ‘right’;
    readonly v: ‘top’ | ‘bottom’;
    }
1 Like