Roofing Filter [DW]
"Roofing Filter [DW]" by DonovanWall, MPL-2.0. Ported to FloScript by FloSignals; modifications under MPL-2.0. Original: Roofing Filter [DW] (v2.0), read 2026-10-12T15:58:56.341Z; source language v4.
Licence: Mozilla Public License 2.0 (MPL-2.0) — full text. The source is published with it, modifications included, under the same licence.
Roofing Filter [DW] by DonovanWall, ported from Pine v4. Pane indicator with 5 plots, bar/background colouring and 10 inputs.
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © DonovanWall
//██████╗ ██╗ ██╗
//██╔══██╗██║ ██║
//██║ ██║██║ █╗ ██║
//██║ ██║██║███╗██║
//██████╔╝╚███╔███╔╝
//╚═════╝ ╚══╝╚══╝
// ------------------------------------------------------------------------------------------------
// Original author: DonovanWall
// Original title: Roofing Filter [DW]
// Licence: MPL-2.0 (the header above is the original notice, kept verbatim)
// Pine version: 4
// Fetched: 2026-10-12
// Ported to FloScript by FloSignals; modifications under MPL-2.0.
// Converter: @flosignals/floscript 0.1.0, strict mode, score 100/100
// ------------------------------------------------------------------------------------------------
//@floscript=1
//@version=5
// Converted by the FloScript converter from a Pine-compatible v4 source.
indicator("Roofing Filter [DW]", overlay=false)
//This is an experimental study built on the concept of using roofing filters on price data proposed by John Ehlers.
//Roofing filters are a type of bandpass filter conventionally used in HF radio receivers in the first IF stage to limit the frequency spectrum passed on to later stages in the receiver.
//The goal in applying roofing filters to a price signal is to simultaneously attenuate high frequency noise and low frequency distortion to pass an oscillating signal with a nearly zero mean for analysis and/or further calculation.
//In this study, there are three filter types to choose from:
// -> Ehlers Roofing Filter, which passes data through a 2 pole high pass filter, then through a Super Smoother filter.
// -> Gaussian Roofing Filter, which passes data through a 2 pole Gaussian high pass filter, then through a 2 pole Gaussian low pass filter.
// -> Butterworth Roofing Filter, which passes data through a 2 pole Butterworth high pass filter, then through a 2 pole Butterworth low pass filter.
//Each filter type has different amplitude and delay characteristics, so play around with each type and see which response suits your needs best.
//There is an option to normalize the scale of the output as well. The normalization process in this script is computed by comparing positive and negative outputs to the filter's moving RMS value.
//The resulting oscillator can be fed through numerous conventional indicators including Stochastic Oscillator, RSI, CCI, etc. to generate smoother, less distorted indicators for a clearer view of turning points.
//Alternatively, it can also act as an indicator itself, as implied by the corresponding color scheme included in the script.
//Although roofing filters are not conventionally used in the analysis of market data, applying such spectral analysis techniques may prove to be quite useful for the design of more efficient indicators and more reliable predictions.
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Update:
// -> Fixed bug in RMS calculation. Big thanks to @yatrader2 for bringing this issue to my attention!
// -> Reoganized script structure.
// -> Added an optional compression algorithm to the script. This can be used to reduce transients (short duration, high amplitude values) by a specified level of intensity.
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Functions
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Ehlers Roofing Filter
erf(x, t_hp, t_lp) =>
omega1 = 4 * math.asin(1) / t_hp
omega2 = 4 * math.asin(1) / t_lp
alpha = (math.cos(math.sqrt(2) / 2 * omega1) + math.sin(math.sqrt(2) / 2 * omega1) - 1) / math.cos(math.sqrt(2) / 2 * omega1)
hp = 0.0
hp := math.pow(1 - alpha / 2, 2) * (x - 2 * x[1] + x[2]) + 2 * (1 - alpha) * nz(hp[1]) - math.pow(1 - alpha, 2) * nz(hp[2])
a1 = math.exp(-math.sqrt(2) * 2 * math.asin(1) / t_lp)
b1 = 2 * a1 * math.cos(math.sqrt(2) / 2 * omega2)
c2 = b1
c3 = -math.pow(a1, 2)
c1 = 1 - c2 - c3
erf = 0.0
erf := c1 * hp + c2 * nz(erf[1]) + c3 * nz(erf[2])
erf
//Gaussian Roofing Filter
grf(x, t_hp, t_lp) =>
beta1 = (1 - math.cos(4 * math.asin(1) / t_hp)) / (math.sqrt(2) - 1)
alpha1 = -beta1 + math.sqrt(math.pow(beta1, 2) + 2 * beta1)
beta2 = (1 - math.cos(4 * math.asin(1) / t_lp)) / (math.sqrt(2) - 1)
alpha2 = -beta2 + math.sqrt(math.pow(beta2, 2) + 2 * beta2)
ghp = 0.0
ghp := (1 - math.pow(alpha1, 2)) * x + 2 * (1 - alpha1) * (nz(ghp[1]) - x[1]) + math.pow(1 - alpha1, 2) * (x[2] - nz(ghp[2]))
grf = 0.0
grf := math.pow(alpha2, 2) * ghp + 2 * (1 - alpha2) * nz(grf[1]) - math.pow(1 - alpha2, 2) * nz(grf[2])
grf
//Butterworth Roofing Filter
brf(x, t_hp, t_lp) =>
a1 = math.exp(-math.sqrt(2) * 2 * math.asin(1) / t_hp)
b1 = 2 * a1 * math.cos(math.sqrt(2) * 2 * math.asin(1) / t_hp)
c1 = math.pow(a1, 2)
d1 = (1 - b1 + c1) / 4
a2 = math.exp(-math.sqrt(2) * 2 * math.asin(1) / t_lp)
b2 = 2 * a2 * math.cos(math.sqrt(2) * 2 * math.asin(1) / t_lp)
c2 = math.pow(a2, 2)
d2 = (1 - b2 + c2) / 4
bhp = 0.0
bhp := b1 * nz(bhp[1]) - c1 * nz(bhp[2]) + (1 - d1) * x - (b1 + 2 * d1) * x[1] + (c1 - d1) * x[2]
brf = 0.0
brf := b2 * nz(brf[1]) - c2 * nz(brf[2]) + d2 * (bhp + 2 * bhp[1] + bhp[2])
brf
//Highest High Function
hi(x, reset) =>
var hi = 0.0
hi := reset or x > hi ? x : hi
hi
//Cumulative Mean Function
c_mean(y) =>
var x = 0
x := x + 1
var y_sum = 0.0
y_sum := y_sum + y
c_mean = y_sum / x
c_mean
//RMS Function
RMS(x, t) =>
math.sqrt(ta.sma(math.pow(x, 2), t))
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Inputs
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Source
src = input(defval=close, title="Source")
//Low Pass Cutoff Period
lp_per = input.int(defval=10, minval=2, title="Low Pass Cutoff Period")
//High Pass Cutoff Period
hp_per = input.int(defval=30, minval=2, title="High Pass Cutoff Period")
//Roofing Filter Type
type = input.string(defval="Ehlers", options=["Ehlers", "Gaussian", "Butterworth"], title="Roofing Filter Type")
//Output Scale Normalization
normalize = input(defval=false, title="Normalize Output Scale")
//Signal Compression Inputs
use_comp = input(defval=false, title="Use Compression")
comp_intensity = input.float(defval=90.0, minval=0, maxval=100, title="Compression Intensity (%)")
comp_thresh_type = input.string(defval="Auto", options=["Auto", "Custom"], title="Compression Threshold Type")
comp_ht_custom = input.float(defval=0.0, minval=0, title="Custom High Compression Threshold")
comp_lt_custom = input.float(defval=0.0, maxval=0, title="Custom Low Compression Threshold")
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Definitions
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Roofing Filter Type
roof = type == "Ehlers" ? erf(src, hp_per, lp_per) : type == "Gaussian" ? grf(src, hp_per, lp_per) : brf(src, hp_per, lp_per)
//Scale Normalization
rms = RMS(roof, math.round((hp_per + lp_per) / 2))
norm_roof = roof > 0 ? roof / rms : roof < 0 ? -math.abs(roof) / rms : 0
//Filter
filt1 = normalize ? norm_roof : roof
//Dichotomized QRS Values
filt_pos = filt1 > 0 ? filt1 : 0
filt_neg = filt1 < 0 ? filt1 : 0
//Compressor Thresholds
hi_filt = hi(filt_pos, ta.crossover(filt1, 0))
lo_filt = -hi(math.abs(filt_neg), ta.crossunder(filt1, 0))
comp_ht = comp_thresh_type == "Auto" ? c_mean(hi_filt) : comp_ht_custom
comp_lt = comp_thresh_type == "Auto" ? c_mean(lo_filt) : comp_lt_custom
//Transient Reduction
filt = use_comp ? (filt1 > comp_ht ? filt1 - comp_intensity * (filt1 - comp_ht) / 100 : filt1 < comp_lt ? filt1 + comp_intensity * (comp_lt - filt1) / 100 : filt1) : filt1
//Colors
filt_color = filt > 0 and filt > filt[1] ? #38ff97 : filt < 0 and filt < filt[1] ? #ff3874 : color.silver
hist_color = filt > 0 ? #38ff97 : filt < 0 ? #ff3874 : color.silver
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Outputs
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
//Center Line
plot(0, color=color.new(color.orange, 0), title="Center Line")
//Filter Line
plot(filt, color=color.new(filt_color, 0), linewidth=3, title="Filter Line")
//Filter Columns
plot(filt, color=color.new(hist_color, 80), style=plot.style_columns, title="Filter Columns")
//Thresholds
plot(use_comp ? comp_ht : na, color=color.new(#cccccc, 80), style=plot.style_circles, title="High Compression Threshold")
plot(use_comp ? comp_lt : na, color=color.new(#cccccc, 80), style=plot.style_circles, title="Low Compression Threshold")
//Bar Color
barcolor(filt_color)
This file is under the Mozilla Public License 2.0; it is a port of the original by DonovanWall, with the original licence header kept and the changes made for FloScript noted in the file.
- v2
Library refresh 2026-10-14
- v1
Library refresh 2026-10-12
Disclaimer. Scripts are written and published by members, or ported by FloSignals from open-source originals, and are their authors' own work and opinion. FloSignals does not review them for correctness and nothing in a script is financial advice or a recommendation to buy or sell anything — read the Risk Disclaimer. Posting here is under the Community Rules; a script that is not the poster's to post can be reported as the Copyright & Takedowns page says.

Comments (0)
No comments yet. Members can leave one.