iir

First introduced in version: 2.00.19/3.00.6

Syntax

iir(X, b, a)

Details

Applies an infinite impulse response (IIR) filter to the input data X and returns the filtered result. An IIR filter uses previous output values as feedback, allowing them to achieve sharper transition bands with a much lower filter order.

The mathematical expression is: a[0]·y[n] = (b[0]·x[n] + b[1]·x[n-1] + … + b[M]·x[n-M]) - (a[1]·y[n-1] + … + a[N]·y[n-N]), where b denotes the numerator coefficients and a denotes the denominator coefficients.

Note:
  • Leading null values in the input are ignored in the calculation, and the corresponding output are also NULL. Any null values after the leading null values are treated as 0.

  • This function is equivalent to Python's scipy.signal.lfilter(b, a, X), except for its handling of missing values. scipy.signal.lfilter does not treat NaN values specially, so NaNs are included in the filtering process and propagate through subsequent outputs.

Parameters

X is a regular vector of numeric type specifying the data to be filtered.

b is a non-empty regular vector of numeric type specifying the numerator coefficients of the IIR filter.

a is a non-empty regular vector of numeric type specifying the denominator coefficients of the IIR filter.

Returns

Returns a result of the same form and length as the input X.

Examples

Example 1. Apply an IIR filter to a vector.

x = 1 2 3 4 5
b = 0.2 0.2
a = 1 -0.5
iir(x, b, a)
// output: [0.2,0.7,1.35,2.075,2.8375]

x = [1, 2, NULL, 4]  // equivalent to [1, 2, 0, 4]
b = 0.2 0.2
a = 1 -0.5
iir(x, b, a)
// output: [0.2,0.7,0.75,1.175]

Example 2. The moving average crossover strategy is one of the most classic trend-following strategies in quantitative trading. It uses a fast EMA and a slow EMA: a buy signal is generated when the fast EMA crosses above the slow EMA, and a sell signal is generated when the fast EMA crosses below the slow EMA. An EMA is essentially a first-order IIR filter.

Compared with an FIR filter that provides a similar degree of smoothing, an IIR filter requires only two coefficients. This offers significant computational advantages when backtesting strategies across hundreds of stocks in a broad market universe.

The following example simulates 1-minute bar data for a single trading day of a stock. Two first-order IIR filters, alphaFast (corresponding to a 10-period EMA) and alphaSlow (corresponding to a 30-period EMA), are used to extract the fast and slow trend lines, respectively, and generate crossover signals.

// 1. Create the catalog, database, and table
if(!existsCatalog("FinanceData")){
    createCatalog("FinanceData")
}
go
use catalog FinanceData

if(!existsDatabase("FinanceData.kline")){
    create database kline
    partitioned by VALUE(2026.06.01..2026.06.30),
    engine='TSDB'
}
go
if(!existsTable("FinanceData.kline", "min1")){
    create table kline.min1 (
        SecurityID SYMBOL,
        TradeDate DATE,
        BarTime TIMESTAMP,
        Open DOUBLE,
        High DOUBLE,
        Low DOUBLE,
        Close DOUBLE,
        Volume LONG
    )
    partitioned by TradeDate,
    sortColumns = [`SecurityID, `BarTime],
    keepDuplicates = ALL
}

// 2. Generate sample data: simulate 240 one-minute bars for 600519.SH on a trading day
n = 240
idx = 0..(n - 1)

baseTime = 2026.06.02T09:30:00.000
barTime = baseTime + idx * 60000

trendComponent = 1800.0 + 10.0 * sin(2.0 * pi * idx / n * 2.0) + cumsum(rand(0.2, n) - 0.1)
noise = norm(0.0, 1.0, n)

closePrice = round(trendComponent + noise, 2)
openPrice  = round(closePrice + norm(0.0, 0.5, n), 2)

highBase = iif(openPrice > closePrice, openPrice, closePrice)
lowBase  = iif(openPrice < closePrice, openPrice, closePrice)

highPrice = round(highBase + abs(norm(0.0, 0.3, n)), 2)
lowPrice  = round(lowBase  - abs(norm(0.0, 0.3, n)), 2)

volume = long(rand(10000, n) + 100)

t = table(
    symbol(take("600519.SH", n)) as SecurityID,
    take(2026.06.02, n) as TradeDate,
    barTime as BarTime,
    openPrice as Open,
    highPrice as High,
    lowPrice as Low,
    closePrice as Close,
    volume as Volume
)

// 3. Insert the data
kline.min1.append!(t)

// 4. Load the bar data
raw = select BarTime, Close
      from kline.min1
      where SecurityID = `600519.SH
        and TradeDate = 2026.06.02
      order by BarTime

// 5. Compute the fast and slow EMA lines using IIR filters
alphaFast = 2.0 / (10 + 1)
alphaSlow = 2.0 / (30 + 1)

emaResult = select
                BarTime,
                Close,
                iir(Close, [alphaFast], [1.0, -(1.0 - alphaFast)]) as emaFast,
                iir(Close, [alphaSlow], [1.0, -(1.0 - alphaSlow)]) as emaSlow
            from raw

// 6. Generate crossover signals
signals = select
              BarTime,
              Close,
              emaFast,
              emaSlow,
              iif(
                  emaFast > emaSlow and prev(emaFast) <= prev(emaSlow),
                  "BUY",
                  iif(
                      emaFast < emaSlow and prev(emaFast) >= prev(emaSlow),
                      "SELL",
                      "HOLD"
                  )
              ) as signal
          from emaResult
          order by BarTime

// 7. Query all buy and sell signals
select *
from signals
where signal != "HOLD"
order by BarTime

Related Functions: fir