├── indicators ├── .gitkeep ├── .DS_Store ├── aggr │ ├── cld.png │ ├── ctd.png │ ├── cvd.png │ ├── ema.png │ ├── mfi.png │ ├── rsi.png │ ├── shm.png │ ├── sma.png │ ├── macd.png │ ├── price.png │ ├── volume.png │ ├── vwap.png │ ├── cvd-perp.png │ ├── cvd-spot.png │ ├── ichimoku.png │ ├── all-markets.png │ ├── bb-ribbon.png │ ├── vwap-bands.png │ ├── liquidations.png │ ├── td-sequential.png │ ├── bollinger-bands.png │ ├── chandelier-exit.png │ ├── fibonacci-grid.png │ ├── volume-delta-avg.png │ ├── fibonacci-retracement.png │ ├── liquidation-heatmap.png │ ├── smoothed-vwap-bands.png │ ├── vwap-stdevmultiplier.png │ ├── liquidation-heatmap-light.png │ ├── ctd.json │ ├── cvd-perp.json │ ├── cvd-spot.json │ ├── cvd.json │ ├── cld.json │ ├── liquidations.json │ ├── ema.json │ ├── sma.json │ ├── ichimoku.json │ ├── bollinger-bands.json │ ├── price.json │ ├── vwap.json │ ├── macd.json │ ├── mfi.json │ ├── volume.json │ ├── bb-ribbon.json │ ├── all-markets.json │ ├── vwap-stdevmultiplier.json │ ├── smoothed-vwap-bands.json │ ├── rsi.json │ ├── vwap-bands.json │ ├── chandelier-exit.json │ ├── td-sequential.json │ ├── volume-delta-avg.json │ ├── fibonacci-retracement.json │ ├── fibonacci-grid.json │ ├── shm.json │ ├── liquidation-heatmap.json │ └── liquidation-heatmap-light.json ├── redzone.png ├── KeTan │ ├── obv.png │ ├── delta-divs.png │ ├── obv.json │ └── delta-divs.json ├── qwpto │ ├── pvsra.png │ └── pvsra.json ├── finchen │ ├── mondays.png │ ├── obv-spots.png │ ├── mondays.json │ └── obv-spots.json ├── Robsn77 │ ├── smoothheikinashi.png │ └── smoothheikinashi.json ├── the_syndrome │ ├── alternating-periods-backgrounds.png │ └── alternating-periods-backgrounds.json ├── chriselderxyz │ └── volume-delta.json └── redZone.json ├── .gitignore └── README.md /indicators/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | !*.json 4 | !*.png 5 | !*.md 6 | !.gitkeep -------------------------------------------------------------------------------- /indicators/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/.DS_Store -------------------------------------------------------------------------------- /indicators/aggr/cld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/cld.png -------------------------------------------------------------------------------- /indicators/aggr/ctd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/ctd.png -------------------------------------------------------------------------------- /indicators/aggr/cvd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/cvd.png -------------------------------------------------------------------------------- /indicators/aggr/ema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/ema.png -------------------------------------------------------------------------------- /indicators/aggr/mfi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/mfi.png -------------------------------------------------------------------------------- /indicators/aggr/rsi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/rsi.png -------------------------------------------------------------------------------- /indicators/aggr/shm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/shm.png -------------------------------------------------------------------------------- /indicators/aggr/sma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/sma.png -------------------------------------------------------------------------------- /indicators/redzone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/redzone.png -------------------------------------------------------------------------------- /indicators/KeTan/obv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/KeTan/obv.png -------------------------------------------------------------------------------- /indicators/aggr/macd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/macd.png -------------------------------------------------------------------------------- /indicators/aggr/price.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/price.png -------------------------------------------------------------------------------- /indicators/aggr/volume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/volume.png -------------------------------------------------------------------------------- /indicators/aggr/vwap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/vwap.png -------------------------------------------------------------------------------- /indicators/qwpto/pvsra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/qwpto/pvsra.png -------------------------------------------------------------------------------- /indicators/aggr/cvd-perp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/cvd-perp.png -------------------------------------------------------------------------------- /indicators/aggr/cvd-spot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/cvd-spot.png -------------------------------------------------------------------------------- /indicators/aggr/ichimoku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/ichimoku.png -------------------------------------------------------------------------------- /indicators/KeTan/delta-divs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/KeTan/delta-divs.png -------------------------------------------------------------------------------- /indicators/aggr/all-markets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/all-markets.png -------------------------------------------------------------------------------- /indicators/aggr/bb-ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/bb-ribbon.png -------------------------------------------------------------------------------- /indicators/aggr/vwap-bands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/vwap-bands.png -------------------------------------------------------------------------------- /indicators/finchen/mondays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/finchen/mondays.png -------------------------------------------------------------------------------- /indicators/aggr/liquidations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/liquidations.png -------------------------------------------------------------------------------- /indicators/aggr/td-sequential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/td-sequential.png -------------------------------------------------------------------------------- /indicators/finchen/obv-spots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/finchen/obv-spots.png -------------------------------------------------------------------------------- /indicators/aggr/bollinger-bands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/bollinger-bands.png -------------------------------------------------------------------------------- /indicators/aggr/chandelier-exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/chandelier-exit.png -------------------------------------------------------------------------------- /indicators/aggr/fibonacci-grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/fibonacci-grid.png -------------------------------------------------------------------------------- /indicators/aggr/volume-delta-avg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/volume-delta-avg.png -------------------------------------------------------------------------------- /indicators/Robsn77/smoothheikinashi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/Robsn77/smoothheikinashi.png -------------------------------------------------------------------------------- /indicators/aggr/fibonacci-retracement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/fibonacci-retracement.png -------------------------------------------------------------------------------- /indicators/aggr/liquidation-heatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/liquidation-heatmap.png -------------------------------------------------------------------------------- /indicators/aggr/smoothed-vwap-bands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/smoothed-vwap-bands.png -------------------------------------------------------------------------------- /indicators/aggr/vwap-stdevmultiplier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/vwap-stdevmultiplier.png -------------------------------------------------------------------------------- /indicators/aggr/liquidation-heatmap-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/aggr/liquidation-heatmap-light.png -------------------------------------------------------------------------------- /indicators/the_syndrome/alternating-periods-backgrounds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tucsky/aggr-lib/HEAD/indicators/the_syndrome/alternating-periods-backgrounds.png -------------------------------------------------------------------------------- /indicators/aggr/ctd.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:CTD", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "script": "line(cum(cbuy-csell))", 7 | "description": "Cumulative Trades Delta", 8 | "options": { 9 | "priceScaleId": "ctd", 10 | "lineStyle": 4, 11 | "lineWidth": 1 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /indicators/aggr/cvd-perp.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:CVD PERP", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "description": "CVD selected perp markets", 7 | "script": "buyVol = source(vbuy, type=perp)\nsellVol = source(vsell, type=perp)\nline(cum(buyVol - sellVol), title=PERP)", 8 | "options": { 9 | "priceFormat": { 10 | "type": "volume" 11 | }, 12 | "priceScaleId": "cvdperp", 13 | "color": "#42a5f5" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /indicators/aggr/cvd-spot.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:CVD SPOT", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "description": "CVD selected spot markets", 7 | "script": "buyVol = source(vbuy, type=spot)\nsellVol = source(vsell, type=spot)\nline(cum(buyVol - sellVol), title=SPOT)", 8 | "options": { 9 | "priceFormat": { 10 | "type": "volume" 11 | }, 12 | "priceScaleId": "cvdspot", 13 | "color": "#4caf50" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /indicators/aggr/cvd.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:CVD", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "name": "CVD", 7 | "description": "Cumulative Volume Delta", 8 | "script": "line(cum(vbuy - vsell))", 9 | "options": { 10 | "priceScaleId": "cvd", 11 | "priceFormat": { 12 | "type": "volume" 13 | }, 14 | "color": "#3BCA6D", 15 | "scaleMargins": { 16 | "top": 0.84, 17 | "bottom": 0.00 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /indicators/aggr/cld.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:CLD", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "script": "line(cum(lbuy-lsell))", 7 | "description": "Cumulative Liquidations Delta", 8 | "options": { 9 | "priceFormat": { 10 | "type": "volume" 11 | }, 12 | "color": "rgb(156,39,176)", 13 | "priceScaleId": "ctd", 14 | "lineWidth": 1, 15 | "scaleMargins": { 16 | "top": 0.75, 17 | "bottom": 0.17 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /indicators/KeTan/obv.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:OBV", 4 | "data": { 5 | "id": "obv", 6 | "name": "OBV", 7 | "options": { 8 | "priceScaleId": "_tfjysqee4eywg3a2", 9 | "scaleMargins": { 10 | "top": 0.1, 11 | "bottom": 0.2 12 | } 13 | }, 14 | "script": "sign = $price.close > $price.open ? 1 : -1\nvolume = vbuy + vsell\nobv = cum(sign * volume)\nplotline(obv, title=\"obv\")", 15 | "createdAt": 1704853083764, 16 | "updatedAt": 1704853103409, 17 | "displayName": "OBV", 18 | "author": "KeTan" 19 | } 20 | } -------------------------------------------------------------------------------- /indicators/aggr/liquidations.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:Liquidations", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "name": "Liquidations", 7 | "description": "Liquidations by side", 8 | "script": "histogram(lbuy, color=options.upColor)\nhistogram(-lsell, color=options.downColor)", 9 | "options": { 10 | "priceFormat": { 11 | "type": "volume" 12 | }, 13 | "priceScaleId": "volume_liquidations", 14 | "upColor": "rgb(255,76,243)", 15 | "downColor": "rgb(255,183,77)", 16 | "scaleMargins": { 17 | "top": 0.75, 18 | "bottom": 0.17 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## aggr-lib 2 | 3 | An open-source community-driven repository for sharing and enhancing indicators, panes, and workspaces for aggr.trade 4 | 5 | ### How to Use 6 | 7 | aggr.trade directly pull indicators and other resources from this repository. This means that new and updated indicators are made available on the site automatically ! 8 | 9 | #### Contributing through Github 10 | 1. **Fork and Create**: Simply fork this repository, add your indicator, pane, or workspace you want to share and ensure it functions well with the aggr.trade platform. 11 | 2. **Submit Your Work**: Once you're done, submit a pull request. Don't worry about making everything perfect – our community is here to help! 12 | -------------------------------------------------------------------------------- /indicators/aggr/ema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:EMA", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "name": "EMA {fastLength} {mediumLength} {slowLength}", 7 | "displayName": "EMA 50 100 200", 8 | "script": "line(ema($price.close, options.fastLength), color=options.fastColor)\nline(ema($price.close, options.mediumLength), color=options.mediumColor)\nline(ema($price.close, options.slowLength), color=options.slowColor)", 9 | "options": { 10 | "priceScaleId": "right", 11 | "fastLength": 50, 12 | "mediumLength": 100, 13 | "slowLength": 200, 14 | "fastColor": "#42a5f5", 15 | "mediumColor": "#64b5f6", 16 | "slowColor": "#90caf9" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /indicators/aggr/sma.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:SMA", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "name": "SMA {fastLength} {mediumLength} {slowLength}", 7 | "displayName": "SMA 50 100 200", 8 | "script": "line(sma($price.close, options.fastLength), color=options.fastColor)\nline(sma($price.close, options.mediumLength), color=options.mediumColor)\nline(sma($price.close, options.slowLength), color=options.slowColor)", 9 | "options": { 10 | "priceScaleId": "right", 11 | "fastLength": 50, 12 | "mediumLength": 100, 13 | "slowLength": 200, 14 | "fastColor": "#42a5f5", 15 | "mediumColor": "#64b5f6", 16 | "slowColor": "#90caf9" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /indicators/aggr/ichimoku.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:Ichimoku", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "script": "conversionLine=(lowest($price.low, options.conversionLineLength) + highest($price.high, options.conversionLineLength)) / 2\nbaseLine=(lowest($price.low, options.baseLineLength) + highest($price.high, options.baseLineLength)) / 2\nleadLineOne=(conversionLine + baseLine) / 2\nleadLineTwo=(lowest($price.low, options.laggingSpanTwoLength) + highest($price.high, options.laggingSpanTwoLength)) / 2\nplotcloudarea(leadLineTwo, leadLineOne, offset=options.displacement)", 7 | "options": { 8 | "baseLineLength": 26, 9 | "conversionLineLength": 9, 10 | "displacement": 24, 11 | "laggingSpanTwoLength": 52, 12 | "priceScaleId": "right" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /indicators/aggr/bollinger-bands.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:Bollinger Bands", 4 | "data": { 5 | "createdAt": 1623024000000, 6 | "script": "a = sma(Math.pow($price.close,2),options.length)\nb = Math.pow(sum($price.close,options.length),2)/Math.pow(options.length,2)\nstdev = Math.sqrt(a - b)\nsrc = $price.close\n\nbasis = sma($price.close, options.length)\ndev = options.mult * stdev\nupper = basis + dev\nlower = basis - dev\n\nline(basis, color=options.basisColor)\nplotcloudarea(lower, upper)", 7 | "options": { 8 | "priceScaleId": "right", 9 | "mult": 2, 10 | "length": 20, 11 | "lineStyle": 2, 12 | "basisColor": "rgba(129,199,132,0.5)", 13 | "positiveColor": "rgba(0,0,0,0.1)", 14 | "negativeColor": "rgba(0,0,0,0.1)", 15 | "lowerLineColor": "rgb(129,199,132)", 16 | "higherLineColor": "rgb(129,199,132)" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /indicators/aggr/price.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:Price", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "script": "var ohlc = options.useHeikinAshi ? \n avg_heikinashi(bar) : \n options.useGaps ? \n avg_ohlc_with_gaps(bar) : \n avg_ohlc(bar)\n\nplotcandlestick(ohlc)", 7 | "options": { 8 | "priceScaleId": "right", 9 | "priceFormat": { 10 | "auto": true, 11 | "precision": 1 12 | }, 13 | "priceLineVisible": true, 14 | "lastValueVisible": true, 15 | "borderVisible": true, 16 | "upColor": "rgb(59,202,109)", 17 | "downColor": "rgb(214,40,40)", 18 | "borderUpColor": "rgb(59,202,109)", 19 | "borderDownColor": "rgb(239,67,82)", 20 | "wickUpColor": "rgb(223,211,144)", 21 | "wickDownColor": "rgb(239,67,82)", 22 | "useGaps": false, 23 | "useHeikinAshi": false 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /indicators/aggr/vwap.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:VWAP", 4 | "data": { 5 | "createdAt": 1647173400001, 6 | "script": "var src = ($price.high + $price.low + $price.close)/3\nvar vol=vbuy+vsell\nvar sumVol = cum(vol)\nvar sumSrcVol = cum(src * vol)\nvar sumSrcSrcVol = cum(vol*Math.pow(src, 2))\nvar VWAP = sumSrcVol / sumVol \nvar variance = sumSrcSrcVol / sumVol - Math.pow(VWAP, 2)\nvar stDev = Math.sqrt(variance < 0 ? 0 : variance)\n\t\nline(VWAP)\ncloudarea(VWAP - stDev * options.stdevMultiplier, VWAP + stDev * options.stdevMultiplier)\n\nvar unixDays = bar.timestamp / 60 / 60 / 24 + 3\nif (unixDays % (options.sessionLengthInDays) === 0) {\n fns[0].state = {}\n fns[1].state = {}\n fns[2].state = {}\n}", 7 | "name": "VWAP", 8 | "options": { 9 | "priceScaleId": "right", 10 | "stdevMultiplier": 1, 11 | "color": "rgb(41,98,255)", 12 | "sessionLengthInDays": 7 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /indicators/aggr/macd.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:MACD", 4 | "data": { 5 | "createdAt": 1697138760000, 6 | "description": "A trend-following momentum indicator that shows the relationship between two exponential moving averages (EMAs) of a security's price", 7 | "options": { 8 | "priceScaleId": "macd", 9 | "MACDLength": 14, 10 | "scaleMargins": { 11 | "top": 0.53, 12 | "bottom": 0.1 13 | }, 14 | "visible": true, 15 | "negativeColor": "rgb(242,54,69)", 16 | "positiveColor": "rgba(76,175,80,0.91)" 17 | }, 18 | "script": "// source: https://www.investopedia.com/terms/m/macd.asp\n\nconst MACD = ema($price.close, 12) - ema($price.close, 26)\nconst signalLine = ema(MACD, 9)\n\nline(signalLine, color=red)\nline(MACD, color=blue)\n\nconst histoValue = (MACD - signalLine)\nhistoValue > 0 ? histogram(histoValue, color=green) : histogram(histoValue, color=red)" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /indicators/aggr/mfi.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:MFI", 4 | "data": { 5 | "id": "mfi", 6 | "name": "MFI", 7 | "options": { 8 | "priceScaleId": "mfi", 9 | "scaleMargins": { 10 | "top": 0.63, 11 | "bottom": 0.05 12 | }, 13 | "color": "rgb(255,229,0)", 14 | "thresholdColor": "rgba(255,255,255,0.25)" 15 | }, 16 | "script": "src = ($price.high + $price.low + $price.close) / 3\r\nvolume=vbuy+vsell\r\nchange=src-src[1]\r\nupper = sum(volume * (change <= 0 ? 0 : src), 14)\r\nlower = sum(volume * (change >= 0 ? 0 : src), 14)\r\nmf = !lower ? null : 100 - (100 / (1 + upper / lower))\r\nplotline(mf, color=options.color)\r\nplotline(80, color=options.thresholdColor, lineStyle=3)\r\nplotline(20, color=options.thresholdColor, lineStyle=3)", 17 | "createdAt": 1704851612924, 18 | "updatedAt": 1704851624642, 19 | "displayName": "MFI", 20 | "description": null, 21 | "author": "aggr" 22 | } 23 | } -------------------------------------------------------------------------------- /indicators/aggr/volume.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:Volume", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "name": "Volume", 7 | "description": "Volume + delta", 8 | "script": "if (upColor === 0) {\n if (options.showDelta) {\n upColor = options.upBgColor\n downColor = options.downBgColor\n } else {\n upColor = options.upColor\n downColor = options.downColor\n }\n}\n\nif (options.showDelta) {\n histogram({ time: time, value: Math.abs(vbuy-vsell), color: vbuy - vsell > 0 ? options.upColor : options.downColor})\n}\n\nhistogram({ time: time, value: vbuy + vsell, color: vbuy > vsell ? upColor : downColor })", 9 | "options": { 10 | "priceFormat": { 11 | "type": "volume" 12 | }, 13 | "upColor": "rgb(59,202,109)", 14 | "downColor": "rgb(235,30,47)", 15 | "priceScaleId": "volume", 16 | "scaleMargins": { 17 | "top": 0.84, 18 | "bottom": 0 19 | }, 20 | "showDelta": true, 21 | "upBgColor": "rgba(59,202,109,0.5)", 22 | "downBgColor": "rgba(235,30,47,0.5)" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /indicators/aggr/bb-ribbon.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:BB Ribbon", 4 | "data": { 5 | "createdAt": 1623024000000, 6 | "script": "src = sma($price.close, options.maLength)\na = sma(Math.pow(src,2),options.bbLength)\nb = Math.pow(sum(src,options.bbLength),2)/Math.pow(options.bbLength,2)\nstdev = Math.sqrt(a - b)\ndev = options.mult * stdev\nbasis = sma(src, options.bbLength)\nupper = basis + dev\nlower = basis - dev\nsrcTwo=($price.close + $price.low + $price.high) / 3\nemaSlow = ema(srcTwo, options.emaSlowLength)\nemaFast = ema(srcTwo, options.emaFastLength)\nplotcloudarea(lower, upper, positiveColor=options.slowRibbonColor, negativeColor=options.slowRibbonColor)\nplotcloudarea(emaSlow, emaFast)", 7 | "options": { 8 | "priceScaleId": "right", 9 | "maLength": 200, 10 | "bbLength": 50, 11 | "mult": 3, 12 | "emaSlowLength": 50, 13 | "emaFastLength": 20, 14 | "slowRibbonColor": "rgba(255,204,128,0.1)", 15 | "higherLineColor": "rgba(0,0,0,0)", 16 | "lowerLineColor": "rgba(0,0,0,0)", 17 | "negativeColor": "rgba(255,167,38,0.25)", 18 | "positiveColor": "rgba(255,167,38,0.25)" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /indicators/chriselderxyz/volume-delta.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "Volume Delta", 4 | "data": { 5 | "libraryId": "volume-delta", 6 | "displayName": "Volume Delta", 7 | "options": { 8 | "priceScaleId": "_9xmhdhmj2pf5zrgv", 9 | "scaleMargins": { 10 | "top": 0.1, 11 | "bottom": 0.2 12 | }, 13 | "exchange": 0, 14 | "type": 0, 15 | "chartTitle": "" 16 | }, 17 | "script": "// Options //\n\n// Filters\nexchange = option(default=null, type=exchange, rebuild=true)\ntype = option(default=null, type=list, options=[\"spot\", \"perp\"], rebuild=true)\n\n// Chart\nchartTitle = option(default=\"Delta\", type=text, rebuild=true)\nupColor = option(default=\"#42bda8\", type=color, rebuild=true)\ndownColor = option(default=\"#f77c80\", type=color, rebuild=true)\n\n// Get Filtered Sources //\nvar src = source(type=type, exchange=exchange)\n\n// Calculate Volume Delta //\nvar delta = 0\n\nfor (const [key, value] of Object.entries(src.sources)) {\n delta = delta + value.vbuy - value.vsell\n}\n\n// Get Color //\nvar chartColor = delta > 0 ? upColor : downColor\n\n// Plot Delta //\n histogram(\n {\n value: delta,\n time: time,\n color: chartColor\n },\n title = chartTitle\n)", 18 | "createdAt": 1729970010068, 19 | "updatedAt": 1729970046285, 20 | "presets": [], 21 | "author": "chriselderxyz" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /indicators/the_syndrome/alternating-periods-backgrounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:Alternating periods backgrounds", 4 | "data": { 5 | "id": "alternating-periods-backgrounds", 6 | "name": "Alternating periods backgrounds", 7 | "options": { 8 | "priceScaleId": "right", 9 | "barCount": null, 10 | "visible": true, 11 | "highlightColor": "rgba(255,255,255,0.05)", 12 | "baseLineVisible": false, 13 | "lastValueVisible": false, 14 | "priceLineVisible": false, 15 | "scaleMargins": { 16 | "top": 0.04, 17 | "bottom": 0.26 18 | } 19 | }, 20 | "script": "var barCount = options.barCount || 24;\nvar highlighted = (bar.length % (barCount * 2)) > barCount;\nif (highlighted) {\n brokenarea(\n $price.close - $price.close,\n $price.close + $price.close,\n color=(options.highlightColor || \"rgba(255, 255, 255, 0.05)\")\n );\n}", 21 | "createdAt": 1704843848569, 22 | "updatedAt": 1704843911852, 23 | "displayName": "Alternating periods backgrounds", 24 | "description": "Every X number of bars alternate the background color. For example if viewing 1min chart and barCount is 60 each hour the background color will change. It works best with subtle background colors and RGBA so the user can set the transparency and see through it. Author discord: the_syndrome", 25 | "author": "the_syndrome" 26 | } 27 | } -------------------------------------------------------------------------------- /indicators/aggr/all-markets.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:All markets", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "script": "line(BITFINEX:BTCUSD.close, color=#97c554, title=Bitfinex.spot)\nline(BINANCE:btcusdt.close, color=#f3ba2f, title=Binance.spot)\nline(OKEX:BTC-USDT.close, color=#5894f2, title=Okex.spot)\nline(KRAKEN:XBT/USD.close, color=#5841d8, title=Kraken.spot)\nline(COINBASE:BTC-USD.close, color=#0e70e2, title=Coinbase.spot)\nline(POLONIEX:BTC_USDT.close, color=#858b8f, title=Poloniex.spot)\nline(HUOBI:btcusdt.close, color=#2ca6e0, title=Huobi.spot)\nline(BITSTAMP:btcusd.close, color=#169f4a, title=Bitstamp.spot)\nline(BITMEX:XBTUSD.close, color=#595fb5, title=Bitmex.perp)\nline(BITFINEX:BTCF0:USTF0.close, color=#97c554, title=Bitfinex.perp)\nline(OKEX:BTC-USD-SWAP.close, color=#5894f2, title=Okex.perp)\nline(BINANCE_FUTURES:btcusdt.close, color=#f3ba2f, title=Binance.perp)\nline(BINANCE_FUTURES:btcusd_perp.close, color=#f3ba2f, title=Binance.perp)\nline(HUOBI:BTC-USD.close, color=#2ca6e0, title=Huobi.perp)\nline(KRAKEN:PI_XBTUSD.close, color=#5841d8, title=Kraken.perp)\nline(DERIBIT:BTC-PERPETUAL.close, color=#2dae9a, title=Deribit.perp)\nline(FTX:BTC-PERP.close, color=#02a6c2, title=Ftx.perp)\nline(BYBIT:BTCUSD.close, color=#f9b600, title=Bybit.perp)", 7 | "description": "Multiple markets at once", 8 | "options": { 9 | "priceScaleId": "right" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /indicators/aggr/vwap-stdevmultiplier.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:VWAP {stdevMultiplier}", 4 | "data": { 5 | "id": "vwap-stdevmultiplier", 6 | "name": "VWAP {stdevMultiplier}", 7 | "options": { 8 | "priceScaleId": "right", 9 | "stdevMultiplier": 1.618, 10 | "color": "rgb(242,54,69)", 11 | "sessionLengthInDays": 1, 12 | "visible": true, 13 | "positiveColor": "rgba(0,0,0,0.05)", 14 | "negativeColor": "rgba(0,0,0,0.05)", 15 | "lowerLineColor": "rgb(255,255,255)", 16 | "higherLineColor": "rgb(255,255,255)", 17 | "scaleMargins": { 18 | "top": 0.08, 19 | "bottom": 0.26 20 | }, 21 | "higherLineWidth": 1, 22 | "lineWidth": 2 23 | }, 24 | "script": "var src = ($price.high + $price.low + $price.close)/3\nvar vol=vbuy+vsell\nvar sumVol = cum(vol)\nvar sumSrcVol = cum(src * vol)\nvar sumSrcSrcVol = cum(vol*Math.pow(src, 2))\nvar VWAP = sumSrcVol / sumVol \nvar variance = sumSrcSrcVol / sumVol - Math.pow(VWAP, 2)\nvar stDev = Math.sqrt(variance < 0 ? 0 : variance)\n\t\nline(VWAP)\ncloudarea(VWAP - stDev * options.stdevMultiplier, VWAP + stDev * options.stdevMultiplier)\nvar unixDays = bar.timestamp / 60 / 60 / 24 + 3\nif (unixDays % (options.sessionLengthInDays) === 0) {\n fns[0].state = {}\n fns[1].state = {}\n fns[2].state = {}\n}", 25 | "createdAt": 1671228046733, 26 | "updatedAt": 1704890651196, 27 | "displayName": "VWAP 1.618", 28 | "description": null, 29 | "author": "aggr" 30 | } 31 | } -------------------------------------------------------------------------------- /indicators/Robsn77/smoothheikinashi.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicators:SmoothHeikinAshi", 4 | "data": { 5 | "options": { 6 | "priceScaleId": "left", 7 | "priceFormat": { 8 | "auto": true, 9 | "precision": 1 10 | }, 11 | "priceLineVisible": true, 12 | "lastValueVisible": true, 13 | "borderVisible": false, 14 | "upColor": "rgb(59,202,109)", 15 | "downColor": "rgb(214,40,40)", 16 | "borderUpColor": "rgb(59,202,109)", 17 | "borderDownColor": "rgb(239,67,82)", 18 | "wickUpColor": "rgb(223,211,144)", 19 | "wickDownColor": "rgb(239,67,82)", 20 | "useGaps": true, 21 | "useHeikinAshi": true, 22 | "visible": true, 23 | "scaleMargins": { 24 | "top": 0.38, 25 | "bottom": 0.22 26 | }, 27 | "baseLineVisible": true, 28 | "smoothingLength": 100 29 | }, 30 | "script": "// Optionen für die Glättung\nsmoothingLength = option(type=range, min=1, max=100, default=14, step=1); // Länge des gleitenden Durchschnitts\n\n// Original-Heikin-Ashi-Daten berechnen\nha = avg_heikinashi(bar)\n//line($price.close,colour=transparent)\n// Glätten der Heikin-Ashi-Werte\nsmoothedOpen = ema(ha.open, smoothingLength)\nsmoothedHigh = ema(ha.high, smoothingLength)\nsmoothedLow = ema(ha.low, smoothingLength)\nsmoothedClose = ema(ha.close, smoothingLength)\n\n// Glättete Heikin-Ashi-Kerzen plotten\nplotcandlestick(\n smoothedOpen,\n smoothedHigh,\n smoothedLow,\n smoothedClose,\n color=smoothedClose > smoothedOpen ? 'green' : 'red' // Farbe: Grün für bullish, Rot für bearish\n)", 31 | "createdAt": 1623024000001, 32 | "updatedAt": 1733788087783, 33 | "displayName": "SmoothHeikinAshi", 34 | "description": "Smoother HeikinAshi+Range", 35 | "enabled": true, 36 | "author": "Robsn77" 37 | } 38 | } -------------------------------------------------------------------------------- /indicators/qwpto/pvsra.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:PVSRA", 4 | "data": { 5 | "id": "pvsra", 6 | "name": "PVSRA", 7 | "options": { 8 | "priceScaleId": "pvsra", 9 | "blueVectorColor": "rgba(42,32,223,0.47)", 10 | "greenVectorColor": "rgba(38,218,44,0.5)", 11 | "redVectorColor": "rgba(255,0,0,0.5)", 12 | "regularCandleDownColor": "rgba(60,60,60,0.5)", 13 | "regularCandleUpColor": "rgba(170,170,170,0.51)", 14 | "violetVectorColor": "rgba(215,45,215,0.51)", 15 | "showDelta": true, 16 | "downColor": "rgba(203,101,101,0.5)", 17 | "upColor": "rgba(147,198,134,0.51)", 18 | "scaleMargins": { 19 | "top": 0.84, 20 | "bottom": 0 21 | }, 22 | "visible": true 23 | }, 24 | "script": "high = $price.high\nlow = $price.low\nclose = $price.close\nopen = $price.open\nvolume = (vbuy+vsell)/close\n\naverageVolume = sum(volume[1], 10)/10.0\nvolumeSpread = volume*(high - low)\nhighestVolumeSpread = highest(volumeSpread[1], 10)\ncolor = \n (\n (volume >= 2*averageVolume || volumeSpread >= highestVolumeSpread) ? \n (close > open ? options.greenVectorColor : options.redVectorColor) : \n (volume >= 1.5*averageVolume) ? \n (close > open ? options.blueVectorColor : options.violetVectorColor) : \n (close > open ? options.regularCandleUpColor : options.regularCandleDownColor)\n )\n\nif (options.showDelta) {\n plothistogram({ time: time, value: Math.abs((vbuy-vsell)/close), color: (vbuy-vsell)/close > 0 ? options.upColor : options.downColor})\n}\n\nplothistogram({\n time: time,\n value: volume < 0 ? Math.abs(volume) : volume,\n color: color\n})\n\n$price.color = color", 25 | "createdAt": 1683851146489, 26 | "updatedAt": 1704857482660, 27 | "displayName": "PVSRA", 28 | "description": "PVSRA candles indicator as used by traders reality and described here : https://www.forexfactory.com/thread/540706-pvsra-price-volume-sr-analysis", 29 | "author": "qwpto" 30 | } 31 | } -------------------------------------------------------------------------------- /indicators/aggr/smoothed-vwap-bands.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:Smoothed VWAP Bands", 4 | "data": { 5 | "createdAt": 1647173400000, 6 | "options": { 7 | "priceScaleId": "right", 8 | "showBands": true, 9 | "length": 200, 10 | "bandMultiplier": 2, 11 | "showClose": true, 12 | "showHLArea": true, 13 | "color": "rgb(255,255,255)", 14 | "higherLineColor": "rgb(41,98,255)", 15 | "lowerLineColor": "rgb(41,98,255)", 16 | "negativeColor": "rgba(41,98,255,0.2)", 17 | "positiveColor": "rgba(41,98,255,0.2)" 18 | }, 19 | "script": "var vol = vbuy + vsell\nvar length = options.length || 200\n\nvar volSma = sma(vol, options.length)\nvar srcVolSmaClose = sma($price.close * vol, options.length)\nvar srcVolSmaHigh = sma($price.high * vol, options.length)\nvar srcVolSmaLow = sma($price.low * vol, options.length)\nvwapHighClose = srcVolSmaClose / volSma \nvwapHighHigh = srcVolSmaHigh / volSma \nvwapHighLow = srcVolSmaLow / volSma \n\nif (options.showHLArea) {\n cloudarea(vwapHighHigh, vwapHighLow)\n}\n\nif (options.showClose) {\n line(vwapHighClose)\n}\n\nif (options.showBands) {\n var srcSrcVolSmaClose = sma(vol * Math.pow($price.close, 2), options.length)\n var srcSrcVolSmaHigh = sma(vol * Math.pow($price.high, 2), options.length)\n var srcSrcVolSmaLow = sma(vol * Math.pow($price.low, 2), options.length)\n var stDevHighClose = Math.sqrt(srcSrcVolSmaClose / volSma - Math.pow(vwapHighClose, 2))\n var stDevHighHigh = Math.sqrt(srcSrcVolSmaHigh / volSma - Math.pow(vwapHighHigh, 2))\n var stDevHighLow = Math.sqrt(srcSrcVolSmaLow / volSma - Math.pow(vwapHighLow, 2))\n\n if (options.showHLArea) {\n cloudarea(vwapHighLow - stDevHighLow * options.bandMultiplier, vwapHighHigh - stDevHighHigh * options.bandMultiplier)\n cloudarea(vwapHighLow + stDevHighLow * options.bandMultiplier, vwapHighHigh + stDevHighHigh * options.bandMultiplier)\n }\n\n if (options.showClose) {\n line(vwapHighClose - stDevHighClose * options.bandMultiplier)\n line(vwapHighClose + stDevHighClose * options.bandMultiplier)\n }\n}" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /indicators/KeTan/delta-divs.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:Delta divs", 4 | "data": { 5 | "id": "delta-divs", 6 | "name": "Delta divs", 7 | "options": { 8 | "priceScaleId": "right", 9 | "lookback": 10, 10 | "lessRatio": 0.021, 11 | "loopback": 200, 12 | "visible": true 13 | }, 14 | "script": "lookback = option(default=200,min=0,max=200,type=range,step=1)\nlessRatio = option(default=0.05,min=0.02,max=0.1,type=range,step=0.001)\n\n// \nif (!pendingMarkers) {\n // runs only once\n markers = []\n pendingMarkers = []\n lastIndex = null\n}\n// \n\n// \nif (pendingMarkers.length) {\n markers = markers.concat(pendingMarkers)\n if (series[0].setMarkers) {\n series[0].setMarkers(markers)\n }\n pendingMarkers = []\n}\n// \n\n// process only on new candle\nif (bar.length === lastIndex) {\n return\n}\n\ntimes = time\nworkingTime = times[1]\nhigh = $price.high\nlow = $price.low\nph = high[1]\npl = low[1]\n\n// ghost price for markers\ncandlestick($price, color=transparent, border=false, borderColor=transparent, wickColor=transparent)\n\nvs = vsell\nvb = vbuy\nvd = vb[1] - vs[1]\nvol = vb[1] + vs[1]\nvar top = highest(ph, options.lookback)\nvar btm = lowest(pl, options.lookback)\nvar avgVol = sum(vol, options.lookback) / options.lookback\nvar vdRatio = Math.abs(vd / vol)\nvar isLessRatio = vdRatio < options.lessRatio\nvar highVol = vol > avgVol\nvar bearSignal = ph >= top && highVol && (isLessRatio || vd < 0)\nvar bullSignal = pl <= btm && highVol && (isLessRatio || vd > 0)\n\nif (bearSignal) {\n pendingMarkers.push({\n time: workingTime,\n position: 'aboveBar',\n color: 'red',\n shape: 'arrowDown'\n })\n}\n\nif (bullSignal) {\n pendingMarkers.push({\n time: workingTime,\n position: 'belowBar',\n color: 'lime',\n shape: 'arrowUp'\n })\n}\n\n// set reference to bar index : only process first bar of tick\nlastIndex = bar.length", 15 | "createdAt": 1704852855961, 16 | "updatedAt": 1704852923594, 17 | "displayName": "Delta divs", 18 | "description": " ", 19 | "author": "KeTan" 20 | } 21 | } -------------------------------------------------------------------------------- /indicators/aggr/rsi.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:RSI", 4 | "data": { 5 | "createdAt": 1623024000001, 6 | "script": "// rsi\nsrc(50) = $price.close\nvar change = src - src[1]\nvar up = ema(Math.max(change, 0), options.length)\nvar down = ema(-Math.min(change, 0), options.length)\nrsi = down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down))\nline(rsi)\nbrokenarea(options.thresholdUp, options.thresholdLow, color=options.thresholdColor)\n\nif (options.showDivs) {\n // \n if (markers === 0) {\n markers = []\n }\n \n if (pendingMarker && pendingMarker.time < time) {\n markers.push(pendingMarker)\n pendingMarker = null\n }\n \n var newMarker = null\n // \n \n // find long signals\n pl = pivot_low(rsi, options.pivotLeftLength, options.pivotRightLength)\n if (pl && pl !== _pl) {\n var plsrc = src[options.pivotRightLength]\n if (pl > _pl && plsrc < _plsrc && (pl < options.thresholdLow || _pl < options.thresholdLow)) {\n newMarker = {\n time: time - options.pivotRightLength * bar.timeframe,\n position: 'belowBar',\n text: '🔺',\n size: 1\n }\n }\n \n _plsrc = plsrc\n _pl = pl\n }\n \n // find sell signals\n ph = pivot_high(rsi, options.pivotLeftLength, options.pivotRightLength)\n \n if (ph && ph !== _ph) {\n var phsrc = src[options.pivotRightLength]\n if (ph < _ph && phsrc > _phsrc && (ph > options.thresholdUp || _ph > options.thresholdUp)) {\n newMarker = {\n time: time - options.pivotRightLength * bar.timeframe,\n position: 'aboveBar',\n text: '🔻',\n size: 1\n }\n }\n \n _phsrc = phsrc\n _ph = ph\n }\n \n // \n if (newMarker || (pendingMarker && !newMarker)) {\n pendingMarker = newMarker\n \n if (markers.length > 200) {\n markers.splice(0, 1)\n }\n \n if (series[0].setMarkers) {\n series[0].setMarkers(markers.concat(pendingMarker))\n }\n }\n // \n}", 7 | "options": { 8 | "priceScaleId": "rsi", 9 | "scaleMargins": { 10 | "top": 0.01, 11 | "bottom": 0.83 12 | }, 13 | "thresholdColor": "rgba(102,187,106,0.1)", 14 | "color": "rgb(165,214,167)", 15 | "length": 14, 16 | "pivotLeftLength": 3, 17 | "pivotRightLength": 3, 18 | "thresholdLow": 20, 19 | "thresholdUp": 70, 20 | "showDivs": false 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /indicators/redZone.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:RedZone Dynamic Fibonacci Levels", 4 | "data": { 5 | "id": "dynamic-fibonacci-levels", 6 | "name": "RedZone Dynamic Fibonacci Levels", 7 | "options": { 8 | "priceScaleId": "right", 9 | "longPeriod": 200, 10 | "shortPeriod": 55, 11 | "visible": true, 12 | "lastValueVisible": true, 13 | "priceLineVisible": true, 14 | "scaleMargins": { 15 | "top": 0.04, 16 | "bottom": 0.26 17 | } 18 | }, 19 | "script": "if(fib_high===0){fib_high=0;fib_low=0;above50=false;inRedZone=false;hasResetAtZero=true}var price=avg_ohlc(bar);var highestHigh=highest(price.high,options.longPeriod);var lowestLow=lowest(price.low,options.longPeriod);if(highestHigh>fib_high||lowestLowwma_fib50){above50=true;hasResetAtZero=false}}else if(above50&&price.close<=wma_fib50){inRedZone=true;above50=false}candlestick({time:time,open:price.open,high:price.high,low:price.low,close:price.close,color:inRedZone?'red':above50&&price.close>=wma_fib618?'lightgreen':above50?'green':price.close>=wma_fib2618?'white':price.close>=wma_fib1?'orange':price.close>=wma_fib50?'green':price.close>=wma_fib28?'red':price.close>=wma_fib0?'magenta':'blue',title:'Price'});line(wma_fib2618,color='#FFFFFF',title='261.8%');line(wma_fib1,color='#FFA500',title='100%');line(wma_fib618,color='#32CD32',title='61.8%');line(wma_fib50,color='#FF0000',title='50%');line(wma_fib28,color='#FF00FF',title='28%');area({time:time,value:wma_fib0,color:'rgba(0,0,255,0.2)',title:'0%'});cloudarea(wma_fib1,wma_fib2618,color='rgba(0,0,0,0.6)')", 20 | "createdAt": 1704843848569, 21 | "updatedAt": 1704843911852, 22 | "displayName": "Dynamic Fibonacci Levels", 23 | "description": "Dynamic Fibonacci levels with state management and color-coded candles based on price position relative to key levels. Features EMA smoothing and cloud areas for better visualization.", 24 | "author": "TheRealAlanWatts" 25 | } 26 | } -------------------------------------------------------------------------------- /indicators/aggr/vwap-bands.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:VWAP Bands {length}", 4 | "data": { 5 | "createdAt": 1647173400000, 6 | "options": { 7 | "priceScaleId": "right", 8 | "stdevMultiplier": 1.5, 9 | "stdevSecondaryMultiplier": 2, 10 | "buyBgColor": "rgba(255,255,255,0.15)", 11 | "buyColor": "rgba(255,255,255,0.47)", 12 | "sellBgColor": "rgba(255,255,255,0.15)", 13 | "sellColor": "rgba(255,255,255,0.55)", 14 | "dblBuyBgColor": "rgba(255,255,255,0.15)", 15 | "dblBuyColor": "rgba(255,255,255,0.5)", 16 | "dblSellBgColor": "rgba(255,255,255,0.15)", 17 | "dblSellColor": "rgba(255,255,255,0.53)", 18 | "length": 100, 19 | "showDouble": true 20 | }, 21 | "script": "var price = $price\nvar src = (price.high + price.low + price.close)/3\n\nvar sumSell = sum(vsell, options.length)\nvar sumSrcSell = sum(src * vsell, options.length)\nvar sumSrcSrcSell = sum(vsell*Math.pow(src, 2), options.length)\nvar VWAPSell = sumSrcSell / sumSell \nvar varianceSell = sumSrcSrcSell / sumSell - Math.pow(VWAPSell, 2)\nvar stDevSell = Math.sqrt(varianceSell < 0 ? 0 : varianceSell)\nvar sumBuy = sum(vbuy, options.length)\nvar sumSrcBuy = sum(src * vbuy, options.length)\nvar sumSrcSrcBuy = sum(vbuy*Math.pow(src, 2), options.length)\nvar VWAPBuy = sumSrcBuy / sumBuy \nvar varianceBuy = sumSrcSrcBuy / sumBuy - Math.pow(VWAPBuy, 2)\nvar stDevBuy = Math.sqrt(varianceBuy < 0 ? 0 : varianceBuy)\n\nvar thresholdDown = VWAPBuy - stDevBuy * options.stdevMultiplier\nvar thresholdUp = VWAPSell + stDevSell * options.stdevMultiplier\n\ncloudarea(\n VWAPSell - stDevSell * options.stdevMultiplier,\n thresholdDown,\n positiveColor=options.buyBgColor,\n negativeColor=options.buyBgColor,\n higherLineColor=options.buyColor,\n lowerLineColor=options.buyColor)\n\ncloudarea(thresholdUp,\n VWAPBuy + stDevBuy * options.stdevMultiplier,\n positiveColor=options.sellBgColor,\n negativeColor=options.sellBgColor,\n higherLineColor=options.sellColor,\n lowerLineColor=options.sellColor)\n\nvar na = { time: time }\n\nif (options.showDouble) {\n cloudarea(\n VWAPSell - stDevSell * options.stdevSecondaryMultiplier,\n thresholdDown,\n positiveColor=options.dblBuyBgColor,\n negativeColor=options.dblBuyBgColor,\n higherLineColor=options.dblBuyColor,\n lowerLineColor=options.dblBuyColor\n )\n \n cloudarea(\n thresholdUp,\n VWAPBuy + stDevBuy * options.stdevSecondaryMultiplier,\n positiveColor=options.dblSellBgColor,\n negativeColor=options.dblSellBgColor,\n higherLineColor=options.dblSellColor,\n lowerLineColor=options.dblSellColor\n )\n}" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /indicators/aggr/chandelier-exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:Chandelier Exit", 4 | "data": { 5 | "id": "chandelier-exit", 6 | "name": "Chandelier Exit", 7 | "options": { 8 | "priceScaleId": "right", 9 | "highlightState": true, 10 | "mult": 3, 11 | "shortColor": "rgba(183,28,28,0.45)", 12 | "length": 22, 13 | "useClose": true, 14 | "longColor": "rgb(110,246,115)", 15 | "showLabels": true, 16 | "higherLineColor": "rgba(129,199,132,0)", 17 | "positiveColor": "rgba(129,199,132,0.33)", 18 | "negativeColor": "rgba(183,28,28,0.33)", 19 | "lowerLineColor": "rgba(156,52,52,0)" 20 | }, 21 | "script": "/* eslint-disable */\n/* prettier-disable */\n\nif (dir === 0) {\n dir = 1\n markers = []\n}\n\n// \nif (pendingMarker && pendingMarker.time < time) {\n markers.push(pendingMarker)\n pendingMarker = null\n}\n\nvar newMarker = null\n// \n\nopen = $price.open \nhigh = $price.high\nlow = $price.low \nclose = $price.close \n\natr = options.mult * rma(Math.max(high - low, Math.max(Math.abs(high - close[1]), Math.abs(low - close[1]))), options.length)\nlongStop = (options.useClose ? highest(close, length) : highest(high, length)) - atr\n\nif (close[1] > longStop[1]) {\n longStop = Math.max(longStop, longStop[1]) \n}\n\nshortStop = (options.useClose ? lowest(close, length) : lowest(low, length)) + atr\n\nif (close[1] < shortStop[1]) {\n shortStop = Math.min(shortStop, shortStop[1]) \n}\n\nif (close > shortStop[1]) {\n dir= 1\n}\nif (close < longStop[1]) {\n dir= -1\n}\n\nvar ohlc4 = (open + high + low + close) / 4\n\ncloudarea(dir == 1 ? {time: time, higherValue: ohlc4, lowerValue: longStop } : dir == -1 ? {time: time, higherValue: ohlc4, lowerValue: shortStop} : {time: time}, title=\"Stop\")\n\nif (dir == 1 && dir[1] == -1) {\n newMarker = {\n time: time,\n position: 'belowBar',\n color: options.longColor,\n shape: 'arrowUp',\n text: options.showLabels ? 'BUY' : undefined,\n size: 1\n }\n \n // alert for sell signal here\n} else if (dir == -1 && dir[1] == 1) {\n newMarker = {\n time: time,\n position: 'aboveBar',\n color: options.sellColor,\n shape: 'arrowDown',\n text: options.showLabels ? 'SELL' : undefined,\n size: 1\n }\n \n // alert for buy signal here\n}\n\n\n// \nif (newMarker || (pendingMarker && !newMarker)) {\n pendingMarker = newMarker\n \n if (markers.length > 200) {\n markers.splice(0, 1)\n }\n \n if (series[0].setMarkers) {\n series[0].setMarkers(markers.concat(pendingMarker))\n }\n}\n// ", 22 | "createdAt": 1704890240682, 23 | "updatedAt": 1704890292371, 24 | "displayName": "Chandelier Exit", 25 | "description": null, 26 | "author": "aggr" 27 | } 28 | } -------------------------------------------------------------------------------- /indicators/aggr/td-sequential.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:TD Sequential", 4 | "data": { 5 | "id": "td-sequential", 6 | "name": "TD Sequential", 7 | "options": { 8 | "priceScaleId": "right", 9 | "visible": true 10 | }, 11 | "script": "// \nif (!pendingMarkers) {\n // runs only once\n markers = []\n pendingMarkers = []\n lastIndex = null\n\n upCount = 0\n downCount = 0\n upCountdown = 0\n downCountdown = 0\n} else if (pendingMarkers.length) {\n markers = markers.concat(pendingMarkers)\n if (series[0].setMarkers) {\n series[0].setMarkers(markers)\n }\n pendingMarkers = []\n}\nif (bar.length === lastIndex) {\n // ignore same candle, only process previous candle on new candle\n return\n}\n// \n\ncandlestick($price, border = false, upColor = transparent, downColor = transparent, wickColor = transparent) // ghost price for markers\n\ntimes = time\nclose = $price.close\nhigh = $price.high\nlow = $price.low\n// only analyze bar once it's already printed\n// this why we offset everything by 1\nvar currentTime = times[1]\nvar currentClose = close[1]\nvar currentHigh = high[1]\nvar currentLow = low[1]\nvar closeCompare = close[5]\nvar highCompare = high[3]\nvar lowCompare = low[3]\n\nif (!downCountdown && !upCountdown) {\n if (currentClose >= closeCompare) {\n upCount = upCount + 1\n downCount = 0\n\n if (upCount === 9) {\n pendingMarkers.push({\n time: currentTime,\n position: 'aboveBar',\n color: 'red',\n text: upCount.toString(),\n })\n upCount = 0\n upCountdown = 1\n }\n } else if (currentClose <= closeCompare) {\n downCount = downCount + 1\n upCount = 0\n\n if (downCount === 9) {\n pendingMarkers.push({\n time: currentTime,\n position: 'belowBar',\n color: 'red',\n text: downCount.toString(),\n })\n downCount = 0\n downCountdown = 1\n }\n }\n}\n\nif (upCountdown > 0) {\n if (currentClose > highCompare) {\n upCountdown = upCountdown + 1\n }\n\n if (upCountdown === 13) {\n pendingMarkers.push({\n time: currentTime,\n position: 'aboveBar',\n color: 'red',\n text: '🔻',\n })\n upCountdown = 0\n }\n} else if (downCountdown > 0) {\n if (currentClose < lowCompare) {\n downCountdown = downCountdown + 1\n }\n\n if (downCountdown === 13) {\n pendingMarkers.push({\n time: currentTime,\n position: 'belowBar',\n color: 'red',\n text: '🔺',\n })\n downCountdown = 0\n }\n}\n\nlastIndex = bar.length", 12 | "createdAt": 1704847309933, 13 | "updatedAt": 1704938931039, 14 | "displayName": "TD Sequential", 15 | "description": "Tom DeMark", 16 | "pr": "https://github.com/Tucsky/aggr-lib/pull/5", 17 | "author": "aggr" 18 | } 19 | } -------------------------------------------------------------------------------- /indicators/finchen/mondays.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:Mondays", 4 | "data": { 5 | "id": "mondays", 6 | "name": "Mondays", 7 | "options": { 8 | "priceScaleId": "right", 9 | "pivotLength": 14, 10 | "strokeWidth": 1, 11 | "visible": true, 12 | "strokeColor": "rgba(255,255,255,0.6)", 13 | "scaleMargins": { 14 | "top": 0.04, 15 | "bottom": 0.63 16 | } 17 | }, 18 | "script": "// \r\nif (!boundaries) {\r\n lastPivotHigh = null\r\n lastPivotLow = null\r\n\r\n mondayHigh = null\r\n mondayLow = null\r\n\r\n pivotLength = 10\r\n boundaries = {}\r\n if (series[0].setExtensionsBoundaries) {\r\n series[0].setExtensionsBoundaries(boundaries)\r\n }\r\n if (series[1].setExtensionsBoundaries) {\r\n series[1].setExtensionsBoundaries(boundaries)\r\n }\r\n}\r\n// \r\n\r\n// process only on new candle\r\nif (bar.length === lastIndex) {\r\n return\r\n}\r\n\r\nvar nyDateTime = new Date(time*1000);\r\n\r\n// oOnly within 2 weeks\r\nif( new Date().getTime() - nyDateTime.getTime() > 1209600000 ){\r\n return\r\n}\r\n\r\n// Tuesday 1pm in NZ = Monday midnight\r\nif( nyDateTime.getDay() == 2 && nyDateTime.getHours() === 13 && nyDateTime.getMinutes() === 0 ){\r\n pivotLength = Math.round((23*60*60/bar.timeframe) )\r\n mondayHigh = pivot_high($price.high, pivotLength, 0)\r\n mondayLow = pivot_low($price.low, pivotLength, 0)\r\n}\r\n\r\nif (mondayHigh) {\r\n if (lastPivotHigh) {\r\n boundaries[lastPivotHigh] = bar.length\r\n }\r\n lastPivotHigh = mondayHigh\r\n mondayHigh = null\r\n renderer.indicators[indicatorId].series[0] = {\r\n time: time,\r\n id: lastPivotHigh,\r\n lowerValue: lastPivotHigh,\r\n higherValue: lastPivotHigh,\r\n extendRight: true,\r\n label: lastPivotHigh,\r\n }\r\n\r\n if (lastPivotLow) {\r\n boundaries[lastPivotLow] = bar.length\r\n }\r\n lastPivotLow = mondayLow\r\n mondayHigh = null\r\n renderer.indicators[indicatorId].series[1] = {\r\n time: time,\r\n id: lastPivotLow,\r\n lowerValue: lastPivotLow,\r\n higherValue: lastPivotLow,\r\n extendRight: true,\r\n label: lastPivotLow,\r\n }\r\n}\r\n\r\nlastIndex = bar.length\r\n \r\nreturn\r\n \r\nbrokenarea({ time: time }, strokeWidth = options.strokeWidth, strokeColor = options.strokeColor, lastValueVisible = true, title = \"M.H\")\r\nbrokenarea({ time: time }, strokeWidth = options.strokeWidth, strokeColor = options.strokeColor, lastValueVisible = true, title = \"M.L\")", 19 | "createdAt": 1701224825127, 20 | "updatedAt": 1704843293396, 21 | "displayName": "Mondays", 22 | "description": "If you are following Rektproof strategy around mondays high & low. Here an indicator to print it. \nYou'll need to change the date to trigger it (moonday midnight). I am based in NZ so it is tuesday 1pm.", 23 | "author": "finchen" 24 | } 25 | } -------------------------------------------------------------------------------- /indicators/aggr/volume-delta-avg.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicators:VOL Δ Avg", 4 | "data": { 5 | "options": { 6 | "priceScaleId": "_8x3g8odw7u9j6jx5", 7 | "priceFormat": { 8 | "type": "volume" 9 | }, 10 | "upColor": "rgb(82,209,88)", 11 | "downColor": "rgb(242,54,69)", 12 | "scaleMargins": { 13 | "top": 0.8, 14 | "bottom": 0 15 | }, 16 | "showDelta": true, 17 | "upBgColor": "rgba(65,240,123,0.5)", 18 | "downBgColor": "rgba(49,121,245,0.5)", 19 | "visible": true, 20 | "length": 14, 21 | "smoothLength": 3, 22 | "avgLength": 4, 23 | "veryDownColor": "rgb(128,25,34)" 24 | }, 25 | "script": "avgLength = option(default=4)\nsmoothLength = option(default=3)\nveryUpColor = option(type=color,default=rgb(255,255,0))\nupColor = option(type=color,default=rgb(82,209,88))\ndownColor = option(type=color,default=rgb(242,54,69))\nveryDownColor = option(type=color,default=rgb(128,25,34))\n\nvar delta = vbuy - vsell; // delta in USD\nvar avgDelta = sma(vbuy - vsell, avgLength); // average delta over 'avgLength' bars\n\n// Ensure avgDelta is positive for stable division and add a small epsilon to avoid division by zero\navgDelta = Math.abs(avgDelta) + Number.EPSILON;\n\n// Adjusted normalization to include a smoothing factor (k)\nvar k = 0.5; // Smoothing constant, adjust based on desired sensitivity\nvar normalized_delta = (delta / (2 * avgDelta * k) + 0.5);\n\n// Smoothed ratio calculation to reduce extremes\n// Using Math.max and Math.min to ensure the ratio stays between 0 and 1\nvar smoothed_ratio = Math.max(0, Math.min(1, normalized_delta));\n\n// Apply a simple moving average (SMA) or exponential moving average (EMA) for further smoothing\n// 'smoothing_length' defines the period for the moving average\nvar smoothed_ratio_sma = sma(smoothed_ratio, smoothLength);\nhistogram({\n time: time,\n value: vbuy + vsell,\n color: interpolate(smoothed_ratio_sma, veryDownColor, downColor, upColor, veryUpColor)\n})", 26 | "createdAt": 1708780055899, 27 | "updatedAt": 1709483301384, 28 | "displayName": "VOL Δ Avg", 29 | "description": "The Dynamic Volume Delta Histogram Indicator is an advanced tool designed for financial market analysis, providing traders with a vivid visualization of volume dynamics. This indicator displays the volume of the current bar while utilizing a color-coded system to represent the normalized volume delta.\n\nBy calculating the difference between buy (vbuy) and sell (vsell) volumes, termed as 'delta', and then smoothing this value over a user-defined average length (avgLength), the indicator offers a clear and customizable view of market sentiment. The normalization process adjusts the delta by a smoothing factor (k), ensuring the ratio is stabilized and falls between 0 and 1. The final smoothed ratio is then depicted as a histogram, which changes color according to the magnitude of volume change: from 'veryDownColor' for significant sell volumes, 'downColor' for moderate sells, 'upColor' for moderate buys, to 'veryUpColor' for substantial buy volumes.\n\nThis indicator is ideal for traders who need to quickly assess market volume and sentiment changes. By integrating both a simple moving average (SMA) and an exponential moving average (EMA) for additional smoothing, the Dynamic Volume Delta Histogram Indicator minimizes noise and highlights true market trends.", 30 | "pr": "https://github.com/Tucsky/aggr-lib-test/pull/7", 31 | "author": "aggr" 32 | } 33 | } -------------------------------------------------------------------------------- /indicators/finchen/obv-spots.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:OBV spots", 4 | "data": { 5 | "id": "obv-spots", 6 | "name": "OBV spots", 7 | "options": { 8 | "priceScaleId": "obv-copy-1", 9 | "visible": true, 10 | "scaleMargins": { 11 | "top": 0.64, 12 | "bottom": 0 13 | }, 14 | "lineStyle": 1, 15 | "color": "rgba(255,235,59,0.81)", 16 | "bodyPercentWatch": 0.3, 17 | "wickDiffPercent": 90, 18 | "debug": null, 19 | "toggleDebug": true, 20 | "toggleVolumeWick": false, 21 | "wickRatioThreshold": 1.5, 22 | "togglePrintNormalObv": true, 23 | "originalObvColor": "rgb(178,181,190)", 24 | "refinedObvColor": "rgb(41,98,255)" 25 | }, 26 | "script": "if (markers === 0) {\r\n // first script execution (0 is default values for all persistent variables)\r\n\r\n // create a var to store already drawn markers\r\n markers = []\r\n\r\n // contain the last marker that can change as the bar update\r\n repaintableMarker = null\r\n\r\n // if candle body has a price percent change less than bodyPercentWatch, we start a refinement\r\n bodyPercentWatch = options.bodyPercentWatch || 0.3\r\n\r\n // if a wick is x times greater than the other, we might use it to refine change the side\r\n wickRatioThreshold = options.wickRatioThreshold || 1.5\r\n\r\n // paint a marker when OBV has been refined (changed of direction)\r\n toggleDebug = options.toggleDebug || false\r\n\r\n // when on, we calculate the wick percent difference and keep only that volume to add to OBV\r\n toggleVolumeWick = options.toggleVolumeWick || false\r\n\r\n togglePrintNormalObv = options.togglePrintNormalObv || false\r\n}\r\nif (repaintableMarker && repaintableMarker.time < time) {\r\n // bar is +1 since active marker was added, lock it in the array\r\n markers.push(repaintableMarker)\r\n\r\n // free up active marker\r\n repaintableMarker = null\r\n}\r\n\r\nvar newMarker = null\r\nvar _vbuy = source(vbuy, type=spot)\r\nvar _vsell = source(vsell, type=spot)\r\nvar _open = $price.open\r\nvar _high = $price.high\r\nvar _low = $price.low\r\nvar _close = $price.close\r\n\r\nvar sign = _close > _open ? 1 : -1\r\nvar volume = _vbuy + _vsell\r\n\r\nvar bodySize = Math.abs(_close - _open)\r\nvar priceChange = (_close - _open) / _open // Percentage change\r\n\r\nif (togglePrintNormalObv) {\r\n plotline(cum(sign * volume), title='Original', color=options.originalObvColor)\r\n}\r\n\r\nvar improved = false\r\n\r\n// Check if price change is less than 0.4%\r\nif (Math.abs(priceChange) < bodyPercentWatch / 100) {\r\n var upperWickSize = _high - _close\r\n var lowerWickSize = _close - _low\r\n\r\n // Calculate the thresholds\r\n var upperWickThreshold = wickRatioThreshold * lowerWickSize\r\n var lowerWickThreshold = wickRatioThreshold * upperWickSize\r\n\r\n // Check if upper and lower wicks have similar sizes\r\n if (upperWickSize < upperWickThreshold && lowerWickSize < lowerWickThreshold) {\r\n // Neutral move, don't add volume\r\n sign = 0\r\n improved = true\r\n } else {\r\n // Calculate the thresholds\r\n var upperWickThreshold = wickRatioThreshold * lowerWickSize\r\n var lowerWickThreshold = wickRatioThreshold * upperWickSize\r\n\r\n // Determine the side based on wick sizes\r\n // upper: 500, lower 100\r\n if (upperWickSize > upperWickThreshold) {\r\n improved = sign !== -1\r\n // More selling happened, add volume as negative\r\n sign = -1\r\n if (toggleVolumeWick) {\r\n improved = true\r\n var percentageDifference = (upperWickSize - lowerWickSize) / upperWickSize\r\n volume = percentageDifference * volume\r\n }\r\n } else if (lowerWickSize > lowerWickThreshold) {\r\n improved = sign !== 1\r\n // More buying happened, add volume as positive\r\n sign = 1\r\n if (toggleVolumeWick) {\r\n improved = true\r\n var percentageDifference = (lowerWickSize - upperWickSize) / lowerWickSize\r\n volume = percentageDifference * volume\r\n }\r\n }\r\n }\r\n\r\n if (improved && toggleDebug) {\r\n if (sign == -1) {\r\n // newMarker is a temporary variable (not included in the indicator state)\r\n // to avoid it being treated as persistent variable we wrap it inside parenthesis\r\n newMarker = {\r\n time: time,\r\n position: 'aboveBar',\r\n color: 'red',\r\n shape: 'arrowDown',\r\n }\r\n }\r\n\r\n if (sign == 1) {\r\n newMarker = {\r\n time: time,\r\n position: 'belowBar',\r\n color: 'lime',\r\n shape: 'arrowUp',\r\n }\r\n }\r\n }\r\n}\r\n\r\nif (newMarker || (repaintableMarker && !newMarker)) {\r\n // override persistent variable repaintable marker\r\n repaintableMarker = newMarker\r\n\r\n var index = togglePrintNormalObv ? 1 : 0\r\n\r\n if (series[index].setMarkers) {\r\n series[index].setMarkers(markers.concat(repaintableMarker))\r\n }\r\n}\r\n\r\nvar obv = cum(sign * volume)\r\nplotline(obv, title=\"Refined\", color=options.refinedObvColor)", 27 | "createdAt": 1701126053261, 28 | "updatedAt": 1704842942708, 29 | "displayName": "OBV spots", 30 | "description": null, 31 | "author": "finchen" 32 | } 33 | } -------------------------------------------------------------------------------- /indicators/aggr/fibonacci-retracement.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicators:Fibonacci Retracement", 4 | "data": { 5 | "options": { 6 | "priceScaleId": "right", 7 | "strokeWidth": 4, 8 | "scaleMargins": { 9 | "top": 0.04, 10 | "bottom": 0.22 11 | }, 12 | "color": "rgb(255,255,255)", 13 | "lineWidth": 1 14 | }, 15 | "script": "// Top to bottom fib retracement levels / extentions\n// 1. identify top and bottom\n// 2. pull a fib sequence (see levels option)\n\n// define series (1 serie = 1 line renderable at each bar)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\nbrokenarea(infinite = extend)\n\n// options\nsrcExchange = option(default=null,type=exchange,rebuild=true)\nsrcQuote = option(default=null,type=list,options=[null, \"USD\", \"USDT\", \"TUSD\", \"USDC\"],rebuild=true)\nsrcType = option(default=null,type=list,options=[null, \"spot\", \"perp\"],rebuild=true)\nuseHeikinAshi = option(type=checkbox)\nuseGaps = option(type=checkbox)\nlengthLeft=option(type=range,default=80,min=3,max=200,step=1,description=\"Left pivot length (look back)\")\nlengthRight=option(type=range,default=80,min=3,max=200,step=1,description=\"Right pivot length (look ahead)\")\nextendLength=option(type=range,min=0,max=2000,log=true,default=0,step=1,description=\"Extend line length (0 = infinite)\")\nrepaint=option(type=checkbox,default=false,description=\"Place line at (current time - lengthRight)\")\nuseClose=option(type=checkbox,default=false,description=\"Use close instead of high and low\")\nuseSwing=option(type=checkbox,default=false,description=\"Use inner body (open or close)\")\nuseLog=option(type=checkbox,default=false,description=\"Use log scale for ext calculation\")\nextend=option(rebuild=true,type=checkbox,default=true,description=\"Extend beyond visible range\")\nshowLevels=option(type=checkbox,default=false,description=\"Show what levels each line is\")\nlevels=option(type=text,default=\"0|0.618|1|1.118|1.618|2.118|2.618|3.427|4.236\",description=\"Comma separated list of fib levels (max 4 or see script)\")\ncolorA=option(type=color,default='rgba(255,255,0,0.13)')\ncolorB=option(type=color,default='rgb(255,235,59)')\ncolorC=option(type=color,default='rgb(255,235,59)')\ncolorD=option(type=color,default='rgb(255,235,59)')\nshowPivots=option(default=false,type=checkbox)\nshowLookbackMa=option(default=false,type=checkbox)\nminLookback=option(default=200,description=\"Forces chart to fetch more data than needed\")\nlookbackMaColor=option(type=color,default='white')\nstrokeWidth=option(default=1)\n\n// \n\nif (!pendingMarkers) {\n markers = []\n pendingMarkers = []\n count=0\n\n if (!showPivots && series[series.length - 1].setMarkers) {\n series[series.length - 1].setMarkers([])\n }\n}\n\nif (pendingMarkers.length) {\n markers = markers.concat(pendingMarkers)\n if (series[series.length - 1].setMarkers) {\n series[series.length - 1].setMarkers(markers)\n }\n pendingMarkers = []\n}\n\nif (!boundaries) {\n //init\n\n lastIndex = null\n boundaries = {} // store *end* of lines here (id => index)\n levelsArr = String(levels).split(/,|\\|/)\n\n var slots = 0\n for (let i = 0; i < series.length; i++) {\n if (series[i].seriesType() === 'BrokenArea') {\n series[i].setExtensionsBoundaries(boundaries)\n slots++\n }\n }\n\n maxLevels = Math.min(levelsArr.length, slots)\n} else \n\nif (bar.length === lastIndex || bar.length < lengthLeft) {\n // only execute 1 time per bar\n return\n}\n\n// get price source\nvar src = srcExchange || srcType || srcQuote ? source(type=srcType, exchange=srcExchange, quote=srcQuote) : bar\n\nif (!src) {\n return\n}\n\nvar a = 0\nvar b = 0\n\n// get price ohlc\nprice = useHeikinAshi ? avg_heikinashi(src) : useGaps ? avg_ohlc_with_gaps(src) : avg_ohlc(src)\n\nif (showLookbackMa) {\n // this forces chart to fetch more candle than needed for more fibs\n line(ema(price.close, minLookback), lineStyle=3, color=lookbackMaColor)\n}\n\ncandlestick(price, upColor=transparent,downColor=transparent,wickColor=transparent)\n// find pivot high\nswingHigh = useSwing ? price.close < price.open && price[1].close > price[1].open ? Math.min(price[1].close, price.open) : swingHigh : null\nvar ph = pivot_high(useClose ? useSwing ? swingHigh : price.close : price.high, lengthLeft, lengthRight)\n\nif (bar.length > lengthLeft+lengthRight && ph && ph != lastPh) {\n // if pivot high\n lastPh = ph\n\n lastPhTime = time - bar.timeframe * (lengthRight - 1)\n if (showPivots) {\n pendingMarkers.push({\n time: lastPhTime,\n position: 'aboveBar',\n color: '#dfd390ff',\n text: lastPh.toString(),\n })\n }\n if (lastPl) {\n a = lastPh\n b = lastPl\n t = lastPhTime\n }\n}\n\n// find pivot low\nswingLow = useSwing ? price.open < price.close && price[1].open > price[1].close ? Math.max(price[1].close, price.open) : swingLow : null\nvar pl = pivot_low(useClose ? useSwing ? swingLow : price.close : price.low, lengthLeft, lengthRight)\n\nif (bar.length > lengthLeft+lengthRight && pl && pl !== lastPl) {\n // if pivot low\n lastPl = pl\n\n lastPlTime = time - bar.timeframe * (lengthRight - 1)\n if (showPivots) {\n pendingMarkers.push({\n time:lastPlTime,\n position: 'belowBar',\n color: '#ef4352ff',\n text: lastPl.toString(),\n })\n }\n\n if (lastPh) {\n a = lastPl\n b = lastPh\n t = lastPlTime\n }\n}\n\nif (a && b) {\n var isLow = b < a\n var r = useLog ? Math.log(isLow ? a / b : b / a) : isLow ? a - b : b - a\n if (paintId && extendLength > 0) {\n boundaries[paintId] = bar.length + extendLength - lengthRight - 1\n }\n paintId = String(Math.random())\n\n var isFirst = !ext\n\n for (let i = 0; i < levelsArr.length; i++) {\n if (!series[i]) {\n continue\n }\n var level = levelsArr[i]\n if (useLog) {\n ext = isLow ? a * Math.exp(-r * level) : a * Math.exp(r * level);\n } else {\n ext = a + r * level * (isLow ? -1 : 1);\n }\n if (bar.length > lengthLeft + lengthRight) {\n bar.series[series[i].id] = {\n id: paintId,\n time: repaint ? t : time,\n higherValue: ext,\n lowerValue: ext,\n extendRight: true,\n color: interpolate(i / maxLevels, colorA, colorB, colorC, colorD),\n lineWidth: i / maxLevels,\n label: showLevels ? String(level) : undefined\n }\n }\n }\n a = null\n b = null\n}\n\n// set reference to bar index (only process first tick of a bar)\nlastIndex = bar.length", 16 | "createdAt": 1708647696429, 17 | "updatedAt": 1729109961977, 18 | "displayName": "Fibonacci Retracement", 19 | "description": "This Automatic Fibonacci Retracement/Extension Indicator is engineered to assist traders in identifying key support and resistance levels using pivot points and custom Fibonacci ratios. It automatically calculates and draws Fibonacci levels on a trading chart by locating pivot highs and lows within a user-defined lookback and lookahead range.\n\nThe indicator offers a set of configurable options allowing for precise customization to fit various trading strategies. Users can input a list of preferred Fibonacci-like ratios beyond the standard 0.618 and 1.618, including extended levels such as 4.236 and 9.618, which are particularly useful for predicting potential retracements or extensions of market movements.\n\nFeatures include:\n\nCustomizable Fibonacci ratios for both retracement and extension levels.\nAdjustable pivot lengths to define the range for high and low pivots.\nOptional logarithmic scaling for extension calculations.\nSelection between using high/low or open/close for determining pivots.\nHeikin Ashi and gap inclusion for more sophisticated price calculations.\nInfinite or specified line extension to project future price levels.\nVisual differentiation through color gradients applied to the Fibonacci levels.", 20 | "enabled": false, 21 | "pr": "https://github.com/Tucsky/aggr-lib/pull/32", 22 | "author": "aggr" 23 | } 24 | } -------------------------------------------------------------------------------- /indicators/aggr/fibonacci-grid.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicators", 3 | "name": "indicators:Fibonacci Grid", 4 | "data": { 5 | "id": "fibonacci-grid", 6 | "name": "Fibonacci Grid", 7 | "options": { 8 | "priceScaleId": "right", 9 | "levels": "0|1|2|3|5|8|13|21|34|55|89", 10 | "levelMult": 1, 11 | "drawCount": 3, 12 | "srcExchange": 0, 13 | "srcQuote": 0, 14 | "srcType": null, 15 | "targetLevelUp": 84.30000000000001, 16 | "invalidationThreshold": 0, 17 | "enableInvalidation": 0, 18 | "invalidateSwing": true, 19 | "useHeikinAshi": 0, 20 | "useGaps": true, 21 | "showUp": true, 22 | "enableTarget": true, 23 | "showLabel": 0, 24 | "lineWidth": 1.5, 25 | "upColor": "rgb(255,235,59)" 26 | }, 27 | "script": "levels = option(default=\"0|1|2|3|5|8|13|21|34|55|89\",type=\"text\")\nlevelMult = option(default=\"1\",type=\"text\")\ndrawCount = option(type=range,min=1,max=6,default=3,step=1)\nsrcExchange = option(type=exchange,rebuild=true)\nsrcQuote = option(default=null,type=list,options=[null, \"USD\", \"USDT\", \"TUSD\", \"USDC\"],rebuild=true)\nsrcType = option(default=perp,type=list,options=[null, \"spot\", \"perp\"],rebuild=true)\ntargetLevelUp = option(type=range,min=0,max=89,default=79)\ninvalidationThreshold = option(type=range,max=2,min=0,step=0.01)\nenableInvalidation = option(type=checkbox)\ninvalidateSwing = option(type=checkbox,default=true)\nuseHeikinAshi = option(type=checkbox)\nuseGaps = option(type=checkbox,default=true)\nshowUp = option(type=checkbox,default=true)\nenableTarget = option(type=checkbox,default=true)\nshowLabel = option(type=checkbox)\nlineWidth = option(default=1.5)\nupColor = option(type=color,default=\"rgb(255,235,59)\")\n\n// \nif (!pendingMarkers) {\n // runs only once\n markers = []\n pendingMarkers = []\n lastIndex = null\n freeSlots = []\n usedSlots = []\n swings = []\n rawLevels = options.levels.toString().split(/,|\\|/).map(a => +a)\n baseLevelIndex = rawLevels.indexOf(0)\n up = {}\n boundaries = {}\n var mults = levelMult.toString().split(/,|\\|/).map(a => +a)\n calculatedLevels = rawLevels.reduce((acc, level, index, arr) => {\n return mults.reduce((acc2, val) => {\n arr[index + 1] && acc2.push(+(level + (arr[index + 1] - level) * val).toFixed(3));\n return acc2\n }, acc)\n }, [rawLevels[0]])\n for (var i = 1; i < series.length; i++) {\n if (series[i].seriesType() !== 'BrokenArea') {\n continue\n }\n\n freeSlots.push({\n index: i,\n redrawAt: 0\n })\n\n series[i].setExtensionsBoundaries(boundaries)\n }\n}\n// \n\n// \nif (pendingMarkers.length) {\n markers = markers.concat(pendingMarkers)\n if (series[0].setMarkers) {\n series[0].setMarkers(markers)\n }\n pendingMarkers = []\n}\n// \n\n// process only on new candle\nif (bar.length === lastIndex) {\n return\n}\nsrc = options.srcExchange || options.srcType || options.srcQuote ? source(type=options.srcType, exchange=options.srcExchange, quote=options.srcQuote) : bar\n\nif (!src) {\n return\n}\nprice = options.useHeikinAshi ? avg_heikinashi(src) : options.useGaps ? avg_ohlc_with_gaps(src) : avg_ohlc(src)\ncandlestick(price, upColor=transparent, downColor=transparent, wickUpColor=transparent, wickDownColor=transparent, showBorders=false) // ghost price for markers\n\n// constants and working values\nopen = price.open\nclose = price.close\nhigh = price.high\nlow = price.low\nvar workingClose = close[1]\nvar workingOpen = open[1]\nvar workingHigh = high[1]\nvar workingLow = low[1]\nvar workingTime = time\nvar swingHigh = workingClose > workingOpen && close <= open ? Math.min(open, workingClose) : null\nvar swingLow = workingClose < workingOpen && close >= open ? Math.max(open, workingClose) : null\n\nif (swingHigh || swingLow) {\n swings.unshift(swingHigh || swingLow)\n if (swings.length > 4) {\n swings.pop()\n }\n}\n\nif (options.showUp) {\n if (up.valid) {\n // up trend origin broken ❌\n if (options.enableInvalidation && (options.invalidateSwing ? swingHigh && swingHigh < up.invalidation : workingClose < up.invalidation)) {\n // deactivate trend\n up.valid = false\n }\n \n // up trend target reached 💰\n if (options.enableTarget && high[1] >= up.target) {\n up.valid = false\n pendingMarkers.push({\n time: time,\n position: 'aboveBar',\n text: '💰'\n })\n }\n }\n\n // in active up trend process\n if (up.active) {\n\n // ranges breakout\n if (workingClose > up.levels[up.higherIndex].price) { // move next\n for (let i = up.levels[up.higherIndex].index + 1; i < calculatedLevels.length; i++) {\n var higherPriceLevel = up.origin + up.impulse * calculatedLevels[i]\n\n if (workingClose < higherPriceLevel) {\n var change = i - up.levels[up.higherIndex].index // +1\n if (up.levels[0].index + change < 0 || up.levels[up.levels.length - 1].index + change > calculatedLevels.length - 1) {\n break\n }\n up.redrawRequired = true\n\n for (var j = 0; j < up.levels.length; j++) {\n up.levels[j].index = up.levels[j].index + change\n\n\n if (j < change && up.drawings[up.levels[j].price]) {\n // level out of scope\n var plotIndexToFree = usedSlots.splice(usedSlots.indexOf(up.drawings[up.levels[j].price].index), 1)[0]\n freeSlots.push({\n index: plotIndexToFree,\n redrawAt: bar.length + 1\n })\n boundaries[up.drawings[up.levels[j].price].id] = bar.length - 1\n delete up.drawings[up.levels[j].price]\n }\n \n var level = (calculatedLevels)[up.levels[j].index]\n up.levels[j].price = up.origin + up.impulse * level\n up.levels[j].level = level\n up.levels[j].upper = up.origin + up.impulse * level\n up.levels[j].lower = up.origin + up.impulse * level\n }\n\n break;\n }\n }\n\n if (up.invalidationIndex === null) {\n up.invalidationIndex = up.levels[up.lowerIndex].index - 1\n var nextLevel = (calculatedLevels)[up.invalidationIndex]\n up.invalidation = up.origin + up.impulse * (nextLevel === undefined ? (calculatedLevels)[up.invalidationIndex] : (levels)[up.invalidationIndex] + (atr) * (fibMultUp)[0])\n }\n\n } else if (workingClose < up.levels[up.lowerIndex].price) { // move prev\n for (let i = up.levels[up.lowerIndex].index - 1; i >= 0; i--) {\n var lowerPriceLevel = up.origin + up.impulse * calculatedLevels[i]\n\n if (workingClose > lowerPriceLevel) {\n var change = i - up.levels[up.lowerIndex].index // -1\n if (up.levels[0].index + change < 0 || up.levels[up.levels.length - 1].index + change > levels.length - 1) {\n break\n }\n up.redrawRequired = true\n\n for (var j = 0; j < up.levels.length; j++) {\n up.levels[j].index = up.levels[j].index + change\n\n if (j >= change + up.levels.length && up.drawings[up.levels[j].price]) {\n var plotIndexToFree = usedSlots.splice(usedSlots.indexOf(up.drawings[up.levels[j].price].index), 1)[0]\n freeSlots.push({\n index: plotIndexToFree,\n redrawAt: bar.length + 1\n })\n boundaries[up.drawings[up.levels[j].price].id] = bar.length - 1\n delete up.drawings[up.levels[j].price]\n }\n\n var level = (calculatedLevels)[up.levels[j].index]\n up.levels[j].price = up.origin + up.impulse * level\n up.levels[j].level = level\n up.levels[j].upper = up.origin + up.impulse * level\n up.levels[j].lower = up.origin + up.impulse * level\n }\n\n break;\n }\n }\n }\n\n // drawer\n if (up.redrawRequired) {\n up.redrawRequired = false\n var missingSlots = 0\n for (var i = 0; i < up.levels.length; i++) {\n if (!up.drawings[up.levels[i].price]) {\n if (freeSlots.length) {\n var slot = freeSlots.find(slot => slot.redrawAt < bar.length)\n\n if (!slot) {\n up.redrawRequired = true\n continue\n }\n\n up.drawings[up.levels[i].price] = {\n index: slot.index,\n id: 'up-' + up.levels[i].price + time\n }\n\n usedSlots.push(freeSlots.splice(freeSlots.indexOf(slot), 1)[0].index)\n renderer.indicators[indicatorId].series[up.drawings[up.levels[i].price].index] = {\n time: workingTime,\n color: options.upColor,\n id: up.drawings[up.levels[i].price].id,\n lowerValue: up.levels[i].lower,\n higherValue: up.levels[i].upper,\n extendRight: true,\n label: options.showLabel ? up.levels[i].level : ''\n }\n } else {\n up.redrawRequired = true\n missingSlots = missingSlots + 1\n }\n }\n }\n }\n }\n}\n\nif (options.showUp && swingHigh && (swings)[0] > (swings)[2] && (!up.valid || up.origin > (swings)[1])) {\n var upId = 'up' + bar.length\n\n if (up.active) {\n // active if opposite side didn't stopped it (happens when singleSide is disabled)\n boundaries[up.id] = bar.length - 1\n for (let i = 0; i < up.levels.length; i++) {\n if (up.drawings[up.levels[i].price]) {\n var plotIndexToFree = usedSlots.splice(usedSlots.indexOf(up.drawings[up.levels[i].price].index), 1)[0]\n freeSlots.push({\n index: plotIndexToFree,\n redrawAt: bar.length + 1\n })\n boundaries[up.drawings[up.levels[i].price].id] = bar.length - 1\n delete up.drawings[up.levels[i].price]\n }\n }\n }\n\n var a = (swings)[1]\n var b = (swings)[0]\n\n // define current up trend\n up = {\n active: true, // active drawing\n valid: true,\n timestamp: workingTime,\n id: upId,\n origin: a,\n neckline: b,\n impulse: b - a,\n target: a + (b - a) * (options.targetLevelUp),\n invalidation: a + (b - a) * (options.invalidationThreshold),\n invalidationIndex: 0,\n levels: [],\n drawings: {},\n redrawRequired: true,\n }\n\n var fromIndex = Math.max(baseLevelIndex - options.drawCount, 0)\n var toIndex = fromIndex + options.drawCount * 2 - 1\n up.lowerIndex = options.drawCount - 1\n up.higherIndex = options.drawCount\n\n for (let i = fromIndex; i <= toIndex; i++) {\n var level = (calculatedLevels)[i]\n up.levels.push({\n index: i,\n price: up.origin + up.impulse * level,\n upper: up.origin + up.impulse * level,\n lower: up.origin + up.impulse * level,\n level: level,\n })\n }\n}\n\n// set reference to bar index : avoid process next tick\nlastIndex = bar.length\n\n// 1-* = trend levels (slots)\nif (renderer.indicators[indicatorId].series[1]) {\n brokenarea(renderer.indicators[indicatorId].series[1] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[2]) {\n brokenarea(renderer.indicators[indicatorId].series[2] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[3]) {\n brokenarea(renderer.indicators[indicatorId].series[3] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[4]) {\n brokenarea(renderer.indicators[indicatorId].series[4] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[5]) {\n brokenarea(renderer.indicators[indicatorId].series[5] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[6]) {\n brokenarea(renderer.indicators[indicatorId].series[6] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[7]) {\n brokenarea(renderer.indicators[indicatorId].series[7] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[8]) {\n brokenarea(renderer.indicators[indicatorId].series[8] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[9]) {\n brokenarea(renderer.indicators[indicatorId].series[9] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[10]) {\n brokenarea(renderer.indicators[indicatorId].series[10] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[11]) {\n brokenarea(renderer.indicators[indicatorId].series[11] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[12]) {\n brokenarea(renderer.indicators[indicatorId].series[12] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[13]) {\n brokenarea(renderer.indicators[indicatorId].series[13] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[14]) {\n brokenarea(renderer.indicators[indicatorId].series[14] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[15]) {\n brokenarea(renderer.indicators[indicatorId].series[15] || { time: time }, strokeWidth = lineWidth)\n}\nif (renderer.indicators[indicatorId].series[16]) {\n brokenarea(renderer.indicators[indicatorId].series[16] || { time: time }, strokeWidth = lineWidth)\n}", 28 | "createdAt": 1704935468237, 29 | "updatedAt": 1704938541637, 30 | "displayName": "Fibonacci Grid", 31 | "description": "Auto Fibonacci Levels", 32 | "pr": "https://github.com/Tucsky/aggr-lib/pull/14", 33 | "author": "aggr" 34 | } 35 | } -------------------------------------------------------------------------------- /indicators/aggr/shm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicator:SHM", 4 | "data": { 5 | "createdAt": 1658483940000, 6 | "options": { 7 | "priceScaleId": "shm", 8 | "stochMultiplier": 1, 9 | "pColor1": "rgb(207,0,0)", 10 | "pColor2": "#f22b11", 11 | "pColor3": "#f29811", 12 | "pColor4": "#eef211", 13 | "pColor5": "#3af211", 14 | "nColor1": "#02269e", 15 | "nColor2": "#0039f5", 16 | "nColor3": "#1176f2", 17 | "nColor4": "#11aff2", 18 | "nColor5": "#11e7f2", 19 | "showHeatmap": true, 20 | "scaleMargins": { 21 | "top": 0.84, 22 | "bottom": 0 23 | }, 24 | "emaLength": 1, 25 | "bottomColor": "rgb(1,24,101)", 26 | "topColor": "rgb(153,0,0)" 27 | }, 28 | "description": "Stochastic Heat Map", 29 | "script": "// © Violent\n// https://www.tradingview.com/script/7PRbCBjk-Stochastic-Heat-Map/\n\nvar stoch1 = ema(stoch($price.close, $price.high, $price.low, 1 * options.stochMultiplier), 1 + options.emaLength)\nvar stoch2 = ema(stoch($price.close, $price.high, $price.low, 2 * options.stochMultiplier), 2 + options.emaLength)\nvar stoch3 = ema(stoch($price.close, $price.high, $price.low, 3 * options.stochMultiplier), 3 + options.emaLength)\nvar stoch4 = ema(stoch($price.close, $price.high, $price.low, 4 * options.stochMultiplier), 4 + options.emaLength)\nvar stoch5 = ema(stoch($price.close, $price.high, $price.low, 5 * options.stochMultiplier), 5 + options.emaLength)\nvar stoch6 = ema(stoch($price.close, $price.high, $price.low, 6 * options.stochMultiplier), 6 + options.emaLength)\nvar stoch7 = ema(stoch($price.close, $price.high, $price.low, 7 * options.stochMultiplier), 7 + options.emaLength)\nvar stoch8 = ema(stoch($price.close, $price.high, $price.low, 8 * options.stochMultiplier), 8 + options.emaLength)\nvar stoch9 = ema(stoch($price.close, $price.high, $price.low, 9 * options.stochMultiplier), 9 + options.emaLength)\nvar stoch10 = ema(stoch($price.close, $price.high, $price.low, 10 * options.stochMultiplier), 10 + options.emaLength)\nvar stoch11 = ema(stoch($price.close, $price.high, $price.low, 15 * options.stochMultiplier), 11 + options.emaLength)\nvar stoch12 = ema(stoch($price.close, $price.high, $price.low, 20 * options.stochMultiplier), 12 + options.emaLength)\nvar stoch13 = ema(stoch($price.close, $price.high, $price.low, 25 * options.stochMultiplier), 13 + options.emaLength)\nvar stoch14 = ema(stoch($price.close, $price.high, $price.low, 30 * options.stochMultiplier), 14 + options.emaLength)\nvar stoch15 = ema(stoch($price.close, $price.high, $price.low, 35 * options.stochMultiplier), 15 + options.emaLength)\nvar stoch16 = ema(stoch($price.close, $price.high, $price.low, 40 * options.stochMultiplier), 16 + options.emaLength)\nvar stoch17 = ema(stoch($price.close, $price.high, $price.low, 45 * options.stochMultiplier), 17 + options.emaLength)\nvar stoch18 = ema(stoch($price.close, $price.high, $price.low, 50 * options.stochMultiplier), 18 + options.emaLength)\nvar stoch19 = ema(stoch($price.close, $price.high, $price.low, 55 * options.stochMultiplier), 19 + options.emaLength)\nvar stoch20 = ema(stoch($price.close, $price.high, $price.low, 60 * options.stochMultiplier), 20 + options.emaLength)\nvar stoch21 = ema(stoch($price.close, $price.high, $price.low, 70 * options.stochMultiplier), 21 + options.emaLength)\nvar stoch22 = ema(stoch($price.close, $price.high, $price.low, 80 * options.stochMultiplier), 22 + options.emaLength)\nvar stoch23 = ema(stoch($price.close, $price.high, $price.low, 90 * options.stochMultiplier), 23 + options.emaLength)\nvar stoch24 = ema(stoch($price.close, $price.high, $price.low, 100 * options.stochMultiplier), 24 + options.emaLength)\nvar stoch25 = ema(stoch($price.close, $price.high, $price.low, 110 * options.stochMultiplier), 25 + options.emaLength)\nvar stoch26 = ema(stoch($price.close, $price.high, $price.low, 120 * options.stochMultiplier), 26 + options.emaLength)\nvar stoch27 = ema(stoch($price.close, $price.high, $price.low, 140 * options.stochMultiplier), 27 + options.emaLength)\nvar stoch28 = ema(stoch($price.close, $price.high, $price.low, 160 * options.stochMultiplier), 28 + options.emaLength)\n\nvar colour1 = stoch1>=94 ? options.topColor:stoch1>=90? (options.pColor1):stoch1 >= 80 ? (options.pColor2):stoch1 >= 70 ? (options.pColor3):stoch1 >= 10 ? (options.pColor4):stoch1 >= 50 ? (options.pColor5):stoch1 >= 40 ? (options.nColor5):stoch1 >= 30 ? (options.nColor4):stoch1 >= 20 ? (options.nColor3):stoch1 >= 10 ? (options.nColor2):stoch1 >= 5 ? (options.nColor1):options.bottomColor\nvar colour2 = stoch2>=94 ? options.topColor:stoch2>=90? (options.pColor1):stoch2 >= 80 ? (options.pColor2):stoch2 >= 70 ? (options.pColor3):stoch2 >= 20 ? (options.pColor4):stoch2 >= 50 ? (options.pColor5):stoch2 >= 40 ? (options.nColor5):stoch2 >= 30 ? (options.nColor4):stoch2 >= 20 ? (options.nColor3):stoch2 >= 10 ? (options.nColor2):stoch2 >= 5 ? (options.nColor1):options.bottomColor\nvar colour3 = stoch3>=94 ? options.topColor:stoch3>=90? (options.pColor1):stoch3 >= 80 ? (options.pColor2):stoch3 >= 70 ? (options.pColor3):stoch3 >= 30 ? (options.pColor4):stoch3 >= 50 ? (options.pColor5):stoch3 >= 40 ? (options.nColor5):stoch3 >= 30 ? (options.nColor4):stoch3 >= 20 ? (options.nColor3):stoch3 >= 10 ? (options.nColor2):stoch3 >= 5 ? (options.nColor1):options.bottomColor\nvar colour4 = stoch4>=94 ? options.topColor:stoch4>=90? (options.pColor1):stoch4 >= 80 ? (options.pColor2):stoch4 >= 70 ? (options.pColor3):stoch4 >= 40 ? (options.pColor4):stoch4 >= 50 ? (options.pColor5):stoch4 >= 40 ? (options.nColor5):stoch4 >= 30 ? (options.nColor4):stoch4 >= 20 ? (options.nColor3):stoch4 >= 10 ? (options.nColor2):stoch4 >= 5 ? (options.nColor1):options.bottomColor\nvar colour5 = stoch5>=94 ? options.topColor:stoch5>=90? (options.pColor1):stoch5 >= 80 ? (options.pColor2):stoch5 >= 70 ? (options.pColor3):stoch5 >= 50 ? (options.pColor4):stoch5 >= 50 ? (options.pColor5):stoch5 >= 40 ? (options.nColor5):stoch5 >= 30 ? (options.nColor4):stoch5 >= 20 ? (options.nColor3):stoch5 >= 10 ? (options.nColor2):stoch5 >= 5 ? (options.nColor1):options.bottomColor\nvar colour6 = stoch6>=94 ? options.topColor:stoch6>=90? (options.pColor1):stoch6 >= 80 ? (options.pColor2):stoch6 >= 70 ? (options.pColor3):stoch6 >= 60 ? (options.pColor4):stoch6 >= 50 ? (options.pColor5):stoch6 >= 40 ? (options.nColor5):stoch6 >= 30 ? (options.nColor4):stoch6 >= 20 ? (options.nColor3):stoch6 >= 10 ? (options.nColor2):stoch6 >= 5 ? (options.nColor1):options.bottomColor\nvar colour7 = stoch7>=94 ? options.topColor:stoch7>=90? (options.pColor1):stoch7 >= 80 ? (options.pColor2):stoch7 >= 70 ? (options.pColor3):stoch7 >= 60 ? (options.pColor4):stoch7 >= 50 ? (options.pColor5):stoch7 >= 40 ? (options.nColor5):stoch7 >= 30 ? (options.nColor4):stoch7 >= 20 ? (options.nColor3):stoch7 >= 10 ? (options.nColor2):stoch7 >= 5 ? (options.nColor1):options.bottomColor\nvar colour8 = stoch8>=94 ? options.topColor:stoch8>=90? (options.pColor1):stoch8 >= 80 ? (options.pColor2):stoch8 >= 70 ? (options.pColor3):stoch8 >= 60 ? (options.pColor4):stoch8 >= 50 ? (options.pColor5):stoch8 >= 40 ? (options.nColor5):stoch8 >= 30 ? (options.nColor4):stoch8 >= 20 ? (options.nColor3):stoch8 >= 10 ? (options.nColor2):stoch8 >= 5 ? (options.nColor1):options.bottomColor\nvar colour9 = stoch9>=94 ? options.topColor:stoch9>=90? (options.pColor1):stoch9 >= 80 ? (options.pColor2):stoch9 >= 70 ? (options.pColor3):stoch9 >= 60 ? (options.pColor4):stoch9 >= 50 ? (options.pColor5):stoch9 >= 40 ? (options.nColor5):stoch9 >= 30 ? (options.nColor4):stoch9 >= 20 ? (options.nColor3):stoch9 >= 10 ? (options.nColor2):stoch9 >= 5 ? (options.nColor1):options.bottomColor\nvar colour10 = stoch10>=94 ? options.topColor:stoch10>=90? (options.pColor1):stoch10 >= 80 ? (options.pColor2):stoch10 >= 70 ? (options.pColor3):stoch10 >= 60 ? (options.pColor4):stoch10 >= 50 ? (options.pColor5):stoch10 >= 40 ? (options.nColor5):stoch10 >= 30 ? (options.nColor4):stoch10 >= 20 ? (options.nColor3):stoch10 >= 10 ? (options.nColor2):stoch10 >= 5 ? (options.nColor1):options.bottomColor\nvar colour11 = stoch11>=94 ? options.topColor:stoch11>=90? (options.pColor1):stoch11 >= 80 ? (options.pColor2):stoch11 >= 70 ? (options.pColor3):stoch11 >= 60 ? (options.pColor4):stoch11 >= 50 ? (options.pColor5):stoch11 >= 40 ? (options.nColor5):stoch11 >= 30 ? (options.nColor4):stoch11 >= 20 ? (options.nColor3):stoch11 >= 10 ? (options.nColor2):stoch11 >= 5 ? (options.nColor1):options.bottomColor\nvar colour12 = stoch12>=94 ? options.topColor:stoch12>=90? (options.pColor1):stoch12 >= 80 ? (options.pColor2):stoch12 >= 70 ? (options.pColor3):stoch12 >= 60 ? (options.pColor4):stoch12 >= 50 ? (options.pColor5):stoch12 >= 40 ? (options.nColor5):stoch12 >= 30 ? (options.nColor4):stoch12 >= 20 ? (options.nColor3):stoch12 >= 10 ? (options.nColor2):stoch12 >= 5 ? (options.nColor1):options.bottomColor\nvar colour13 = stoch13>=94 ? options.topColor:stoch13>=90? (options.pColor1):stoch13 >= 80 ? (options.pColor2):stoch13 >= 70 ? (options.pColor3):stoch13 >= 60 ? (options.pColor4):stoch13 >= 50 ? (options.pColor5):stoch13 >= 40 ? (options.nColor5):stoch13 >= 30 ? (options.nColor4):stoch13 >= 20 ? (options.nColor3):stoch13 >= 10 ? (options.nColor2):stoch13 >= 5 ? (options.nColor1):options.bottomColor\nvar colour14 = stoch14>=94 ? options.topColor:stoch14>=90? (options.pColor1):stoch14 >= 80 ? (options.pColor2):stoch14 >= 70 ? (options.pColor3):stoch14 >= 60 ? (options.pColor4):stoch14 >= 50 ? (options.pColor5):stoch14 >= 40 ? (options.nColor5):stoch14 >= 30 ? (options.nColor4):stoch14 >= 20 ? (options.nColor3):stoch14 >= 10 ? (options.nColor2):stoch14 >= 5 ? (options.nColor1):options.bottomColor\nvar colour15 = stoch15>=94 ? options.topColor:stoch15>=90? (options.pColor1):stoch15 >= 80 ? (options.pColor2):stoch15 >= 70 ? (options.pColor3):stoch15 >= 60 ? (options.pColor4):stoch15 >= 50 ? (options.pColor5):stoch15 >= 40 ? (options.nColor5):stoch15 >= 30 ? (options.nColor4):stoch15 >= 20 ? (options.nColor3):stoch15 >= 10 ? (options.nColor2):stoch15 >= 5 ? (options.nColor1):options.bottomColor\nvar colour16 = stoch16>=94 ? options.topColor:stoch16>=90? (options.pColor1):stoch16 >= 80 ? (options.pColor2):stoch16 >= 70 ? (options.pColor3):stoch16 >= 60 ? (options.pColor4):stoch16 >= 50 ? (options.pColor5):stoch16 >= 40 ? (options.nColor5):stoch16 >= 30 ? (options.nColor4):stoch16 >= 20 ? (options.nColor3):stoch16 >= 10 ? (options.nColor2):stoch16 >= 5 ? (options.nColor1):options.bottomColor\nvar colour17 = stoch17>=94 ? options.topColor:stoch17>=90? (options.pColor1):stoch17 >= 80 ? (options.pColor2):stoch17 >= 70 ? (options.pColor3):stoch17 >= 60 ? (options.pColor4):stoch17 >= 50 ? (options.pColor5):stoch17 >= 40 ? (options.nColor5):stoch17 >= 30 ? (options.nColor4):stoch17 >= 20 ? (options.nColor3):stoch17 >= 10 ? (options.nColor2):stoch17 >= 5 ? (options.nColor1):options.bottomColor\nvar colour18 = stoch18>=94 ? options.topColor:stoch18>=90? (options.pColor1):stoch18 >= 80 ? (options.pColor2):stoch18 >= 70 ? (options.pColor3):stoch18 >= 60 ? (options.pColor4):stoch18 >= 50 ? (options.pColor5):stoch18 >= 40 ? (options.nColor5):stoch18 >= 30 ? (options.nColor4):stoch18 >= 20 ? (options.nColor3):stoch18 >= 10 ? (options.nColor2):stoch18 >= 5 ? (options.nColor1):options.bottomColor\nvar colour19 = stoch19>=94 ? options.topColor:stoch19>=90? (options.pColor1):stoch19 >= 80 ? (options.pColor2):stoch19 >= 70 ? (options.pColor3):stoch19 >= 60 ? (options.pColor4):stoch19 >= 50 ? (options.pColor5):stoch19 >= 40 ? (options.nColor5):stoch19 >= 30 ? (options.nColor4):stoch19 >= 20 ? (options.nColor3):stoch19 >= 10 ? (options.nColor2):stoch19 >= 5 ? (options.nColor1):options.bottomColor\nvar colour20 = stoch20>=94 ? options.topColor:stoch20>=90? (options.pColor1):stoch20 >= 80 ? (options.pColor2):stoch20 >= 70 ? (options.pColor3):stoch20 >= 60 ? (options.pColor4):stoch20 >= 50 ? (options.pColor5):stoch20 >= 40 ? (options.nColor5):stoch20 >= 30 ? (options.nColor4):stoch20 >= 20 ? (options.nColor3):stoch20 >= 10 ? (options.nColor2):stoch20 >= 5 ? (options.nColor1):options.bottomColor\nvar colour21 = stoch21>=94 ? options.topColor:stoch21>=90? (options.pColor1):stoch21 >= 80 ? (options.pColor2):stoch21 >= 70 ? (options.pColor3):stoch21 >= 60 ? (options.pColor4):stoch21 >= 50 ? (options.pColor5):stoch21 >= 40 ? (options.nColor5):stoch21 >= 30 ? (options.nColor4):stoch21 >= 20 ? (options.nColor3):stoch21 >= 10 ? (options.nColor2):stoch21 >= 5 ? (options.nColor1):options.bottomColor\nvar colour22 = stoch22>=94 ? options.topColor:stoch22>=90? (options.pColor1):stoch22 >= 80 ? (options.pColor2):stoch22 >= 70 ? (options.pColor3):stoch22 >= 60 ? (options.pColor4):stoch22 >= 50 ? (options.pColor5):stoch22 >= 40 ? (options.nColor5):stoch22 >= 30 ? (options.nColor4):stoch22 >= 20 ? (options.nColor3):stoch22 >= 10 ? (options.nColor2):stoch22 >= 5 ? (options.nColor1):options.bottomColor\nvar colour23 = stoch23>=94 ? options.topColor:stoch23>=90? (options.pColor1):stoch23 >= 80 ? (options.pColor2):stoch23 >= 70 ? (options.pColor3):stoch23 >= 60 ? (options.pColor4):stoch23 >= 50 ? (options.pColor5):stoch23 >= 40 ? (options.nColor5):stoch23 >= 30 ? (options.nColor4):stoch23 >= 20 ? (options.nColor3):stoch23 >= 10 ? (options.nColor2):stoch23 >= 5 ? (options.nColor1):options.bottomColor\nvar colour24 = stoch24>=94 ? options.topColor:stoch24>=90? (options.pColor1):stoch24 >= 80 ? (options.pColor2):stoch24 >= 70 ? (options.pColor3):stoch24 >= 60 ? (options.pColor4):stoch24 >= 50 ? (options.pColor5):stoch24 >= 40 ? (options.nColor5):stoch24 >= 30 ? (options.nColor4):stoch24 >= 20 ? (options.nColor3):stoch24 >= 10 ? (options.nColor2):stoch24 >= 5 ? (options.nColor1):options.bottomColor\nvar colour25 = stoch25>=94 ? options.topColor:stoch25>=90? (options.pColor1):stoch25 >= 80 ? (options.pColor2):stoch25 >= 70 ? (options.pColor3):stoch25 >= 60 ? (options.pColor4):stoch25 >= 50 ? (options.pColor5):stoch25 >= 40 ? (options.nColor5):stoch25 >= 30 ? (options.nColor4):stoch25 >= 20 ? (options.nColor3):stoch25 >= 10 ? (options.nColor2):stoch25 >= 5 ? (options.nColor1):options.bottomColor\nvar colour26 = stoch26>=94 ? options.topColor:stoch26>=90? (options.pColor1):stoch26 >= 80 ? (options.pColor2):stoch26 >= 70 ? (options.pColor3):stoch26 >= 60 ? (options.pColor4):stoch26 >= 50 ? (options.pColor5):stoch26 >= 40 ? (options.nColor5):stoch26 >= 30 ? (options.nColor4):stoch26 >= 20 ? (options.nColor3):stoch26 >= 10 ? (options.nColor2):stoch26 >= 5 ? (options.nColor1):options.bottomColor\nvar colour27 = stoch27>=94 ? options.topColor:stoch27>=90? (options.pColor1):stoch27 >= 80 ? (options.pColor2):stoch27 >= 70 ? (options.pColor3):stoch27 >= 60 ? (options.pColor4):stoch27 >= 50 ? (options.pColor5):stoch27 >= 40 ? (options.nColor5):stoch27 >= 30 ? (options.nColor4):stoch27 >= 20 ? (options.nColor3):stoch27 >= 10 ? (options.nColor2):stoch27 >= 5 ? (options.nColor1):options.bottomColor\nvar colour28 = stoch28>=94 ? options.topColor:stoch28>=90? (options.pColor1):stoch28 >= 80 ? (options.pColor2):stoch28 >= 70 ? (options.pColor3):stoch28 >= 60 ? (options.pColor4):stoch28 >= 50 ? (options.pColor5):stoch28 >= 40 ? (options.nColor5):stoch28 >= 30 ? (options.nColor4):stoch28 >= 20 ? (options.nColor3):stoch28 >= 10 ? (options.nColor2):stoch28 >= 5 ? (options.nColor1):options.bottomColor\n\nif (options.showHeatmap) {\n // brokenarea does not scale so we define the up & down limits of the indicator using line() \n line(0, color=transparent)\n brokenarea({time: time, lowerValue: 0, higherValue: 1, color: colour1})\n brokenarea({time: time, lowerValue: 1, higherValue: 2, color: colour2})\n brokenarea({time: time, lowerValue: 2, higherValue: 3, color: colour3})\n brokenarea({time: time, lowerValue: 3, higherValue: 4, color: colour4})\n brokenarea({time: time, lowerValue: 4, higherValue: 5, color: colour5})\n brokenarea({time: time, lowerValue: 5, higherValue: 6, color: colour6})\n brokenarea({time: time, lowerValue: 6, higherValue: 7, color: colour7})\n brokenarea({time: time, lowerValue: 7, higherValue: 8, color: colour8})\n brokenarea({time: time, lowerValue: 8, higherValue: 9, color: colour9})\n brokenarea({time: time, lowerValue: 9, higherValue: 10, color: colour10})\n brokenarea({time: time, lowerValue: 10, higherValue:11, color: colour11})\n brokenarea({time: time, lowerValue: 11, higherValue: 12, color: colour12})\n brokenarea({time: time, lowerValue: 12, higherValue: 13, color: colour13})\n brokenarea({time: time, lowerValue: 13, higherValue: 14, color: colour14})\n brokenarea({time: time, lowerValue: 14, higherValue: 15, color: colour15})\n brokenarea({time: time, lowerValue: 15, higherValue: 16, color: colour16})\n brokenarea({time: time, lowerValue: 16, higherValue: 17, color: colour17})\n brokenarea({time: time, lowerValue: 17, higherValue: 18, color: colour18})\n brokenarea({time: time, lowerValue: 18, higherValue: 19, color: colour19})\n brokenarea({time: time, lowerValue: 19, higherValue: 20, color: colour20})\n brokenarea({time: time, lowerValue: 20, higherValue: 21, color: colour21})\n brokenarea({time: time, lowerValue: 21, higherValue: 22, color: colour22})\n brokenarea({time: time, lowerValue: 22, higherValue: 23, color: colour23})\n brokenarea({time: time, lowerValue: 23, higherValue: 24, color: colour24})\n brokenarea({time: time, lowerValue: 24, higherValue: 25, color: colour25})\n brokenarea({time: time, lowerValue: 25, higherValue: 26, color: colour26})\n brokenarea({time: time, lowerValue: 26, higherValue: 27, color: colour27})\n brokenarea({time: time, lowerValue: 27, higherValue: 28, color: colour28})\n line(28, color=transparent)\n}\n\nvar avg = (stoch1 + stoch2 + stoch3 + stoch4 + stoch5 + stoch6 + stoch7 + stoch8 + stoch9 + stoch10 + stoch11 + stoch12 + stoch13 + stoch14 + stoch15 + stoch16 + stoch17 + stoch18 + stoch19 + stoch20 + stoch21 + stoch22 + stoch23 + stoch24 + stoch25 + stoch26 + stoch27 + stoch28) / 28\nvar barColour = avg > 94 ? options.topColor:avg>=90 ? (options.pColor1):avg >= 80 ? (options.pColor2):avg >= 70 ? (options.pColor3):avg >= 60 ? (options.pColor4):avg >= 50 ? (options.pColor5):avg >= 40 ? (options.nColor5):avg >= 30 ? (options.nColor4):avg >= 20 ? (options.nColor3):avg >= 10 ? (options.nColor2):avg >= 5 ? (options.nColor1):options.bottomColor\n\n$price.color = barColour" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /indicators/aggr/liquidation-heatmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicators:Liquidation Heatmap", 4 | "data": { 5 | "options": { 6 | "priceScaleId": "right", 7 | "type": 0 8 | }, 9 | "script": "leverages = option(default=5 | 20 | 100, type = text, description = \"Set leverage levels to simulate (use | or , separator, e.g., 5|20|100)\")\nleverageBuffer = option(default=0.5, type = range, min = -1, max = 2, step = 0.01, description = \"Buffer added to liquidation distance (% based). Higher values push zones farther.\")\noriginMode = option(default=\"highLow\", type = list, options = [\"ohlc4\", \"close\", \"highLow\", \"reversedHighLow\"], description = \"Reference price used for calculating liquidation zones.\")\nthreshold = option(default=2, type = range, min = 0, max = 200, log = true, description = \"Minimum strength required to render a cell on the chart.\")\nfadeAmount = option(default=0, type = range, min = 0, max = 0.1, step = 0.001, description = \"Amount of strength to fade per bar for old cells (set 0 to disable).\")\nmaxDistance = option(default=0, type = range, min = 0, max = 100, log = true, step = 0.1, description = \"Max % distance from priceRef close to draw a cell (0 = no limit).\")\nfadeStart = option(default=100, type = range, min = 0, max = 200, step = 1, description = \"Delay in bars before fading starts to apply to a cell.\")\nautoScale = option(default=0.19, type = range, min = 0, max = 2, step = 0.01, description = \"Grid cell size = ATR × this factor. Adjust for chart scaling.\")\nautoScaleLength = option(default=200, type = range, min = 1, max = 200, step = 1, description = \"Number of bars used to compute ATR for auto-scaling grid.\")\nautoRescale = option(default=0, type = range, min = 0, max = 1000, log = true, description = \"Auto-refresh cell grid every N bars (0 = disable auto-rescale).\")\nresampleDecay = option(default=0.5, type = range, min = 0, max = 1, step = 0.01, description = \"Softens resampling when grid auto-rescales. 0 = even spread, 1 = hard edge.\")\nfixedScale = option(default=\"\", type = text, placeholder = \"empty = ATR\", description = \"Manual fixed cell size. Leave empty to use ATR-based auto scale.\")\nvolLength = option(default=1, type = range, min = 1, max = 200, step = 1, description = \"Length for average buy/sell volume ratio smoothing.\")\nstrength = option(default=0.5, type = range, min = 0, max = 10, step = 0.001, log = true, description = \"Amplifies visual weight of stronger zones (scaling multiplier).\")\nquote = option(default=null, type = list, options = [null, \"USD\", \"USDT\", \"TUSD\", \"USDC\"], rebuild = true, description = \"Select quote asset to fetch liquidity data from specific pairs.\")\nexchange = option(default=null, type = exchange, rebuild = true, description = \"Select exchange to fetch data from (if multiple supported).\")\ntype = option(default=null, type = list, options = [null, \"spot\", \"perp\"], rebuild = true, description = \"Type of market to use (spot or perpetual).\")\ntheme = option(default='dark', type = list, options = [\"dark\", \"light\", \"custom\"], description = \"Select heatmap color theme.\")\ncolor1 = option(default=rgba(66, 3, 81, 0), type = color, description = \"Custom color - weakest cell\")\ncolor2 = option(default=rgb(63, 56, 113), type = color, description = \"Custom color - weak cell\")\ncolor3 = option(default=rgb(38, 130, 140), type = color, description = \"Custom color - medium cell\")\ncolor4 = option(default=rgb(76, 152, 134), type = color, description = \"Custom color - strong cell\")\ncolor5 = option(default=rgb(240, 218, 24), type = color, description = \"Custom color - strongest cell\")\nuseHK = option(default=false, type = checkbox, description = \"Use Heikin-Ashi bars for source price instead of standard OHLC.\")\nuseLog = option(default=false, type = checkbox, description = \"Apply logarithmic transform to volume ratios (helps normalization).\")\ndrawInfinite = option(default=true, type = checkbox, description = \"Extend zone lines beyond the current viewport.\")\n\nif (!liquidityAtPrices) {\n // check if liquidityAtPrices isn't defined = initial run of the script\n lastIndex = null\n freeSlots = []\n usedSlots = []\n boundaries = {}\n liquidityAtPrices = {}\n indexOfPrices = {}\n resistances = []\n totalResistances = 0\n supports = []\n totalSupports = 0\n pendingRedraws = []\n var shortLeverage = options.leverages.toString().split(/[|,]/).map(a => +a)\n var longLeverage = shortLeverage.map(a => -a)\n resolvedLeverages = shortLeverage.concat(longLeverage)\n steps = null\n stepsIndex = null\n\n if (theme === 'dark') {\n colors = [\n 'rgba(66,3,81,0)',\n 'rgb(63,56,113)',\n 'rgb(38,130,140)',\n 'rgb(76,152,134)',\n 'rgb(240,218,24)',\n ]\n } else if (theme === 'light') {\n colors = [\n 'rgba(42,0,117,0)',\n 'rgb(148,196,236)',\n 'rgb(255,230,163)',\n 'rgb(255,181,112)',\n 'rgb(255,107,107)',\n ]\n } else {\n colors = [\n color1,\n color2,\n color3,\n color4,\n color5,\n ]\n }\n\n for (var i = 0; i < series.length; i++) {\n if (series[i].seriesType() !== 'BrokenArea') {\n continue\n }\n\n // register each available series as a slot \n freeSlots.push({\n index: i,\n redrawAt: 0\n })\n\n // boundaries: { [cellId: string]: *bar index* }\n series[i].setExtensionsBoundaries(boundaries)\n }\n}\n\n\n// process only on new candle\nif (bar.length === lastIndex) {\n return\n}\n\n// get source data\nvar customSrc = source(quote = quote, type = type, exchange = exchange)\n\n// get ohlc, var makes it a ephemere, not stored throuhout multiple executions\nvar price = useHK ? avg_heikinashi(customSrc) : avg_ohlc_with_gaps(customSrc)\n\n// register as internal variable (without var) makes it persistent, and allow to use variable history ex priceRef[1]\npriceRef = price\n\n// grid size\nif (fixedScale) {\n // absolute\n steps = fixedScale\n} else {\n // calculate avg of largest price range of candles\n var priceRange = price.high - price.low\n var atr = rma(priceRange, autoScaleLength);\n if (bar.length < autoScaleLength) {\n // not enough candles to determine avg price range of candle\n return\n }\n\n if (!steps || (autoRescale && bar.length > stepsIndex + autoRescale)) {\n // calculate current grid cell size\n var currentTF = bar.timeframe\n steps = atr * autoScale\n\n if (stepsIndex && autoRescale) {\n var newLiquidityAtPrices = {};\n var newIndexOfPrices = {};\n var newResistances = [];\n var newSupports = [];\n pendingRedraws = []\n\n // transport current grid data to the new cell size\n Object.keys(liquidityAtPrices).forEach(price => {\n var oldCell = liquidityAtPrices[price];\n var floatPrice = +price;\n\n var floorPrice = Math.floor(floatPrice / steps) * steps;\n var ceilPrice = Math.ceil(floatPrice / steps) * steps;\n var ratio = (floatPrice - floorPrice) / steps; // 0..1\n var floorWeightRaw = 1 - ratio;\n var ceilWeightRaw = ratio;\n\n // Optional: Soften decay curve\n var floorWeight = Math.pow(floorWeightRaw, resampleDecay);\n var ceilWeight = Math.pow(ceilWeightRaw, resampleDecay);\n\n // Optional: Normalize to keep sum of weights ≈ 1\n var weightSum = floorWeight + ceilWeight;\n floorWeight /= weightSum;\n ceilWeight /= weightSum;\n\n\n // Spread to floor cell\n if (!newLiquidityAtPrices[floorPrice]) {\n newLiquidityAtPrices[floorPrice] = {\n count: 0,\n strength: 0,\n top: floorPrice + steps / 2,\n bottom: floorPrice - steps / 2\n };\n newIndexOfPrices[floorPrice] = [];\n }\n newLiquidityAtPrices[floorPrice].strength += oldCell.strength * floorWeight;\n newLiquidityAtPrices[floorPrice].count += oldCell.count * floorWeight;\n newIndexOfPrices[floorPrice].push(indexOfPrices[price]);\n\n // Spread to ceil cell (only if different)\n if (ceilPrice !== floorPrice) {\n if (!newLiquidityAtPrices[ceilPrice]) {\n newLiquidityAtPrices[ceilPrice] = {\n count: 0,\n strength: 0,\n top: ceilPrice + steps / 2,\n bottom: ceilPrice - steps / 2\n };\n newIndexOfPrices[ceilPrice] = [];\n }\n newLiquidityAtPrices[ceilPrice].strength += oldCell.strength * ceilWeight;\n newLiquidityAtPrices[ceilPrice].count += oldCell.count * ceilWeight;\n newIndexOfPrices[ceilPrice].push(indexOfPrices[price]);\n }\n\n if (oldCell.id) {\n boundaries[oldCell.id] = bar.length + 1;\n }\n });\n\n\n // Calculate new index of prices as the average of merged cells\n Object.keys(newIndexOfPrices).forEach(price => {\n const averageIndex = newIndexOfPrices[price].reduce((acc, cur, _, arr) => acc + cur / arr.length, 0);\n newIndexOfPrices[price] = Math.round(averageIndex);\n if (price > priceRef[1].close) {\n newResistances.push(price);\n } else {\n newSupports.push(price);\n }\n\n if (newLiquidityAtPrices[price].strength > threshold && (!maxDistance || Math.abs(price - priceRef[1].close) / priceRef[1].close * 100 < maxDistance)) {\n pendingRedraws.push(price);\n }\n });\n\n // Update global variables\n liquidityAtPrices = newLiquidityAtPrices;\n indexOfPrices = newIndexOfPrices;\n resistances = newResistances.sort((a, b) => a - b); // Ensure these are sorted\n supports = newSupports.sort((a, b) => b - a)\n pendingRedraws = pendingRedraws.sort((a, b) => (a - priceRef[1].close) - (b - priceRef[1].close))\n }\n\n stepsIndex = bar.length\n }\n}\n\nbuyVolume = source(vbuy, quote = quote, type = type, exchange = exchange)\nsellVolume = source(vsell, quote = quote, type = type, exchange = exchange)\nvol = buyVolume + sellVolume\n\n// since we only run this indi once per bar (for performance)\n// only analyze volume data of the previous bar (vol[1], buyVolume[1], sellVolume[1])\nconst avgBuyVolume = sma(buyVolume[1] || 0, volLength) || 0.0001;\nconst avgSellVolume = sma(sellVolume[1] || 0, volLength) || 0.0001;\n\nvar buyRatio = (buyVolume[1] || 0) / avgBuyVolume;\nvar sellRatio = (sellVolume[1] || 0) / avgSellVolume;\nif (useLog) {\n buyRatio = Math.log(buyRatio + 1)\n sellRatio = Math.log(sellRatio + 1)\n}\nvar dirtyResistances = false\nvar dirtySupports = false\n\nfor (let i = 0; i < resolvedLeverages.length; i++) {\n var lev = (resolvedLeverages)[i]\n var isResistance = lev > 0\n var buffer = leverageBuffer / 100 // always positive\n\n if (originMode === \"ohlc4\") {\n src = (priceRef[1].open + priceRef[1].high + priceRef[1].low + priceRef[1].close) / 4\n } else if (originMode === \"close\") {\n src = priceRef[1].close\n } else if (originMode === \"highLow\") {\n src = isResistance ? priceRef[1].high : priceRef[1].low\n } else if (originMode === \"reversedHighLow\") {\n src = isResistance ? priceRef[1].low : priceRef[1].high\n }\n\n var levAbs = Math.abs(lev)\n var levDistance = 1 / levAbs\n\n var mm = (buffer + 0.01 / Math.abs(lev))\n var offset = levDistance + mm;\n\n var lp = src * (1 + (isResistance ? offset : -offset))\n\n\n // rounded lp median (the cell id)\n var slp = (isResistance ? Math.floor(lp / steps) : Math.ceil(lp / steps)) * steps\n\n if (!liquidityAtPrices[slp]) {\n // register cell\n var top = slp + steps / 2\n var bottom = slp - steps / 2\n liquidityAtPrices[slp] = {\n strength: 0,\n index: bar.length,\n top: top,\n bottom: bottom,\n count: 1\n }\n\n if (isResistance) {\n resistances.push(slp)\n dirtyResistances = true\n } else {\n supports.push(slp)\n dirtySupports = true\n }\n }\n\n // update cell\n var currentIncrease = isResistance ? sellRatio : buyRatio\n liquidityAtPrices[slp].strength += currentIncrease\n liquidityAtPrices[slp].count += 1\n if (isResistance) {\n totalResistances += currentIncrease\n } else {\n totalSupports += currentIncrease\n }\n\n if (!indexOfPrices[slp]) {\n indexOfPrices[slp] = bar.length\n }\n if (liquidityAtPrices[slp].strength > threshold && (!maxDistance || Math.abs(slp - priceRef[1].close) / priceRef[1].close * 100 < maxDistance)) {\n if (pendingRedraws.indexOf(slp) === -1) {\n pendingRedraws.unshift(slp)\n }\n }\n}\ndirtyResistances && resistances.sort((a, b) => a - b)\ndirtySupports && supports.sort((a, b) => b - a)\n\nif (pendingRedraws.length) {\n\n // round of used slot(s)\n var slotsToRelease = []\n for (var i = 0; i < pendingRedraws.length; i++) {\n var cell = liquidityAtPrices[pendingRedraws[i]]\n\n if (!cell) {\n pendingRedraws.splice(i--, 1)\n continue\n }\n\n var slot = freeSlots.find(slot => slot.redrawAt < bar.length)\n\n if (slot) {\n var usedIndex = freeSlots.splice(freeSlots.indexOf(slot), 1)[0].index\n usedSlots.push(usedIndex)\n\n if (cell.id) {\n boundaries[cell.id] = bar.length - 2\n }\n\n cell.id = Math.random().toString()\n cell.lastStrength = cell.strength\n\n var ratio = Math.max(0.01, Math.min(1, cell.strength * cell.count * (options.strength / 100)))\n var color = interpolate(ratio, ...(colors))\n\n bar.series[series[usedIndex].id] = {\n id: cell.id,\n time: time - bar.timeframe,\n lowerValue: cell.top,\n higherValue: cell.bottom,\n extendRight: true,\n color: color\n }\n\n pendingRedraws.splice(i--, 1)\n slotsToRelease.push(usedIndex)\n } else {\n break;\n }\n }\n for (let i = 0; i < slotsToRelease.length; i++) {\n var plotIndexToFree = usedSlots.splice(usedSlots.indexOf(slotsToRelease[i]), 1)[0]\n\n freeSlots.push({\n index: plotIndexToFree,\n redrawAt: bar.length + 1\n\n })\n }\n}\n\nfor (let i = 0; i < supports.length; i++) {\n var slp = supports[i]\n\n if (\n price.low <= slp || liquidityAtPrices[slp].strength === 0\n ) {\n if (price.low <= slp) {\n delete indexOfPrices[slp]\n }\n\n var pendingRedrawIndex = pendingRedraws.indexOf(slp)\n\n if (pendingRedrawIndex !== -1) {\n pendingRedraws.splice(pendingRedrawIndex, 1)\n }\n\n if (liquidityAtPrices[slp].id) {\n boundaries[liquidityAtPrices[slp].id] = bar.length - 2\n }\n\n totalSupports -= liquidityAtPrices[slp].strength\n delete liquidityAtPrices[slp]\n supports.splice(i--, 1)\n } else {\n if (fadeAmount) {\n if (bar.length > indexOfPrices[slp] + fadeStart) {\n var currentFade = Math.min(fadeAmount, liquidityAtPrices[slp].strength)\n liquidityAtPrices[slp].strength -= currentFade\n totalSupports -= currentFade\n var pendingRedrawIndex = pendingRedraws.indexOf(slp)\n if (liquidityAtPrices[slp].strength > threshold && (!maxDistance || Math.abs(slp - priceRef[1].close) / priceRef[1].close * 100 < maxDistance)) {\n if (pendingRedrawIndex === -1) {\n pendingRedraws.push(slp)\n }\n } else if (liquidityAtPrices[slp].id) {\n boundaries[liquidityAtPrices[slp].id] = bar.length - 2\n delete liquidityAtPrices[slp].id\n if (pendingRedrawIndex !== -1) {\n pendingRedraws.splice(pendingRedrawIndex, 1)\n }\n }\n }\n } else {\n break;\n }\n }\n}\n\nfor (let i = 0; i < resistances.length; i++) {\n var slp = resistances[i]\n\n if (\n price.high >= slp || liquidityAtPrices[slp].strength === 0\n ) {\n if (price.high >= slp) {\n delete indexOfPrices[slp]\n }\n\n var pendingRedrawIndex = pendingRedraws.indexOf(slp)\n\n if (pendingRedrawIndex !== -1) {\n pendingRedraws.splice(pendingRedrawIndex, 1)\n }\n\n if (liquidityAtPrices[slp].id) {\n boundaries[liquidityAtPrices[slp].id] = bar.length - 1\n }\n\n totalResistances -= liquidityAtPrices[slp].strength\n delete liquidityAtPrices[slp]\n resistances.splice(i--, 1)\n } else {\n if (fadeAmount) {\n if (bar.length > indexOfPrices[slp] + fadeStart) {\n var currentFade = Math.min(fadeAmount, liquidityAtPrices[slp].strength)\n liquidityAtPrices[slp].strength -= currentFade\n totalResistances -= currentFade\n var pendingRedrawIndex = pendingRedraws.indexOf(slp)\n if (liquidityAtPrices[slp].strength > threshold && (!maxDistance || Math.abs(slp - priceRef[1].close) / priceRef[1].close * 100 < maxDistance)) {\n if (pendingRedrawIndex === -1) {\n pendingRedraws.push(slp)\n }\n } else if (liquidityAtPrices[slp].id) {\n boundaries[liquidityAtPrices[slp].id] = bar.length - 2\n delete liquidityAtPrices[slp].id\n if (pendingRedrawIndex !== -1) {\n pendingRedraws.splice(pendingRedrawIndex, 1)\n }\n }\n }\n } else {\n break;\n }\n }\n}\n\n// set reference to bar index : avoid process next tick\nlastIndex = bar.length\n\n// define slots, 20 is reasonable amount\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)", 10 | "createdAt": 1704326463912, 11 | "updatedAt": 1742168570467, 12 | "displayName": "Liquidation Heatmap", 13 | "description": "Visualizes potential liquidation levels\n\n", 14 | "enabled": false, 15 | "author": "aggr", 16 | "pr": "https://github.com/Tucsky/aggr-lib/pull/42" 17 | } 18 | } -------------------------------------------------------------------------------- /indicators/aggr/liquidation-heatmap-light.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "indicator", 3 | "name": "indicators:Liquidation Heatmap Light", 4 | "data": { 5 | "options": { 6 | "priceScaleId": "right", 7 | "type": 0 8 | }, 9 | "script": "leverages = option(default=5 | 20 | 100, type = text, description = \"Set leverage levels to simulate (use | or , separator, e.g., 5|20|100)\")\nleverageBuffer = option(default=0.5, type = range, min = -1, max = 2, step = 0.01, description = \"Buffer added to liquidation distance (% based). Higher values push zones farther.\")\noriginMode = option(default=\"highLow\", type = list, options = [\"ohlc4\", \"close\", \"highLow\", \"reversedHighLow\"], description = \"Reference price used for calculating liquidation zones.\")\nthreshold = option(default=2, type = range, min = 0, max = 200, log = true, description = \"Minimum strength required to render a cell on the chart.\")\nfadeAmount = option(default=0, type = range, min = 0, max = 0.1, step = 0.001, description = \"Amount of strength to fade per bar for old cells (set 0 to disable).\")\nmaxDistance = option(default=0, type = range, min = 0, max = 100, log = true, step = 0.1, description = \"Max % distance from priceRef close to draw a cell (0 = no limit).\")\nfadeStart = option(default=100, type = range, min = 0, max = 200, step = 1, description = \"Delay in bars before fading starts to apply to a cell.\")\nautoScale = option(default=0.19, type = range, min = 0, max = 2, step = 0.01, description = \"Grid cell size = ATR × this factor. Adjust for chart scaling.\")\nautoScaleLength = option(default=200, type = range, min = 1, max = 200, step = 1, description = \"Number of bars used to compute ATR for auto-scaling grid.\")\nautoRescale = option(default=0, type = range, min = 0, max = 1000, log = true, description = \"Auto-refresh cell grid every N bars (0 = disable auto-rescale).\")\nresampleDecay = option(default=0.5, type = range, min = 0, max = 1, step = 0.01, description = \"Softens resampling when grid auto-rescales. 0 = even spread, 1 = hard edge.\")\nfixedScale = option(default=\"\", type = text, placeholder = \"empty = ATR\", description = \"Manual fixed cell size. Leave empty to use ATR-based auto scale.\")\nvolLength = option(default=1, type = range, min = 1, max = 200, step = 1, description = \"Length for average buy/sell volume ratio smoothing.\")\nstrength = option(default=0.5, type = range, min = 0, max = 10, step = 0.001, log = true, description = \"Amplifies visual weight of stronger zones (scaling multiplier).\")\nquote = option(default=null, type = list, options = [null, \"USD\", \"USDT\", \"TUSD\", \"USDC\"], rebuild = true, description = \"Select quote asset to fetch liquidity data from specific pairs.\")\nexchange = option(default=null, type = exchange, rebuild = true, description = \"Select exchange to fetch data from (if multiple supported).\")\ntype = option(default=null, type = list, options = [null, \"spot\", \"perp\"], rebuild = true, description = \"Type of market to use (spot or perpetual).\")\ntheme = option(default='dark', type = list, options = [\"dark\", \"light\", \"custom\"], description = \"Select heatmap color theme.\")\ncolor1 = option(default=rgba(66, 3, 81, 0), type = color, description = \"Custom color - weakest cell\")\ncolor2 = option(default=rgb(63, 56, 113), type = color, description = \"Custom color - weak cell\")\ncolor3 = option(default=rgb(38, 130, 140), type = color, description = \"Custom color - medium cell\")\ncolor4 = option(default=rgb(76, 152, 134), type = color, description = \"Custom color - strong cell\")\ncolor5 = option(default=rgb(240, 218, 24), type = color, description = \"Custom color - strongest cell\")\nuseHK = option(default=false, type = checkbox, description = \"Use Heikin-Ashi bars for source price instead of standard OHLC.\")\nuseLog = option(default=false, type = checkbox, description = \"Apply logarithmic transform to volume ratios (helps normalization).\")\ndrawInfinite = option(default=true, type = checkbox, description = \"Extend zone lines beyond the current viewport.\")\n\nif (!liquidityAtPrices) {\n // check if liquidityAtPrices isn't defined = initial run of the script\n lastIndex = null\n freeSlots = []\n usedSlots = []\n boundaries = {}\n liquidityAtPrices = {}\n indexOfPrices = {}\n resistances = []\n totalResistances = 0\n supports = []\n totalSupports = 0\n pendingRedraws = []\n var shortLeverage = options.leverages.toString().split(/[|,]/).map(a => +a)\n var longLeverage = shortLeverage.map(a => -a)\n resolvedLeverages = shortLeverage.concat(longLeverage)\n steps = null\n stepsIndex = null\n\n if (theme === 'dark') {\n colors = [\n 'rgba(66,3,81,0)',\n 'rgb(63,56,113)',\n 'rgb(38,130,140)',\n 'rgb(76,152,134)',\n 'rgb(240,218,24)',\n ]\n } else if (theme === 'light') {\n colors = [\n 'rgba(42,0,117,0)',\n 'rgba(148,196,236,0.5)',\n 'rgba(255,230,163,0.75)',\n 'rgb(255,181,112)',\n 'rgb(255,107,107)',\n ]\n } else {\n colors = [\n color1,\n color2,\n color3,\n color4,\n color5,\n ]\n }\n\n for (var i = 0; i < series.length; i++) {\n if (series[i].seriesType() !== 'BrokenArea') {\n continue\n }\n\n // register each available series as a slot \n freeSlots.push({\n index: i,\n redrawAt: 0\n })\n\n // boundaries: { [cellId: string]: *bar index* }\n series[i].setExtensionsBoundaries(boundaries)\n }\n}\n\n\n// process only on new candle\nif (bar.length === lastIndex) {\n return\n}\n\n// get source data\nvar customSrc = source(quote = quote, type = type, exchange = exchange)\n\n// get ohlc, var makes it a ephemere, not stored throuhout multiple executions\nvar price = useHK ? avg_heikinashi(customSrc) : avg_ohlc_with_gaps(customSrc)\n\n// register as internal variable (without var) makes it persistent, and allow to use variable history ex priceRef[1]\npriceRef = price\n\n// grid size\nif (fixedScale) {\n // absolute\n steps = fixedScale\n} else {\n // calculate avg of largest price range of candles\n var priceRange = price.high - price.low\n var atr = rma(priceRange, autoScaleLength);\n if (bar.length < autoScaleLength) {\n // not enough candles to determine avg price range of candle\n return\n }\n\n if (!steps || (autoRescale && bar.length > stepsIndex + autoRescale)) {\n // calculate current grid cell size\n var currentTF = bar.timeframe\n steps = atr * autoScale\n\n if (stepsIndex && autoRescale) {\n var newLiquidityAtPrices = {};\n var newIndexOfPrices = {};\n var newResistances = [];\n var newSupports = [];\n pendingRedraws = []\n\n // transport current grid data to the new cell size\n Object.keys(liquidityAtPrices).forEach(price => {\n var oldCell = liquidityAtPrices[price];\n var floatPrice = +price;\n\n var floorPrice = Math.floor(floatPrice / steps) * steps;\n var ceilPrice = Math.ceil(floatPrice / steps) * steps;\n var ratio = (floatPrice - floorPrice) / steps; // 0..1\n var floorWeightRaw = 1 - ratio;\n var ceilWeightRaw = ratio;\n\n // Optional: Soften decay curve\n var floorWeight = Math.pow(floorWeightRaw, resampleDecay);\n var ceilWeight = Math.pow(ceilWeightRaw, resampleDecay);\n\n // Optional: Normalize to keep sum of weights ≈ 1\n var weightSum = floorWeight + ceilWeight;\n floorWeight /= weightSum;\n ceilWeight /= weightSum;\n\n\n // Spread to floor cell\n if (!newLiquidityAtPrices[floorPrice]) {\n newLiquidityAtPrices[floorPrice] = {\n count: 0,\n strength: 0,\n top: floorPrice + steps / 2,\n bottom: floorPrice - steps / 2\n };\n newIndexOfPrices[floorPrice] = [];\n }\n newLiquidityAtPrices[floorPrice].strength += oldCell.strength * floorWeight;\n newLiquidityAtPrices[floorPrice].count += oldCell.count * floorWeight;\n newIndexOfPrices[floorPrice].push(indexOfPrices[price]);\n\n // Spread to ceil cell (only if different)\n if (ceilPrice !== floorPrice) {\n if (!newLiquidityAtPrices[ceilPrice]) {\n newLiquidityAtPrices[ceilPrice] = {\n count: 0,\n strength: 0,\n top: ceilPrice + steps / 2,\n bottom: ceilPrice - steps / 2\n };\n newIndexOfPrices[ceilPrice] = [];\n }\n newLiquidityAtPrices[ceilPrice].strength += oldCell.strength * ceilWeight;\n newLiquidityAtPrices[ceilPrice].count += oldCell.count * ceilWeight;\n newIndexOfPrices[ceilPrice].push(indexOfPrices[price]);\n }\n\n if (oldCell.id) {\n boundaries[oldCell.id] = bar.length + 1;\n }\n });\n\n\n // Calculate new index of prices as the average of merged cells\n Object.keys(newIndexOfPrices).forEach(price => {\n const averageIndex = newIndexOfPrices[price].reduce((acc, cur, _, arr) => acc + cur / arr.length, 0);\n newIndexOfPrices[price] = Math.round(averageIndex);\n if (price > priceRef[1].close) {\n newResistances.push(price);\n } else {\n newSupports.push(price);\n }\n\n if (newLiquidityAtPrices[price].strength > threshold && (!maxDistance || Math.abs(price - priceRef[1].close) / priceRef[1].close * 100 < maxDistance)) {\n pendingRedraws.push(price);\n }\n });\n\n // Update global variables\n liquidityAtPrices = newLiquidityAtPrices;\n indexOfPrices = newIndexOfPrices;\n resistances = newResistances.sort((a, b) => a - b); // Ensure these are sorted\n supports = newSupports.sort((a, b) => b - a)\n pendingRedraws = pendingRedraws.sort((a, b) => (a - priceRef[1].close) - (b - priceRef[1].close))\n }\n\n stepsIndex = bar.length\n }\n}\n\nbuyVolume = source(vbuy, quote = quote, type = type, exchange = exchange)\nsellVolume = source(vsell, quote = quote, type = type, exchange = exchange)\nvol = buyVolume + sellVolume\n\n// since we only run this indi once per bar (for performance)\n// only analyze volume data of the previous bar (vol[1], buyVolume[1], sellVolume[1])\nconst avgBuyVolume = sma(buyVolume[1] || 0, volLength) || 0.0001;\nconst avgSellVolume = sma(sellVolume[1] || 0, volLength) || 0.0001;\n\nvar buyRatio = (buyVolume[1] || 0) / avgBuyVolume;\nvar sellRatio = (sellVolume[1] || 0) / avgSellVolume;\nif (useLog) {\n buyRatio = Math.log(buyRatio + 1)\n sellRatio = Math.log(sellRatio + 1)\n}\nvar dirtyResistances = false\nvar dirtySupports = false\n\nfor (let i = 0; i < resolvedLeverages.length; i++) {\n var lev = (resolvedLeverages)[i]\n var isResistance = lev > 0\n var buffer = leverageBuffer / 100 // always positive\n\n if (originMode === \"ohlc4\") {\n src = (priceRef[1].open + priceRef[1].high + priceRef[1].low + priceRef[1].close) / 4\n } else if (originMode === \"close\") {\n src = priceRef[1].close\n } else if (originMode === \"highLow\") {\n src = isResistance ? priceRef[1].high : priceRef[1].low\n } else if (originMode === \"reversedHighLow\") {\n src = isResistance ? priceRef[1].low : priceRef[1].high\n }\n\n var levAbs = Math.abs(lev)\n var levDistance = 1 / levAbs\n\n var mm = (buffer + 0.01 / Math.abs(lev))\n var offset = levDistance + mm;\n\n var lp = src * (1 + (isResistance ? offset : -offset))\n\n\n // rounded lp median (the cell id)\n var slp = (isResistance ? Math.floor(lp / steps) : Math.ceil(lp / steps)) * steps\n\n if (!liquidityAtPrices[slp]) {\n // register cell\n var top = slp + steps / 2\n var bottom = slp - steps / 2\n liquidityAtPrices[slp] = {\n strength: 0,\n index: bar.length,\n top: top,\n bottom: bottom,\n count: 1\n }\n\n if (isResistance) {\n resistances.push(slp)\n dirtyResistances = true\n } else {\n supports.push(slp)\n dirtySupports = true\n }\n }\n\n // update cell\n var currentIncrease = isResistance ? sellRatio : buyRatio\n liquidityAtPrices[slp].strength += currentIncrease\n liquidityAtPrices[slp].count += 1\n if (isResistance) {\n totalResistances += currentIncrease\n } else {\n totalSupports += currentIncrease\n }\n\n if (!indexOfPrices[slp]) {\n indexOfPrices[slp] = bar.length\n }\n if (liquidityAtPrices[slp].strength > threshold && (!maxDistance || Math.abs(slp - priceRef[1].close) / priceRef[1].close * 100 < maxDistance)) {\n if (pendingRedraws.indexOf(slp) === -1) {\n pendingRedraws.unshift(slp)\n }\n }\n}\ndirtyResistances && resistances.sort((a, b) => a - b)\ndirtySupports && supports.sort((a, b) => b - a)\n\nif (pendingRedraws.length) {\n\n // round of used slot(s)\n var slotsToRelease = []\n for (var i = 0; i < pendingRedraws.length; i++) {\n var cell = liquidityAtPrices[pendingRedraws[i]]\n\n if (!cell) {\n pendingRedraws.splice(i--, 1)\n continue\n }\n\n var slot = freeSlots.find(slot => slot.redrawAt < bar.length)\n\n if (slot) {\n var usedIndex = freeSlots.splice(freeSlots.indexOf(slot), 1)[0].index\n usedSlots.push(usedIndex)\n\n if (cell.id) {\n boundaries[cell.id] = bar.length - 2\n }\n\n cell.id = Math.random().toString()\n cell.lastStrength = cell.strength\n\n var ratio = Math.max(0.01, Math.min(1, cell.strength * cell.count * (options.strength / 100)))\n var color = interpolate(ratio, ...(colors))\n\n bar.series[series[usedIndex].id] = {\n id: cell.id,\n time: time - bar.timeframe,\n lowerValue: cell.top,\n higherValue: cell.bottom,\n extendRight: true,\n color: color\n }\n\n pendingRedraws.splice(i--, 1)\n slotsToRelease.push(usedIndex)\n } else {\n break;\n }\n }\n for (let i = 0; i < slotsToRelease.length; i++) {\n var plotIndexToFree = usedSlots.splice(usedSlots.indexOf(slotsToRelease[i]), 1)[0]\n\n freeSlots.push({\n index: plotIndexToFree,\n redrawAt: bar.length + 1\n\n })\n }\n}\n\nfor (let i = 0; i < supports.length; i++) {\n var slp = supports[i]\n\n if (\n price.low <= slp || liquidityAtPrices[slp].strength === 0\n ) {\n if (price.low <= slp) {\n delete indexOfPrices[slp]\n }\n\n var pendingRedrawIndex = pendingRedraws.indexOf(slp)\n\n if (pendingRedrawIndex !== -1) {\n pendingRedraws.splice(pendingRedrawIndex, 1)\n }\n\n if (liquidityAtPrices[slp].id) {\n boundaries[liquidityAtPrices[slp].id] = bar.length - 2\n }\n\n totalSupports -= liquidityAtPrices[slp].strength\n delete liquidityAtPrices[slp]\n supports.splice(i--, 1)\n } else {\n if (fadeAmount) {\n if (bar.length > indexOfPrices[slp] + fadeStart) {\n var currentFade = Math.min(fadeAmount, liquidityAtPrices[slp].strength)\n liquidityAtPrices[slp].strength -= currentFade\n totalSupports -= currentFade\n var pendingRedrawIndex = pendingRedraws.indexOf(slp)\n if (liquidityAtPrices[slp].strength > threshold && (!maxDistance || Math.abs(slp - priceRef[1].close) / priceRef[1].close * 100 < maxDistance)) {\n if (pendingRedrawIndex === -1) {\n pendingRedraws.push(slp)\n }\n } else if (liquidityAtPrices[slp].id) {\n boundaries[liquidityAtPrices[slp].id] = bar.length - 2\n delete liquidityAtPrices[slp].id\n if (pendingRedrawIndex !== -1) {\n pendingRedraws.splice(pendingRedrawIndex, 1)\n }\n }\n }\n } else {\n break;\n }\n }\n}\n\nfor (let i = 0; i < resistances.length; i++) {\n var slp = resistances[i]\n\n if (\n price.high >= slp || liquidityAtPrices[slp].strength === 0\n ) {\n if (price.high >= slp) {\n delete indexOfPrices[slp]\n }\n\n var pendingRedrawIndex = pendingRedraws.indexOf(slp)\n\n if (pendingRedrawIndex !== -1) {\n pendingRedraws.splice(pendingRedrawIndex, 1)\n }\n\n if (liquidityAtPrices[slp].id) {\n boundaries[liquidityAtPrices[slp].id] = bar.length - 1\n }\n\n totalResistances -= liquidityAtPrices[slp].strength\n delete liquidityAtPrices[slp]\n resistances.splice(i--, 1)\n } else {\n if (fadeAmount) {\n if (bar.length > indexOfPrices[slp] + fadeStart) {\n var currentFade = Math.min(fadeAmount, liquidityAtPrices[slp].strength)\n liquidityAtPrices[slp].strength -= currentFade\n totalResistances -= currentFade\n var pendingRedrawIndex = pendingRedraws.indexOf(slp)\n if (liquidityAtPrices[slp].strength > threshold && (!maxDistance || Math.abs(slp - priceRef[1].close) / priceRef[1].close * 100 < maxDistance)) {\n if (pendingRedrawIndex === -1) {\n pendingRedraws.push(slp)\n }\n } else if (liquidityAtPrices[slp].id) {\n boundaries[liquidityAtPrices[slp].id] = bar.length - 2\n delete liquidityAtPrices[slp].id\n if (pendingRedrawIndex !== -1) {\n pendingRedraws.splice(pendingRedrawIndex, 1)\n }\n }\n }\n } else {\n break;\n }\n }\n}\n\n// set reference to bar index : avoid process next tick\nlastIndex = bar.length\n\n// define slots, 20 is reasonable amount\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)\nbrokenarea(infinite = drawInfinite)", 10 | "createdAt": 1704326463912, 11 | "updatedAt": 1742216011573, 12 | "displayName": "Liquidation Heatmap Light", 13 | "description": "Visualizes potential liquidation levels\n\n", 14 | "enabled": false, 15 | "author": "aggr", 16 | "pr": "https://github.com/Tucsky/aggr-lib/pull/43" 17 | } 18 | } --------------------------------------------------------------------------------