Notebook
In [66]:
import numpy as np
from matplotlib import pyplot as plt
import pandas as pd
In [67]:
df = get_pricing(symbols('SPY'), start_date='2018-01-01', end_date='2020-01-01')

Revised version of the github code, the calculation for alpha had some errors

In [68]:
Price = df.close_price
Length = len(df)
InputPrice = Price.copy()
batch = 10

# Initialize output before the algorithm
Filt = np.array(InputPrice)


# sequencially calculate all variables and the output
for i in range(0, Length, 1):
    # If there's not enough data, Filt is the price - whitch it already is, so just skip
    if i < 2 * batch:
        continue
    # take 2 batches of the input
    v1 = InputPrice[i-2*batch:i - batch]
    v2 = InputPrice[i - batch:i]

    # for the 1st batch calculate N1
    H1 = np.max(v1)
    L1 = np.min(v1)
    N1 = (H1 - L1) / batch

    # for the 2nd batch calculate N2
    H2 = np.max(v2)
    L2 = np.min(v2)
    N2 = (H2 - L2) / batch

    # for both batches calculate N3
    H = np.max([H1, H2])
    L = np.min([L1, L2])
    N3 = (H - L) / (2 * batch)

    # calculate fractal dimension
    Dimen = 0
    if N1 > 0 and N2 > 0 and N3 > 0:
        Dimen = (np.log(N1 + N2) - np.log(N3)) / np.log(2)

    # calculate lowpass filter factor
    alpha = np.exp(-4.6 * (Dimen - 1)) # this was wrongly calculated as np.exp(-4.6 * (Dimen) - 1)
    alpha = np.max([alpha, 0.01]) # here it was 0.1 but Ehler clips at 0.01
    alpha = np.min([alpha, 1])

    # filter the input data
    Filt[i] = alpha * InputPrice[i] + (1 - alpha) * Filt[i-1]
    # if currentBar < 2*batch + 1: <--- i dont get what these 2 lines do
    # Filt = InputPrice[i]

This fuction only works with a pandas Series or DataFrame, but it can compute the FRAMA for multiple assets

In [69]:
def pd_frama(c, per):
    c = c.copy()
    window = per * 2

    hh = c.rolling(per).max()
    ll = c.rolling(per).min()

    n1 = (hh - ll) / per
    n2 = n1.shift(per)

    hh2 = c.rolling(window).max()
    ll2 = c.rolling(window).min()
    n3 = (hh2 - ll2) / window

    D = (np.log(n1 + n2) - np.log(n3)) / np.log(2)
    alp = np.exp(-4.6 * (D - 1))
    alp = np.clip(alp, .01, 1).values

    filt = c.values
    for i, x in enumerate(alp):
        cl = c.values[i]
        if i < window:
            continue
        filt[i] = cl * x + (1 - x) * filt[i - 1]

    return filt

First let's see if it does the same

  1. Plot the result of the first version
In [70]:
df['filt'] = Filt
df.filt.plot(color='y')
Price.plot(color='k')
Out[70]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb03ae44dd8>
  1. Call the new function and plot it
In [71]:
filt2 = pd_frama(df.close_price, batch)

filt2_df = pd.DataFrame(filt2, index=df.index)
filt2_df.plot(color='y')
Price.plot(color='k')
Out[71]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb037b25978>

Looks the same. Now let's get the close price for 2 symbols

In [72]:
df2 = get_pricing(symbols(['SPY', 'QQQ']), start_date='2018-01-01', end_date='2020-01-01', fields='close_price')
In [73]:
df2.head()
Out[73]:
Equity(8554 [SPY]) Equity(19920 [QQQ])
2018-01-02 00:00:00+00:00 258.879 155.861
2018-01-03 00:00:00+00:00 260.497 157.386
2018-01-04 00:00:00+00:00 261.614 157.671
2018-01-05 00:00:00+00:00 263.309 159.215
2018-01-08 00:00:00+00:00 263.829 159.825

Apply the filter to these symbols

In [74]:
filt3 = pd_frama(df2, 20)
filt_df = pd.DataFrame(filt3, index=df2.index, columns=df2.columns)
In [ ]:
 
In [75]:
plt.plot(filt_df)
plt.plot(df2)
Out[75]:
[<matplotlib.lines.Line2D at 0x7fb03db2dda0>,
 <matplotlib.lines.Line2D at 0x7fb038639ac8>]
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]: