TradingView indicator calculations

Kalman filters on TradingView: a reproducible price filter

A Kalman filter in trading estimates a hidden state from observed prices under an explicit model. This example estimates one price level: each completed close adjusts the previous estimate by a gain determined by assumed process and measurement variances. The result is a causal filtered price, with lag after a jump. It does not establish a future price forecast or select profitable settings. The equations, six-step fixture and Pine Script below define exactly which filter is being calculated.

See the decision guide

Define the state and what counts as noise

Use the random-walk model x[t] = x[t−1] + w[t] and observation model z[t] = x[t] + v[t]. Here x is an unobserved price level and z is the observed close. Assume independent, zero-mean process noise w and measurement noise v, with variances Q and R. These are modeling assumptions about a latent level; R does not mean that the exchange reported the wrong price.

  • The state has no velocity, acceleration or trend term. A two-state price-and-velocity filter is a different model, even if both scripts are called a Kalman filter indicator.
  • Prices can jump and their statistical behavior can change. A small P is an internal model quantity, not proof of small future trading losses or a calibrated confidence interval.
  • If prices are multiplied by 10, multiply Q, R and initial P by 100 to preserve the same gain path. Changing chart timeframe also changes the meaning of a per-bar Q.
SymbolMeaning in this pageUnits
xEstimated latent price level after the observationPrice
zObserved close, or a stated synthetic observationPrice
QVariance added by the random-walk state transition per chart barPrice squared
RAssumed variance of observation noise around the latent levelPrice squared
PEstimated state-error variance under the modelPrice squared
KWeight applied to the observation residualUnitless

Apply the scalar predict-and-update equations

Let xPrevious and PPrevious be the last completed estimates. A superscript minus marks the prediction before the new observation. With transition and observation coefficients both equal to 1, the scalar equations below follow from the Kalman recurrence. Keep full precision between steps.

  • For this implementation, require Q ≥ 0, R > 0 and initial P ≥ 0. In exact arithmetic, the denominator is positive and 0 ≤ K < 1 for finite inputs. The updated level lies between the prior level and the observation.
  • At fixed Q and R, K depends on P, not on the size of the price residual. A large gap alone does not cause this filter to infer a larger Q or a smaller R.
  • In the constant-variance case, the gain approaches a constant after initialization. The level update then resembles an exponential smoother with that gain; the Kalman name does not remove smoothing lag.
StepEquationPurpose
Predict levelxMinus = xPreviousCarry the previous level forward; this model has no directional drift.
Predict variancePMinus = PPrevious + QAdd uncertainty for one state transition.
Calculate gainK = PMinus / (PMinus + R)Weight the observation using the two variances.
Update levelx = xMinus + K × (z − xMinus)Move part of the way toward the observed price.
Update varianceP = (1 − K) × PMinusRetain the model variance after the observation.

Reproduce the six-step price fixture

Use hypothetical observations 100, 102, 101, 110, 109 and 111. At step 0, seed x = 100 and P = 1 without another observation update. For steps 1 through 5 use Q = 1 and R = 4. The jump from 101 to 110 is an invented discontinuity for checking arithmetic, not a historical trade.

  • At step 1: PMinus = 1 + 1 = 2, K = 2 / (2 + 4) = 1/3, x = 100 + (1/3) × 2 = 100.666667 and P = (2/3) × 2 = 1.333333.
  • At the gap, the residual is 110 − 100.789474 = 9.210526. The gain is about 0.382114, so the estimate moves only about 3.519469 and remains below 110. The filter has not predicted the jump.
  • The displayed values are rounded to six decimals. Reusing those rounded values as state introduces small differences; compare with a tolerance of 0.000001.
StepObservation zPMinusGain KEstimate xPosterior P
0100SeedSeed100.0000001.000000
11022.0000000.333333100.6666671.333333
21012.3333330.368421100.7894741.473684
31102.4736840.382114104.3089431.528455
41092.5284550.387298106.1257781.549191
51112.5491910.389237108.0230081.556950

Change noise assumptions and compare the response

Run three versions of the same fixture from the same seed. The baseline keeps Q = 1 and R = 4. A second version raises R to 16 from step 4, reducing trust in those observations. A third sets Q = 9 only at the gap on step 3, then returns Q to 1. These schedules are deliberate sensitivity experiments, not estimates of actual market noise or a live gap-prediction rule.

  • Higher R at step 4 lowers K from 0.387298 to 0.136463. The estimate moves less toward 109 and its posterior P is higher: 2.183414 instead of 1.549191.
  • Higher Q at the gap raises PMinus to 10.473684 and K to 0.723636. This follows the jump faster, but the same choice would also follow a one-bar outlier more strongly.
  • An adaptive filter needs a separately specified, causal rule for estimating Q or R. Choosing a noise schedule after seeing the desired answer is not evidence that a live rule will work.
ScenarioStep 3 x / KStep 4 x / KStep 5 x / K
Fixed Q = 1, R = 4104.308943 / 0.382114106.125778 / 0.387298108.023008 / 0.389237
R = 16 from step 4104.308943 / 0.382114104.949100 / 0.136463105.953224 / 0.165946
Q = 9 only at step 3107.454545 / 0.723636108.216951 / 0.493321109.403593 / 0.426382

Compare the table with Pine Script output

Paste this indicator into Pine Editor and add it to the chart. Enable Use repeating six-step fixture and keep Q = 1, R = 4 and initial variance = 1. Inspect a completed bar in the Data Window: Fixture step identifies the row, and Estimate x, Gain K, Prior variance P- and Posterior variance P expose the calculation. Each six-bar cycle resets at step 0, so the fixture is easy to locate without scrolling to the beginning of the chart.

  • Select each Fixture noise case to check the sensitivity table. Those overrides apply only in fixture mode. The source and variance inputs remain configurable, so restore the stated defaults before comparing numbers.
  • Turn the fixture off for market data. The script initializes on the first valid completed observation and updates on subsequent completed chart bars. It leaves the open bar unplotted, rather than treating each tick as another observation.
  • The table gives independently calculated expected outputs, not a claim that a live TradingView chart was inspected. Syntax validation checks compilation; use the Data Window to verify numerical agreement in your chart environment.
Pine Script v6, with explicit seed, noise schedules and completed-bar state updates. The fixture resets are for verification only; market mode does not reset every six bars.pine
//@version=6
indicator("Scalar Kalman price filter audit", overlay = false, precision = 6)
source = input.source(close, "Market observation")
qInput = input.float(1.0, "Q: process variance per bar", minval = 0.0)
rInput = input.float(4.0, "R: measurement variance", minval = 0.000001)
pInitial = input.float(1.0, "Initial estimate variance", minval = 0.0)
useFixture = input.bool(false, "Use repeating six-step fixture")
noiseCase = input.string("Fixed Q/R", "Fixture noise case", options = ["Fixed Q/R", "Higher R from step 4", "Higher Q at gap"])
step = bar_index % 6
fixture = switch step
    0 => 100.0
    1 => 102.0
    2 => 101.0
    3 => 110.0
    4 => 109.0
    => 111.0
z = useFixture ? fixture : source
q = useFixture and noiseCase == "Higher Q at gap" and step == 3 ? 9.0 : qInput
r = useFixture and noiseCase == "Higher R from step 4" and step >= 4 ? 16.0 : rInput
var float estimate = na
var float variance = na
float priorVariance = na
float gain = na
if barstate.isconfirmed
    if na(estimate) or (useFixture and step == 0)
        if not na(z)
            estimate := z
            variance := pInitial
    else
        priorVariance := variance + q
        if na(z)
            variance := priorVariance
        else
            gain := priorVariance / (priorVariance + r)
            estimate := estimate + gain * (z - estimate)
            variance := (1.0 - gain) * priorVariance
plot(barstate.isconfirmed ? z : na, "Observation z", color = color.gray)
plot(barstate.isconfirmed ? estimate : na, "Estimate x", color = color.blue, linewidth = 2)
plot(useFixture ? step : na, "Fixture step", display = display.data_window)
plot(priorVariance, "Prior variance P-", display = display.data_window)
plot(gain, "Gain K", display = display.data_window)
plot(barstate.isconfirmed ? variance : na, "Posterior variance P", display = display.data_window)
plot(q, "Q used", display = display.data_window)
plot(r, "R used", display = display.data_window)

Check initialization, missing values and signal boundaries

Seeding the level with the first observation and assigning P = 1 is this page's initialization convention. It is not a universal Kalman default. An independently supplied prior followed by an update of the first observation would produce different early values. Changing the loaded history, source or seed can therefore change later output.

  • With Q = 0 and initial P = 0, K stays zero and the estimate never moves. The denominator remains valid because R is positive, but the model declares complete certainty in a fixed state.
  • On a completed bar with a missing observation after initialization, this implementation holds x and adds Q to P without a measurement update. Before the first valid observation it remains uninitialized. An exchange closure with no chart bar triggers no update.
  • For a flat sequence equal to the seed, every residual is zero, so the level stays flat even while P and K change. This is a useful separate check of state versus uncertainty.
  • The code uses only the chart timeframe and has no future-data requests. Confirmed bars prevent intrabar state changes, but cannot prevent revised market data, changed inputs or a different history start from changing results after a reload.
  • A close crossing the estimate is a relationship between two series, not a complete strategy. Specify equality, first crossing versus persistent state, bar-close timing, exits and costs before testing a trading rule.

Give Pineify the model and the expected numbers

Use the Pine Script AI Coding Agent to draft or revise this public scalar algorithm. Provide the equations, first-observation seed, missing-value rule and six-step fixture. Ask for the diagnostic plots and request a syntax check explicitly. Compare the generated values in TradingView before asking the agent to add a confirmed-bar alert.

  • Keep the model contract with the script: source, chart timeframe, Q, R, initial P, update timing and any adaptive rule. A script name or a smooth screenshot is insufficient to reproduce a calculation.
  • Work from public formulas or source you may use. This example does not reconstruct a protected indicator, infer its settings or promise that another script bearing the Kalman name will match.
  • A successful compiler result does not prove a correct model, optimal settings, forecast accuracy or profitability. Those require separate numerical, statistical and trading-rule tests.
Where Pineify fits

Pine Script AI Coding Agent

Draft or revise a Pine implementation from explicit state equations and initialization rules. Request syntax checking, then compare the fixture values before adding alerts.

Draft a Kalman filter script

Frequently asked questions

Educational information, not investment advice. The synthetic observations and noise schedules are calculation examples, not backtests or forecasts. Filter settings, syntax validation and confirmed-bar calculations do not guarantee trading returns.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.