TradingView indicator guides

Modular Filter: Recursive States and Pine Script | Pineify

The Modular Filter blends price with its own previous output, updates upper and lower tracking values, and selects a weighted combination using a persistent binary state. This guide describes the exact implementation below: length 14 by default, feedback weight 0.5, and band weight 0.8. Internal state, output slope, and price direction are different quantities.

Inspect the Pine Script

Origin and implementation scope

alexgrover published Modular Filter in June 2019, describing feedback and configurable band weighting. The implementation here fixes both weights and exposes only length and source. It is not evidence that Pineify invented the algorithm, nor a claim that every publicly available version produces the same values.

The branch selection is nonlinear, but alpha is constant for a selected length. There is no measured volatility input, learned model, volume filter, or automatic parameter optimization in this script.

Follow the recursion in order

Let x be the selected source, T the filter output, B and C the tracking values, and O the binary state. With alpha = 2 / (length + 1), each bar uses:

  1. A = 0.5 × x + 0.5 × previous T.
  2. B = max(A, alpha × A + (1 − alpha) × previous B).
  3. C = min(A, alpha × A + (1 − alpha) × previous C).
  4. If A equals B, O = 1. Otherwise, if A equals C, O = 0. Otherwise, retain previous O.
  5. Upper = 0.8 × B + 0.2 × C; lower = 0.8 × C + 0.2 × B.
  6. T = upper when O = 1, or lower when O = 0.

The code replaces missing previous T with the current source and missing previous B/C with A. On initialization B = C = A; the first equality branch wins, so O = 1. This initial value does not establish bullish momentum.

B can decrease, and C can increase: they are not monotonic running extrema. The two blends are internal values, not separately plotted bands. This recursion retains history beyond the latest 14 bars; length controls alpha, not a fixed 14-bar window.

Worked example: the output can fall while state stays 1

This hypothetical calculation begins at a constant source of 100, with B = C = T = 100 and O = 1. Length is 14; alpha is 2/15. The EMA comparison also begins at 100 and uses length 14. Values are rounded only for display.

Synthetic source sequence; no market data or trading returns
SourceABCOOutput TEMA 14
1001001001001100100
110105105100.6666671104.133333101.333333
102103.066667104.742222100.9866671103.991111101.422222
100101.995556104.376101.1211851103.725037101.232593
9096.862519103.37420296.862519098.16485599.734914
9898.082428102.66863297.025173098.15386599.503592

At source 102, A is between B and C, so O remains 1. Yet T falls from 104.133333 to 103.991111 while EMA 14 rises. At source 98 after 90, price rises but T still falls. A state label is therefore not a slope label, and a filter slope is not the current price change.

At source 110, this filter responds more than the EMA. That single response does not establish a universal lead, fewer false signals, or a higher win rate. A flat source and settled states can produce a flat output with O remaining 1 indefinitely; flatness does not require rapid state switching.

Parameters, boundaries, and live bars

Length is a positive integer; source defaults to close. Changing length alters alpha. It does not set a known number of state flips or guarantee a delay measured in bars. Switching to HL2 changes the input sequence; it need not make every response smoother than close.

At length 1, alpha = 1 and both B and C equal A on each valid bar. The first branch always sets O to 1, and T = A = 0.5 × source + 0.5 × previous T. With matching initialization, this is the EMA recursion with alpha 0.5, equivalent to EMA length 3. It is not the unfiltered source.

The current close can change before the bar ends, and the script has no barstate.isconfirmed gate. The blue plot can move intrabar. There are no arrows, dynamic colors, alerts, orders, stops, or performance statistics. The state O is internal to the helper and is not separately exposed by its return value.

For a comparison, use identical symbol, session, chart type, source, history, and bar-close policy. Missing data and history initialization can affect the recursion. A visually smooth trace does not establish that a trading rule is useful.

Define a testable condition before a strategy

Choose whether a proposed event means a price crossing T, a change in T slope, or a change in O. These conditions are not interchangeable. If O is needed, adapt the helper to return it explicitly and compare that result with the intermediate values above.

Specify whether an event must survive the closing update, and whether the order could first execute at that close or a later price. Confirmation prevents a condition from being acted on before its chosen observation time; it does not prevent the next closed bar from reversing it.

For any strategy study, define exits, sizing, transaction costs, and evaluation periods separately. Adding RSI, volume, or another average is an additional hypothesis. No measured improvement or preferred market/timeframe is established by this page.

Execution reference: TradingView bar states and repainting.

Pine Script v6: implementation and limits

Length and source are actual settings inputs. Feedback and beta are fixed inside the helper. The complete example plots one blue line and retains the calculation used in the local indicator helper.

Pine Script v6
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify

//@version=6
indicator(title="Modular Filter", overlay=true, max_labels_count=500)

p_comm_timeframe_to_seconds(simple string tf) =>
    float seconds = 0
    tf_lower = str.lower(tf)
    value = str.tonumber(str.substring(tf_lower, 0, str.length(tf_lower) - 1))
    if str.endswith(tf_lower, 's')
        seconds := value
    else if str.endswith(tf_lower, 'd')
        seconds := value * 86400
    else if str.endswith(tf_lower, 'w')
        seconds := value * 604800
    else if str.endswith(tf_lower, 'm')
        seconds := value * 2592000
    else
        seconds := str.tonumber(tf_lower) * 60
    seconds

p_custom_sources() =>
    [open, high, low, close, volume]

p_ta_mod_filt(series float source, simple int length) =>
    z = 0.5
    beta = 0.8
    ts=0.,b=0.,c=0.,os=0.
    alpha = 2/(length+1)
    a = z*source + (1-z)*nz(ts[1],source)
    b := a > alpha*a+(1-alpha)*nz(b[1],a) ? a : alpha*a+(1-alpha)*nz(b[1],a)
    c := a < alpha*a+(1-alpha)*nz(c[1],a) ? a : alpha*a+(1-alpha)*nz(c[1],a)
    os := a == b ? 1 : a == c ? 0 : os[1]
    upper = beta*b+(1-beta)*c
    lower = beta*c+(1-beta)*b
    ts := os*upper+(1-os)*lower

// Inputs
length = input.int(14, "Length", minval=1)
src = input.source(close, "Source")

// Calculation
mf_val = p_ta_mod_filt(src, length)

// Plot
plot(mf_val, "MF", color.rgb(41, 98, 255, 0), 1)
Existing indicator chart preview
Existing visual preview. Verify exact values against the source; the image is not a performance result.

Specify a checkable adaptation in Pineify

Describe the source, length, fixed feedback and beta, and whether the requested output is a line, slope event, or internal-state change. Ask the Pine Script AI Coding Agent for an explicit close-confirmation policy if needed. Compare the generated values with the synthetic sequence above and validate any revised code before adding orders.

Open Pine Script AI Coding Agent

Educational indicator analysis. No measured trading performance is established here. Syntax validation does not prove formula equivalence, execution assumptions or profitability.

Implementation questions

It uses feedback from its previous output, two clipped exponential tracking values, and a persistent state to select a weighted band. This implementation fixes feedback at 0.5 and band weight at 0.8.

No. State chooses a blend; slope compares successive outputs. The worked example has state 1 while the output falls. To inspect state directly, the helper must expose it as an additional return value.

No. Both tracking values become A, but A still includes 50% of the previous output. With matching seeds, the result is the EMA recursion with alpha 0.5, equivalent to length 3.

No best setting is established here. Length 14 and source close are the example defaults. Compare candidate values under explicit rules and costs instead of assuming that a timeframe or asset class has a universal setting.

The current output can change as the open bar changes. This implementation has no close-confirmation gate. It does not use future bars or other-timeframe requests, but that does not make live values final.

No. The AI Coding Agent can help draft and revise code from explicit requirements. Check the resulting formula, syntax, data alignment, and execution assumptions independently; generation does not establish profitable performance.