Page 1 of 1
Why Your Scalping MAs Lag: Introducing the Zero-Lag Adaptive Kalman Filter for M1 & Tick Charts
Posted: Sun Jul 26, 2026 4:02 pm
by PTScalper
Are you tired of getting whipped out of M1 scalps because your moving averages react two candles too late? The unavoidable curse of traditional retail technical analysis is the lag-versus-noise trade-off. If you smooth out erratic intrabar price spikes using standard EMAs or WMAs, you create fatal lag that ruins your risk-to-reward ratio. If you speed up your indicator periods to catch early moves, you get chopped to pieces by spread fluctuations and false breakouts during consolidation.To survive in ultra-short-term forex scalping, we have to abandon static retail averages and look at how high-frequency institutional desks process market data: Digital Signal Processing (DSP). Unlike standard arithmetic tools, an Adaptive Kalman Filter is a recursive Bayesian estimator. It does not just blindly average historical closing prices. Instead, it uses probability matrices to continuously predict where the price should be on the very next tick, and then instantly corrects that trajectory based on real-time measurement error.How the 2-State Engine Eliminates LagTo make this viable for live M1 and tick trading, we implemented a 2-State Constant Velocity (CV) model with Innovation Gating. Instead of tracking just one dimension, our mathematical engine tracks two state variables simultaneously:Estimated Price ($p_k$)Instantaneous Velocity / Momentum ($v_k$)Here is the mathematical secret to eliminating lag: When the market is drifting sideways in a tight range, the filter keeps its internal process noise low. It acts as a super-smoothed baseline that completely ignores sub-pip noise and spread widening. But the exact millisecond an aggressive institutional buyer steps in and spikes the price, the algorithm calculates the Mahalanobis distance (the normalized squared prediction error). If this prediction error breaches a 95% statistical confidence threshold, the filter recognizes a structural market breakout. It dynamically inflates its covariance matrix, forcing the Kalman Gain to jump immediately toward 1.0. In plain English: the indicator instantly "snaps" to the live breakout price on the very first tick without any trailing lag.How to Execute Scalping SetupsZero-Lag Momentum Entries: We do not trade traditional moving average crossovers. We monitor the Kalman Velocity parameter ($v_k$). When the filter line shifts from red (negative momentum) to lime green (positive momentum) during an active London or New York session, the trend has mathematically accelerated—triggering an immediate buy execution.Absorption & Exhaustion Traps: When price pushes hard away from the Kalman baseline but the Velocity parameter begins to flatten out while making a new high, you are witnessing liquidity absorption. Large limit orders are trapping aggressive retail buyers—prepare for an immediate mean-reversion short setup.The Ultimate Order Flow Filter: For maximum strike rate, combine this Kalman momentum line with sub-pip Order Flow Delta. Only take long scalps when Kalman velocity is positive and aggressive Ask volume dominates the tape.Whether you are developing EAs in MQL4, MQL5, or cTrader C#, upgrading from static moving averages to an adaptive Kalman mathematical core will completely transform your execution timing. Stop letting indicator lag eat your profit margins! Who else here is experimenting with DSP or adaptive algorithms in their scalping loops? Let’s discuss your parameter settings below!
Re: Why Your Scalping MAs Lag: Introducing the Zero-Lag Adaptive Kalman Filter for M1 & Tick Charts
Posted: Sun Jul 26, 2026 4:02 pm
by PTScalper
For IC trader:
Code: Select all
using System;
namespace cAlgo.API.Indicators
{
/// <summary>
/// Adaptivní 2-State Kalmanův Filtr pro High-Frequency Scalping.
/// Zpracovává cenu i momentum bez zpoždění pomocí adaptivní inflace kovariance.
/// </summary>
public sealed class ZeroLagAdaptiveKalman
{
// Stavové proměnné (State Vector x)
private double _p; // Odhadovaná cena (Price)
private double _v; // Odhadovaná rychlost / momentum (Velocity)
// Kovarianční matice chyby odhadu P (2x2 matice rozvinutá do skalárů)
private double _p00, _p01, _p10, _p11;
// Parametry šumu
private readonly double _q00; // Procesní šum ceny
private readonly double _q11; // Procesní šum rychlosti
private readonly double _r; // Šum měření (tržní šum / spread noise)
private readonly double _gammaThreshold; // Práh pro detekci průrazu (např. 3.84)
private bool _isInitialized;
/// <summary>
/// Inicializace jádra filtru.
/// </summary>
/// <param name="processNoisePrice">Variabilita ceny (např. 0.00001)</param>
/// <param name="processNoiseVelocity">Variabilita momenta (např. 0.000001)</param>
/// <param name="measurementNoise">Šum trhu (např. 0.001 nebo odvozeno z ATR)</param>
/// <param name="gammaThreshold">Práh Mahalanobisovy vzdálenosti pro adaptaci (standardně 3.84)</param>
public ZeroLagAdaptiveKalman(double processNoisePrice = 1e-5,
double processNoiseVelocity = 1e-6,
double measurementNoise = 1e-3,
double gammaThreshold = 3.84)
{
_q00 = processNoisePrice;
_q11 = processNoiseVelocity;
_r = measurementNoise;
_gammaThreshold = gammaThreshold;
_isInitialized = false;
}
/// <summary>
/// Zpracuje nový tick nebo uzavírací cenu a vrátí odhadovanou cenu bez lagu.
/// </summary>
public double Update(double measurement, out double currentVelocity)
{
if (!_isInitialized)
{
_p = measurement;
_v = 0.0;
_p00 = 1.0; _p01 = 0.0;
_p10 = 0.0; _p11 = 1.0;
_isInitialized = true;
currentVelocity = _v;
return _p;
}
// --- 1. PREDIKCE (Predict Step) ---
// x_pred = F * x => p_pred = p + v, v_pred = v
double pPred = _p + _v;
double vPred = _v;
// P_pred = F * P * F^T + Q
double p00Pred = _p00 + _p01 + _p10 + _p11 + _q00;
double p01Pred = _p01 + _p11;
double p10Pred = _p10 + _p11;
double p11Pred = _p11 + _q11;
// --- 2. ADAPTACE NA VOLATILITU (Innovation Gating) ---
double y = measurement - pPred; // Inovace (chyba predikce)
double s = p00Pred + _r; // Kovariance inovace
double epsilon = (y * y) / s; // Normalizovaná čtvercová chyba
// Pokud inovace překročí práh, trh proráží. Nafoukneme kovarianci!
if (epsilon > _gammaThreshold)
{
double inflation = Math.Sqrt(epsilon);
p00Pred *= inflation;
p01Pred *= inflation;
p10Pred *= inflation;
p11Pred *= inflation;
s = p00Pred + _r; // Přepočítání S po inflaci
}
// --- 3. KOREKCE (Update Step) ---
// Kalman Gain matice K = P_pred * H^T * S^{-1}
double k0 = p00Pred / s; // Zisk pro cenu
double k1 = p10Pred / s; // Zisk pro rychlost
// Aktualizace stavu x = x_pred + K * y
_p = pPred + (k0 * y);
_v = vPred + (k1 * y);
// Aktualizace kovariance P = (I - K * H) * P_pred
_p00 = (1.0 - k0) * p00Pred;
_p01 = (1.0 - k0) * p01Pred;
_p10 = p10Pred - (k1 * p00Pred);
_p11 = p11Pred - (k1 * p01Pred);
currentVelocity = _v;
return _p;
}
/// <summary>
/// Resetuje stav při změně instrumentu nebo odpojení.
/// </summary>
public void Reset() => _isInitialized = false;
}
}
Re: Why Your Scalping MAs Lag: Introducing the Zero-Lag Adaptive Kalman Filter for M1 & Tick Charts
Posted: Sun Jul 26, 2026 4:03 pm
by PTScalper
For MT5 (MQL5)
Code: Select all
//+------------------------------------------------------------------+
//| ZeroLagAdaptiveKalman.mqh |
//| High-Performance 2-State Kalman Filter Core |
//+------------------------------------------------------------------+
class CZeroLagAdaptiveKalman
{
private:
double m_p; // Odhad ceny
double m_v; // Odhad rychlosti / momenta
double m_p00, m_p01, m_p10, m_p11; // Kovarianční matice P
double m_q00; // Procesní šum ceny
double m_q11; // Procesní šum momenta
double m_r; // Šum měření
double m_gamma; // Práh adaptace
bool m_init;
public:
CZeroLagAdaptiveKalman(double qPrice=1e-5, double qVel=1e-6, double rNoise=1e-3, double gamma=3.84);
~CZeroLagAdaptiveKalman(void) {};
double Update(const double measurement, double &out_velocity);
void Reset(void) { m_init = false; }
};
//+------------------------------------------------------------------+
//| Konstruktor |
//+------------------------------------------------------------------+
CZeroLagAdaptiveKalman::CZeroLagAdaptiveKalman(double qPrice=1e-5, double qVel=1e-6, double rNoise=1e-3, double gamma=3.84)
: m_q00(qPrice),
m_q11(qVel),
m_r(rNoise),
m_gamma(gamma),
m_init(false)
{
}
//+------------------------------------------------------------------+
//| Hlavní výpočetní cyklus (volat na každém ticku nebo svíčce) |
//+------------------------------------------------------------------+
double CZeroLagAdaptiveKalman::Update(const double measurement, double &out_velocity)
{
if(!m_init)
{
m_p = measurement;
m_v = 0.0;
m_p00 = 1.0; m_p01 = 0.0;
m_p10 = 0.0; m_p11 = 1.0;
m_init = true;
out_velocity = m_v;
return m_p;
}
// 1. Predikce
double p_pred = m_p + m_v;
double v_pred = m_v;
double p00_pred = m_p00 + m_p01 + m_p10 + m_p11 + m_q00;
double p01_pred = m_p01 + m_p11;
double p10_pred = m_p10 + m_p11;
double p11_pred = m_p11 + m_q11;
// 2. Adaptace na průraz (Innovation Gating)
double y = measurement - p_pred;
double s = p00_pred + m_r;
double epsilon = (y * y) / s;
if(epsilon > m_gamma)
{
double inflation = MathSqrt(epsilon);
p00_pred *= inflation;
p01_pred *= inflation;
p10_pred *= inflation;
p11_pred *= inflation;
s = p00_pred + m_r;
}
// 3. Korekce
double k0 = p00_pred / s;
double k1 = p10_pred / s;
m_p = p_pred + (k0 * y);
m_v = v_pred + (k1 * y);
m_p00 = (1.0 - k0) * p00_pred;
m_p01 = (1.0 - k0) * p01_pred;
m_p10 = p10_pred - (k1 * p00_pred);
m_p11 = p11_pred - (k1 * p01_pred);
out_velocity = m_v;
return m_p;
}
Re: Why Your Scalping MAs Lag: Introducing the Zero-Lag Adaptive Kalman Filter for M1 & Tick Charts
Posted: Sun Jul 26, 2026 4:03 pm
by PTScalper
For MT4 (MQL4):
Code: Select all
//+------------------------------------------------------------------+
//| ZeroLagKalman_Scalper.mq4 |
//| Zero-Lag Adaptive Kalman Filter for MT4 |
//+------------------------------------------------------------------+
#property copyright "Free Open Source"
#property link ""
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrLime
#property indicator_color2 clrRed
#property indicator_width1 2
#property indicator_width2 2
//--- Vstupní parametry filtru
input double InpQPrice = 0.00001; // Procesní šum ceny (Q Price)
input double InpQVel = 0.000001;// Procesní šum rychlosti (Q Velocity)
input double InpRNoise = 0.001; // Šum měření trhu (R Noise)
input double InpGamma = 3.84; // Práh adaptace pro průrazy (Gamma Threshold)
//--- Vykreslovací buffery (Trik pro barevnou čáru v MT4)
double KalmanBullBuffer[]; // Zelená čára (v > 0)
double KalmanBearBuffer[]; // Červená čára (v < 0)
//+------------------------------------------------------------------+
//| Matematické jádro: 2-State Constant Velocity Kalman Filter |
//+------------------------------------------------------------------+
class CZeroLagAdaptiveKalman
{
private:
double m_p; // Odhad ceny
double m_v; // Odhad rychlosti / momenta
double m_p00, m_p01, m_p10, m_p11; // Kovarianční matice P
double m_q00, m_q11, m_r, m_gamma;
bool m_init;
public:
CZeroLagAdaptiveKalman(double qPrice=1e-5, double qVel=1e-6, double rNoise=1e-3, double gamma=3.84);
~CZeroLagAdaptiveKalman(void) {};
double Update(const double measurement, double &out_velocity);
void Reset(void) { m_init = false; }
};
CZeroLagAdaptiveKalman::CZeroLagAdaptiveKalman(double qPrice=1e-5, double qVel=1e-6, double rNoise=1e-3, double gamma=3.84)
: m_q00(qPrice), m_q11(qVel), m_r(rNoise), m_gamma(gamma), m_init(false) {}
double CZeroLagAdaptiveKalman::Update(const double measurement, double &out_velocity)
{
if(!m_init)
{
m_p = measurement;
m_v = 0.0;
m_p00 = 1.0; m_p01 = 0.0;
m_p10 = 0.0; m_p11 = 1.0;
m_init = true;
out_velocity = m_v;
return m_p;
}
// 1. Predikce
double p_pred = m_p + m_v;
double v_pred = m_v;
double p00_pred = m_p00 + m_p01 + m_p10 + m_p11 + m_q00;
double p01_pred = m_p01 + m_p11;
double p10_pred = m_p10 + m_p11;
double p11_pred = m_p11 + m_q11;
// 2. Adaptace na průraz (Innovation Gating)
double y = measurement - p_pred;
double s = p00_pred + m_r;
double epsilon = (y * y) / s;
if(epsilon > m_gamma)
{
double inflation = MathSqrt(epsilon);
p00_pred *= inflation;
p01_pred *= inflation;
p10_pred *= inflation;
p11_pred *= inflation;
s = p00_pred + m_r;
}
// 3. Korekce
double k0 = p00_pred / s;
double k1 = p10_pred / s;
m_p = p_pred + (k0 * y);
m_v = v_pred + (k1 * y);
m_p00 = (1.0 - k0) * p00_pred;
m_p01 = (1.0 - k0) * p01_pred;
m_p10 = p10_pred - (k1 * p00_pred);
m_p11 = p11_pred - (k1 * p01_pred);
out_velocity = m_v;
return m_p;
}
//--- Globální instance filtru pro indikátor
CZeroLagAdaptiveKalman *g_kalman = NULL;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, KalmanBullBuffer);
SetIndexStyle(0, DRAW_LINE);
SetIndexLabel(0, "Kalman Bullish (v > 0)");
SetIndexEmptyValue(0, EMPTY_VALUE);
SetIndexBuffer(1, KalmanBearBuffer);
SetIndexStyle(1, DRAW_LINE);
SetIndexLabel(1, "Kalman Bearish (v < 0)");
SetIndexEmptyValue(1, EMPTY_VALUE);
// Alokace nového objektu s uživatelskými parametry
if(g_kalman != NULL) delete g_kalman;
g_kalman = new CZeroLagAdaptiveKalman(InpQPrice, InpQVel, InpRNoise, InpGamma);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(g_kalman != NULL)
{
delete g_kalman;
g_kalman = NULL;
}
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total < 2 || g_kalman == NULL) return(0);
int limit = rates_total - prev_calculated;
// Pokud počítáme od začátku (nový graf nebo reset), resetujeme stav filtru
if(prev_calculated == 0)
{
limit = rates_total - 1;
g_kalman.Reset();
}
else if(limit > 0)
{
limit++; // Přepočítat i poslední známý bar pro kontinuitu
}
// V MT4 jsou pole standardně indexována od nejnovějšího [0] po nejstarší [rates_total-1].
// Pro rekurzivní Kalmanův filtr musíme postupovat chronologicky od nejstaršího (limit) k nejnovějšímu (0).
for(int i = limit; i >= 0; i--)
{
double velocity = 0.0;
// Krmíme filtr Typickou cenou (High + Low + Close) / 3 pro vyhlazení mikroskopického šumu
double typicalPrice = (high[i] + low[i] + close[i]) / 3.0;
double kalmanPrice = g_kalman.Update(typicalPrice, velocity);
// Reset obou bufferů na prázdnou hodnotu
KalmanBullBuffer[i] = EMPTY_VALUE;
KalmanBearBuffer[i] = EMPTY_VALUE;
// Přepínání barev podle trajektorie momenta (Velocity)
if(velocity >= 0.0)
{
KalmanBullBuffer[i] = kalmanPrice;
// Propojení čar: zajistí, že čára nebude vizuálně přerušená při změně barvy
if(i < rates_total - 1 && KalmanBearBuffer[i+1] != EMPTY_VALUE)
KalmanBullBuffer[i+1] = KalmanBearBuffer[i+1];
}
else
{
KalmanBearBuffer[i] = kalmanPrice;
if(i < rates_total - 1 && KalmanBullBuffer[i+1] != EMPTY_VALUE)
KalmanBearBuffer[i+1] = KalmanBullBuffer[i+1];
}
}
return(rates_total);
}