Trapezoidal Weighted Moving Average and Bollinger Bands
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © rumpypumpydumpy

//@version=5
indicator("Trapezoidal Weighted Moving Average and Bollinger Bands", shorttitle = "TWMA+BB", overlay = true, timeframe = "", timeframe_gaps = true)

len = input.int(100, title = "Length")
src = input.source(close, title = "source")
perc = input.float(15, title = "Weighted percentage", minval = 0, maxval = 50, tooltip = "Percentage of values to linearly weight at the beginning and the end of the rolling window") / 100
stdev_mult = input.float(2.000, title = "stdev mult")

var float[] price_vals = array.new_float()
var float[] weights = array.new_float(len)
var float factor = 1 / (len * perc)
var int linear_len = math.floor(len * perc)

if barstate.isfirst
    for i = 0 to linear_len - 1
        weight = factor * (i + 1)
        array.set(weights, i, weight)
        array.set(weights, len - 1 - i, (i + 1) * factor)
    for i = linear_len to len - linear_len - 1
        array.set(weights, i, 1)

array.unshift(price_vals, src)
if array.size(price_vals) > len
    array.pop(price_vals)

float wtd_sum = 0.0
float denom = 0.0
float twma = na
float stdev = na

if bar_index >= len - 1
    for i = 0 to len - 1
        weight = array.get(weights, i)
        wtd_sum += array.get(price_vals, i) * weight
        denom += weight
    twma := wtd_sum / denom    
    float err_sum = 0.0
    for j = 0 to len - 1
        err_sum += math.pow(twma - array.get(price_vals, j), 2) * array.get(weights, j)
    stdev := math.sqrt(err_sum / denom)

up = twma + stdev_mult * stdev
dn = twma - stdev_mult * stdev

plot(twma, title = "TWMA")
plot(up, title = "TWMA : Upper BB")
plot(dn, title = "TWMA : Lower BB")
