Parabolic SAR in Python….PSAR keep growing instead of reversing

Question:

I have a pandas dataframe of Open/high/low/close stock prices and I am writing to write a function that will add Parabolic SAR to my dataframe. Right now the PSAR number just grows insanely huge and i never seem to get much in terms of flipping between bull and bear directions. Any help in understanding why my PSAR grows so crazy would be great. I’ve tried several variations on this code to no avail.

For those who are not familiar with PSAR:

  • Prior SAR: The SAR value for the previous period.

Rising SAR

  • Extreme Point (EP): The highest high of the current uptrend.
  • Acceleration Factor (AF): Starting at .02, AF increases by .02 each time the extreme point makes a new high. AF can reach a maximum of .20, no matter how long the uptrend extends.
  • The Acceleration Factor is multiplied by the difference between the Extreme Point and the prior period’s SAR. This is then added to the prior period’s SAR. Note however that SAR can never be above the prior two periods’ lows. Should SAR be above one of those lows, use the lowest of the two for SAR.

    Current SAR = Prior SAR + Prior AF(Prior EP - Prior SAR)

Eg: 13-Apr-10: SAR = 48.28 = 48.13 + .14(49.20 – 48.13)

Falling SAR

  • Extreme Point (EP): The lowest low of the current downtrend.
  • Acceleration Factor (AF): Starting at .02, AF increases by .02 each time the extreme point makes a new low. AF can reach a maximum of .20, no matter how long the downtrend extends.
  • The Acceleration Factor is multiplied by the difference between the Prior period’s SAR and the Extreme Point. This is then subtracted from the prior period’s SAR. Note however that SAR can never be below the prior two periods’ highs. Should SAR be below one of those highs, use the highest of the two for SAR.

    Current SAR = Prior SAR - Prior AF(Prior EP - Prior SAR)

Eg: 9-Feb-10: SAR = 43.56 = 43.84 – .16(43.84 – 42.07)

During Reversal, the PSAR becomes the prior extreme point EP, and the new EP is the previous high or low depending on the direction of the flip. The AF resets to 0.02.

My function:

def addSAR(df):
    df.loc[0, 'AF'] =0.02
    df.loc[0, 'PSAR'] = df.loc[0, 'low']
    df.loc[0, 'EP'] = df.loc[0, 'high']
    df.loc[0, 'PSARdir'] = "bull"

    for a in range(1, len(df)):

        if df.loc[a-1, 'PSARdir'] == 'bull':

            df.loc[a, 'PSAR'] = df.loc[a-1, 'PSAR'] + (df.loc[a-1, 'AF']*(df.loc[a-1, 'EP']-df.loc[a-1, 'PSAR']))            

            df.loc[a, 'PSARdir'] = "bull"

            if df.loc[a, 'low'] < df.loc[a-1, 'PSAR']:
                df.loc[a, 'PSARdir'] = "bear"
                df.loc[a, 'PSAR'] = df.loc[a-1, 'EP']
                df.loc[a, 'EP'] = df.loc[a-1, 'low']
                df.loc[a, 'AF'] = .02

            else:
                if df.loc[a, 'high'] > df.loc[a-1, 'EP']:
                    df.loc[a, 'EP'] = df.loc[a, 'high']
                    if df.loc[a-1, 'AF'] <= 0.18:
                        df.loc[a, 'AF'] =df.loc[a-1, 'AF'] + 0.02
                    else:
                        df.loc[a, 'AF'] = df.loc[a-1, 'AF']
                elif df.loc[a, 'high'] <= df.loc[a-1, 'EP']:
                    df.loc[a, 'AF'] = df.loc[a-1, 'AF']
                    df.loc[a, 'EP'] = df.loc[a-1, 'EP']               



        elif df.loc[a-1, 'PSARdir'] == 'bear':

            df.loc[a, 'PSAR'] = df.loc[a-1, 'PSAR'] - (df.loc[a-1, 'AF']*(df.loc[a-1, 'EP']-df.loc[a-1, 'PSAR']))

            df.loc[a, 'PSARdir'] = "bear"

            if df.loc[a, 'high'] > df.loc[a-1, 'PSAR']:
                df.loc[a, 'PSARdir'] = "bull"
                df.loc[a, 'PSAR'] = df.loc[a-1, 'EP']
                df.loc[a, 'EP'] = df.loc[a-1, 'high']
                df.loc[a, 'AF'] = .02

            else:
                if df.loc[a, 'low'] < df.loc[a-1, 'EP']:
                    df.loc[a, 'EP'] = df.loc[a, 'low']
                    if df.loc[a-1, 'AF'] <= 0.18:
                        df.loc[a, 'AF'] = df.loc[a-1, 'AF'] + 0.02
                    else:
                        df.loc[a, 'AF'] = df.loc[a-1, 'AF']

                elif df.loc[a, 'low'] >= df.loc[a-1, 'EP']:
                    df.loc[a, 'AF'] = df.loc[a-1, 'AF']
                    df.loc[a, 'EP'] = df.loc[a-1, 'EP']           

    return df
Asked By: Greg D

||

Answers:

figured it out

df.loc[a, 'PSAR'] = df.loc[a-1, 'PSAR'] + (df.loc[a-1, 'AF']*(df.loc[a-1, 'EP']-df.loc[a-1, 'PSAR'])) 

Should be df.loc[a, 'PSAR'] = df.loc[a-1, 'PSAR'] + (df.loc[a-1, 'AF']*(df.loc[a-1, 'PSAR']-df.loc[a-1, 'EP']))

last two variables transposed!

now I can clean up the function and make it better.

Answered By: Greg D

For my case my dataframe’s index is datetime so i converted all a to df.index[a] and a-1 to df.index[a-1]. Also i changed AF,PSAR,EP,PSARdir columns as all row values will be initialized as same e.g. df['AF'] =0.02

def addSAR(df):
    df['AF'] =0.02
    df['PSAR'] = df['Low']
    df['EP'] = df['High']
    df['PSARdir'] = "bull"

    for a in range(1, len(df)):

        if df.loc[df.index[a-1], 'PSARdir'] == 'bull':

            df.loc[df.index[a], 'PSAR'] = df.loc[df.index[a-1], 'PSAR'] + (df.loc[df.index[a-1], 'AF']*(df.loc[df.index[a-1], 'PSAR']-df.loc[df.index[a-1], 'EP']))          

            df.loc[df.index[a], 'PSARdir'] = "bull"

            if df.loc[df.index[a], 'Low'] < df.loc[df.index[a-1], 'PSAR']:
                df.loc[df.index[a], 'PSARdir'] = "bear"
                df.loc[df.index[a], 'PSAR'] = df.loc[df.index[a-1], 'EP']
                df.loc[df.index[a], 'EP'] = df.loc[df.index[a-1], 'Low']
                df.loc[df.index[a], 'AF'] = .02

            else:
                if df.loc[df.index[a], 'High'] > df.loc[df.index[a-1], 'EP']:
                    df.loc[df.index[a], 'EP'] = df.loc[df.index[a], 'High']
                    if df.loc[df.index[a-1], 'AF'] <= 0.18:
                        df.loc[df.index[a], 'AF'] =df.loc[df.index[a-1], 'AF'] + 0.02
                    else:
                        df.loc[df.index[a], 'AF'] = df.loc[df.index[a-1], 'AF']
                elif df.loc[df.index[a], 'High'] <= df.loc[df.index[a-1], 'EP']:
                    df.loc[df.index[a], 'AF'] = df.loc[df.index[a-1], 'AF']
                    df.loc[df.index[a], 'EP'] = df.loc[df.index[a-1], 'EP']               



        elif df.loc[df.index[a-1], 'PSARdir'] == 'bear':

            df.loc[df.index[a], 'PSAR'] = df.loc[df.index[a-1], 'PSAR'] - (df.loc[df.index[a-1], 'AF']*(df.loc[df.index[a-1], 'EP']-df.loc[df.index[a-1], 'PSAR']))

            df.loc[df.index[a], 'PSARdir'] = "bear"

            if df.loc[df.index[a], 'High'] > df.loc[df.index[a-1], 'PSAR']:
                df.loc[df.index[a], 'PSARdir'] = "bull"
                df.loc[df.index[a], 'PSAR'] = df.loc[df.index[a-1], 'EP']
                df.loc[df.index[a], 'EP'] = df.loc[df.index[a-1], 'High']
                df.loc[df.index[a], 'AF'] = .02

            else:
                if df.loc[df.index[a], 'Low'] < df.loc[df.index[a-1], 'EP']:
                    df.loc[df.index[a], 'EP'] = df.loc[df.index[a], 'Low']
                    if df.loc[df.index[a-1], 'AF'] <= 0.18:
                        df.loc[df.index[a], 'AF'] = df.loc[df.index[a-1], 'AF'] + 0.02
                    else:
                        df.loc[df.index[a], 'AF'] = df.loc[df.index[a-1], 'AF']

                elif df.loc[df.index[a], 'Low'] >= df.loc[df.index[a-1], 'EP']:
                    df.loc[df.index[a], 'AF'] = df.loc[df.index[a-1], 'AF']
                    df.loc[df.index[a], 'EP'] = df.loc[df.index[a-1], 'EP']           

    return df
Answered By: M.SEL

I updated the script, fixed the bug with the increasing PSAR value and also overlapping PSAR with high/low price.
Now this function gives the exact same PSAR like TradingView.
enter image description here

def PSAR(df, af=0.02, max=0.2):
df.loc[0, 'AF'] = 0.02
df.loc[0, 'PSAR'] = df.loc[0, 'low']
df.loc[0, 'EP'] = df.loc[0, 'high']
df.loc[0, 'PSARdir'] = "bull"

for a in range(1, len(df)):
    if df.loc[a-1, 'PSARdir'] == 'bull':
        df.loc[a, 'PSAR'] = df.loc[a-1, 'PSAR'] + (df.loc[a-1, 'AF']*(df.loc[a-1, 'EP']-df.loc[a-1, 'PSAR']))
        df.loc[a, 'PSARdir'] = "bull"

        if df.loc[a, 'low'] < df.loc[a-1, 'PSAR'] or df.loc[a, 'low'] < df.loc[a, 'PSAR']:
            df.loc[a, 'PSARdir'] = "bear"
            df.loc[a, 'PSAR'] = df.loc[a-1, 'EP']
            df.loc[a, 'EP'] = df.loc[a-1, 'low']
            df.loc[a, 'AF'] = af
        else:
            if df.loc[a, 'high'] > df.loc[a-1, 'EP']:
                df.loc[a, 'EP'] = df.loc[a, 'high']
                if df.loc[a-1, 'AF'] <= 0.18:
                    df.loc[a, 'AF'] =df.loc[a-1, 'AF'] + af
                else:
                    df.loc[a, 'AF'] = df.loc[a-1, 'AF']
            elif df.loc[a, 'high'] <= df.loc[a-1, 'EP']:
                df.loc[a, 'AF'] = df.loc[a-1, 'AF']
                df.loc[a, 'EP'] = df.loc[a-1, 'EP']

    elif df.loc[a-1, 'PSARdir'] == 'bear':
        df.loc[a, 'PSAR'] = df.loc[a-1, 'PSAR'] - (df.loc[a-1, 'AF']*(df.loc[a-1, 'PSAR']-df.loc[a-1, 'EP']))
        df.loc[a, 'PSARdir'] = "bear"

        if df.loc[a, 'high'] > df.loc[a-1, 'PSAR'] or df.loc[a, 'high'] > df.loc[a, 'PSAR']:
            df.loc[a, 'PSARdir'] = "bull"
            df.loc[a, 'PSAR'] = df.loc[a-1, 'EP']
            df.loc[a, 'EP'] = df.loc[a-1, 'high']
            df.loc[a, 'AF'] = af
        else:
            if df.loc[a, 'low'] < df.loc[a-1, 'EP']:
                df.loc[a, 'EP'] = df.loc[a, 'low']
                if df.loc[a-1, 'AF'] < max:
                    df.loc[a, 'AF'] = df.loc[a-1, 'AF'] + af
                else:
                    df.loc[a, 'AF'] = df.loc[a-1, 'AF']

            elif df.loc[a, 'low'] >= df.loc[a-1, 'EP']:
                df.loc[a, 'AF'] = df.loc[a-1, 'AF']
                df.loc[a, 'EP'] = df.loc[a-1, 'EP']
return df
Answered By: Dennis

it’s about 100x faster if you turn if from a df to a dict of dict before:

    psar = df.to_dict('index')

    print("Calculating PSAR for {}".format(symbol))

    psar[0]['AF'] = af
    psar[0]['PSAR'] = psar[0]['Low']
    psar[0]['EP'] = psar[0]['High']
    psar[0]['PSARdir'] = 'bull'

    i = list(psar.keys())[1:]

    for i in list(psar.keys())[1:]: # start on second data row
        prev_i = i - 1
        if psar[prev_i]['PSARdir'] == 'bull':
            psar[i]['PSAR'] = psar[prev_i]['PSAR'] + (psar[prev_i]['AF'] * (psar[prev_i]['EP'] - psar[prev_i]['PSAR']))
            psar[i]['PSARdir'] = 'bull'

            if psar[i]['Low'] < psar[prev_i]['PSAR'] or psar[i]['Low'] < psar[i]['PSAR']:
                psar[i]['PSARdir'] = 'bear'
                psar[i]['PSAR'] = psar[prev_i]['EP']
                psar[i]['EP'] = psar[prev_i]['Low']
                psar[i]['AF'] = af

            else:
                if psar[i]['High'] > psar[prev_i]['EP']:
                    psar[i]['EP'] = psar[i]['High']
                    psar[i]['AF'] = min(max, psar[prev_i]['AF'] + af)
                else:
                    psar[i]['AF'] = psar[prev_i]['AF']
                    psar[i]['EP'] = psar[prev_i]['EP']

        else:
            psar[i]['PSAR'] = psar[prev_i]['PSAR'] - (psar[prev_i]['AF'] * (psar[prev_i]['PSAR'] - psar[prev_i]['EP']))
            psar[i]['PSARdir'] = 'bear'

            if psar[i]['High'] > psar[prev_i]['PSAR'] or psar[i]['High'] > psar[i]['PSAR']:
                psar[i]['PSARdir'] = 'bull'
                psar[i]['PSAR'] = psar[prev_i]['EP']
                psar[i]['EP'] = psar[prev_i]['High']
                psar[i]['AF'] = af
            else:
                if psar[i]['Low'] < psar[prev_i]['EP']:
                    psar[i]['EP'] = psar[i]['Low']
                    psar[i]['AF'] = min(max, psar[prev_i]['AF'] + af)
                else:
                    psar[i]['AF'] = psar[prev_i]['AF']
                    psar[i]['EP'] = psar[prev_i]['EP']

    df = pd.DataFrame.from_dict(psar, orient='index')
Answered By: Mike