- Introduction
- Digital Signals
- Cross Correlation
- Convolution
- Impulse Function And Impulse Response
- Step Function And Step Response
- FIR Filter Design In Pine
- Calculating Different Types Of Filters In Pine
- Exponential Averager
- Matched Filter
- Differentiator
- Integrator

Pine is a really lightweight scripting language but it still allows for the computation of basic signal processing processes. In this guide, basic techniques and tools used in signal processing are showcased alongside their implementation in Pine. It is recommended to know the basics of Pine before reading this article. Start here if you don’t.

A digital signal is simply a sequence of values expressing a quantity that varies with time. When using Pine, you’ll mostly be processing market prices as your main signal. However it is possible to process/generate a wide variety of digital signals with Pine.

A periodic signal is a signal that repeats itself after some time. The image below shows a few different periodic signals:

Periodic signals possess characteristics such as **cycles**, **period**, **frequency** and **amplitude**. The **cycles** are the number of times the signal repeat himself, the **period** represents the duration of one cycle, the **frequency** is the number of cycles made by the signal in one unit of time and is calculated as the reciprocal of the **period** or `1/period`

, and finally the amplitude is the highest absolute value of the signal.

The simplest periodic signal is the **sine wave** and is computed in Pine as follows:

```
//@version=4
//-----
study("Sine Wave")
pi = 3.14159
n = bar_index
period = 20
amplitude = 1
sin = sin(2*pi*1/period*n)*amplitude
//---- Plot
plot(sin, color=color.blue)
```

As a reminder,

`bar_index`

is defined as the current number of`close`

data points and is just a linear series of values equal to 0, 1, 2, 3, …

Other common periodic signals are:

The **triangular wave** computed in Pine is as follows:

```
//@version=4
study("Triangular Wave")
//-----
pi = 3.14159
n = bar_index
period = 20
amplitude = 1
triangle = acos(sin(2*pi*1/period*n)*amplitude)
//---- Plot
plot(triangle, color=color.blue)
```

The **square wave** computed in Pine is as follows:

```
//@version=4
//-----
study("Square Wave")
pi = 3.14159
n = bar_index
period = 20
amplitude = 1
square = sign(sin(2*pi*1/period*n))*amplitude
//---- Plot
plot(square, color=color.blue)
```

`sign(x)`

is the signum function and yields`1`

when`x > 0`

and`-1`

when`x < 0`

.

The **sawtooth wave** computed in Pine is as follows:

```
//@version=4
study("Sawtooth Wave")
//-----
n = bar_index
period = 20
amplitude = 1
saw = (((n/period)%1 - .5)*2)*amplitude
//---- Plot
plot(saw, color=color.blue)
```

`%`

represents the modulo which is the remaining of a division and is not related to percentage here.

Note that those are not the only ways to compute those signals.

Cross correlation measures the similarity between two signals, preferably stationary with mean ≈ 0. The cross correlation between signals `f`

and `g`

is often denoted by `f★g`

. In Pine, cross correlation can be calculated as follows: `cum(f*g,length)`

and the the running cross correlation of period `length`

as `sum(f*g,length)`

.

Convolution is one of the most important concepts in signal processing. Basically, convolution is a combination of two signals to make a third signal. The convolution
is mathematically denoted by the symbol `*`

, not to be confused with the multiplication symbol in programming.

For example the convolution of a signal `f`

and another signal `g`

is denoted `f*g`

, in Pine this is done as follows:

```
length = input(14)
//-----
convolve = sum(f*g,length)
```

Or alternatively:

```
length = input(14)
//-----
convolve = 0
for i = 0 to length-1
convolve := convolve + f[i]*g[i]
```

It can be seen that convolution is similar to a dot-product. In digital signal processing, convolution is what is used to create certain filters.

An impulse function represents a series of values equal to 0, except at one point in time where the value is equal to 1. We can make an impulse function in Pine with the following code:

```
//@version=4
study("Impulse")
length = input(100)
//-----
n = bar_index
impulse = n == length ? 1 : 0
//---- Plot
plot(impulse, color=color.blue)
```

where the impulse is equal to 1 when `n`

is equal to `length`

.

The impulse response of a system is a system using an impulse function as its input. The impulse response of a filter is the filter output using an impulse function as its input.

The step function, also called *heavy-side step function*, represents a series of values equal to 0 and then to 1 for the rest of the time. The step function is the cumulative sum of the impulse function `step = cum(impulse)`

(therefore the impulse function is the differentiation of the step function or `impulse = change(step)`

). In Pine we can make a step function by using:

```
//@version=4
study("Step")
length = input(100)
//-----
n = bar_index
step = n > length ? 1 : 0
//---- Plot
plot(step, color=color.blue)
```

where the step is equal to 1 when `n`

is greater than `length`

.

The step response of a system is a system using a step function as input. The step response of a filter is the filter output using a step function as input.

Because Pine allows for convolution it is possible to design a wide variety of FIR filters. A filter `filter(input)`

is equal to `input * filter(impulse)`

where `*`

denotes convolution, or more simply a filter output using a certain input is the convolution between the input and the filter impulse response.

In Pine you can make filters using:

```
filter(input) =>
sum = 0.
for i = 1 to length
sum := sum + input[i-1] * w[i-1]
sum
```

where `w[i-1]`

are the filter coefficients (or *filter kernel*). Note that the sum of `w`

must add up to 1 (this is called *total unity*). It is more convenient for `w`

to be expressed as a function `w(x)`

.

When we can’t have total unity (the sum of the coefficients doesn’t add up to 1) we can rescale the convolution, which is done as follows:

```
filter(input)=>
a = 0.
b = 0.
for i = 1 to length
w = sin(i/length)
b := b + w
a := a + input[i-1]*w
a/b
```

Here `w`

does not add to 1, however because we divide the convolution output by the sum of the coefficients (`b`

in the script) we can get the filter without a problem.

You can also look at the following template which allows to design FIR filters.

The simple (or *arithmetic*) moving average (*SMA*) sometimes called *boxcar filter* can be made in different ways with Pine. The standard and most efficient way is to use the built-in `sma(source,length)`

function as follows:

```
//@version=4
study("Sma",overlay=true)
length = input(100)
//-----
Sma = sma(close,length)
//---- Plot
plot(Sma, color=color.blue)
```

This code plots the simple moving average of the closing price of period `length`

.

Another way is to use convolution with:

```
//@version=4
study("Sma",overlay=true)
//-----
Sma(input,length) =>
sum = 0.
for i = 1 to length
sum := sum + input[i-1] * 1/length
sum
//---- Plot
plot(Sma(close,100), color=color.blue)
```

`1/length`

is the filter kernel of the simple moving average.

An alternative way that allows a variable integer as the period can be made using the following code:

```
//@version=4
study("Sma",overlay=true)
//-----
Sma(input,length) =>
a = cum(input)
(a - a[length])/length
//---- Plot
plot(Sma(close,100), color=color.blue)
```

The Gaussian filter is a filter with an impulse response equal to a Gaussian function:

The Gaussian function is calculated using the standard formula:

with *b* = position of the peak and *c* = curve width. Pine can return an approximation of a Gaussian filter using the `alma(input, length, b, c)`

function with `b`

= 0.5.

A Gaussian filter can also be made in Pine via convolution using the following code:

```
//@version=4
study("Gaussian Filter",overlay=true)
//----
gauss(input, length, width)=>
a = 0.
b = 0.
for i = 1 to length
w = exp(-pow(i/length - .5,2)/(2*pow(width,2)))
b := b + w
a := a + input[i-1]*w
a/b
//---- Plot
plot(gauss(close, 100, 0.2), color=color.blue)
```

Filters comes in different types, where the type determines how the filter interacts with the frequency content of the signal.

Low-pass filters are the most widely used type of filters. They remove high-frequency components—the noise—and thus smooth the signal. Most of the filtering functions integrated in Pine (`sma`

, `wma`

, `swma`

, `alma`

, etc.) are low-pass filters.

Low-pass filters posses the following frequency response:

When the filter’s coefficients add up to 1, the filter is a low-pass filter.

High-pass filters remove low-frequency components of a signal, thus keeping higher-frequency components in the signal. They can be made by subtracting the input with the low-pass filter output: `highpass = input - lowpass(input)`

. For example a simple moving average high-pass filter can be made in Pine as follows:

```
//@version=4
study("Highpass SMA",overlay=true)
length = input(14)
//-----
hp = close - sma(close,length)
//---- Plot
plot(hp, color=color.blue)
```

High-pass filters posses the following frequency response:

When the filter’s coefficients add up to 0, the filter is a high-pass filter.

Band-pass filters remove low/high-frequency components of a signal, thus keeping mid-frequency components in the signal. They can be made by applying a low-pass filter to an high-pass filter output: `bandpass = lowpass(highpass(input))`

. For example a simple moving average band-pass filter can be made in Pine as follows:

```
//@version=4
study("Bandpass SMA",overlay=true)
length = input(14)
//-----
bp = sma(close - sma(close,length),length)
//---- Plot
plot(bp, color=color.blue)
```

Band-pass filters posses the following frequency response:

Band-stop filters remove mid-frequency components of a signal, thus keeping low/high-frequency components in the signal. They can be made by subtracting a band-pass filter output from an input: `bandstop = input - bandpass`

. For example, a simple moving average band-stop filter can be made in Pine as follows:

```
//@version=4
study("Bandstop SMA",overlay=true)
length = input(14)
//-----
bs = close - sma(close - sma(close,length),length)
//---- Plot
plot(bs, color=color.blue)
```

Band-stop filters posses the following frequency response:

The exponential averager (also known as *exponential moving average*, *leaky integrator* or *single exponential smoothing*) is a widely used recursive filter. This filter is similar to the simple moving average but its computation is way more efficient. This filter has the following form: `output = α*input+(1-α)*output[1]`

.

In Pine we can use the integrated function `ema(input,length)`

where `α = 2/(length+1)`

, or:

```
ea(input,alpha)=>
s = 0.
s := alpha * input + (1-alpha) * nz(s[1],close)
```

with `1 > alpha > 0`

.

A matched filter is a system that maximizes the signal to noise ratio of the output. The impulse response of those filters are equal to the reversed input. Since Pine allows a variable index, we can build such filters using the following code:

```
filter(x,y) =>
sum = 0.
w = 0.
for i = 0 to length-1
w := w + y[length-i-1]
sum := sum + x[i] * y[length-i-1]
sum/w
```

where `x`

is the input and `y`

the signal of interest.

The first difference differentiator is the simplest of all differentiators and consists in subtracting the previous input value from the current one. In Pine this can be done with `diff = input - input[1]`

or `change(input)`

.

The rectangular integrator is simply a running summation which can be made in Pine with the `cum(signal)`

function, or:

```
a = 0.
a := nz(a[1],input) + input
```

For examples of DSP techniques used in Pine scripts, see my indicators here.