pandas-ta: PSAR indicator - Incorrect value

Which version are you running? The lastest version is on Github. Pip is for major releases.

import pandas_ta as ta
print(ta.version)

0.2.28b0

Upgrade.

$ pip install -U git+https://github.com/twopirllc/pandas-ta

Describe the bug A clear and concise description of what the bug is. The value of PSAR seems incorrect (as compared to TradingView).

– Pandas_ta for 2020-12-31 PSARs_0.02_0.2 = 377.265728 TradingView for 2020-12-31 PSAR = 363.67

To Reproduce Provide sample code.

from pandas_datareader import data
from IPython.display import display, HTML
import pandas as pd
import datetime
import pandas_ta as ta

start = datetime.datetime(2020, 1, 1)
end = datetime.datetime.now()
ticker = "SPY"
df = data.DataReader(ticker, 
                       start=start, 
                       end=end, 
                       data_source='yahoo')
df.ta.psar(append=True)
display(HTML(df.sort_index(ascending=False).to_html()))

Expected behavior A clear and concise description of what you expected to happen. The value of PSAR should be as close as possible to TradingView Screenshots If applicable, add screenshots to help explain your problem.

Additional context Add any other context about the problem here.

Thanks for using Pandas TA!

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 23 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Hi @twopirllc, I’ve made some changes as well as I’ve done some correlation tests, but I have no idea if it’s enough.

Code:

def psar(high, low, close=None, af=None, max_af=None, offset=None, **kwargs):
    """Indicator: Parabolic Stop and Reverse (PSAR)"""
    # Validate Arguments
    high = verify_series(high)
    low = verify_series(low)
    af = float(af) if af and af > 0 else 0.02
    max_af = float(max_af) if max_af and max_af > 0 else 0.2
    offset = get_offset(offset)

    falling = True
    sar = high.iloc[0]
    ep = low.iloc[0]
    af0 = af

    if close is not None:
        close = verify_series(close)
        sar = close.iloc[0]

    long = Series(npNaN, index=high.index)
    short = long.copy()
    reversal = Series(False, index=high.index)
    _af = long.copy()
    _af.iloc[0:2] = af0

    m = high.shape[0]

    # Calculate Result
    for row in range(2, m):
        HIGH = high.iloc[row]
        LOW = low.iloc[row]

        if falling:
            new_sar = sar + af * (ep - sar)
            reverse = HIGH > new_sar

            if LOW < ep:
                ep = LOW
                af = min(af + af0, max_af)

            new_sar = max(high.iloc[row - 1], high.iloc[row - 2], new_sar)
        else:
            new_sar = sar + af * (ep - sar)
            reverse = LOW < new_sar

            if HIGH > ep:
                ep = HIGH
                af = min(af + af0, max_af)

            new_sar = min(low.iloc[row - 1], low.iloc[row - 2], new_sar)

        if reverse:
            new_sar = ep
            af = af0
            falling = not falling

            if falling:
                ep = LOW
            else:
                ep = HIGH

        sar = new_sar

        if not falling:
            long.iloc[row] = sar
        else:
            short.iloc[row] = sar

        _af.iloc[row] = af
        reversal.iloc[row] = reverse

    # Offset
    if offset != 0:
        _af = _af.shift(offset)
        long = long.shift(offset)
        short = short.shift(offset)
        reversal = reversal.shift(offset)

    # Handle fills
    if "fillna" in kwargs:
        _af.fillna(kwargs["fillna"], inplace=True)
        long.fillna(kwargs["fillna"], inplace=True)
        short.fillna(kwargs["fillna"], inplace=True)
        reversal.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        _af.fillna(method=kwargs["fill_method"], inplace=True)
        long.fillna(method=kwargs["fill_method"], inplace=True)
        short.fillna(method=kwargs["fill_method"], inplace=True)
        reversal.fillna(method=kwargs["fill_method"], inplace=True)

    # Prepare DataFrame to return
    _params = f"_{af0}_{max_af}"
    data = {
        f"PSARl{_params}": long,
        f"PSARs{_params}": short,
        f"PSARaf{_params}": _af,
        f"PSARr{_params}": reversal,
    }
    psardf = DataFrame(data)
    psardf.name = f"PSAR{_params}"
    psardf.category = long.category = short.category = "trend"

    return psardf

Should be reviewd.

Tests:

import pandas as pd
import pandas_ta as ta

data = pd.read_csv("1.csv")  # Read exported 1m data from TV with ParabolicSAR
tv_psar = data['ParabolicSAR'].values

psar = data.ta.psar(fillna=0)
ta_psar = psar['PSARl_0.02_0.2'].values + psar['PSARs_0.02_0.2'].values

psarDf = pd.DataFrame({
    'psar_tv': tv_psar[10:],  # Ignore first first few values because of self correction
    'psar_ta': ta_psar[10:],  # Ignore first first few values because of self correction
})

print(psarDf.tail(20))
print(psarDf.corr())

Results 1m data:

        psar_tv   psar_ta
5816  1.543524  1.543524
5817  1.543254  1.543254
5818  1.542988  1.542988
5819  1.542729  1.542729
5820  1.542120  1.542120
5821  1.541212  1.541212
5822  1.540360  1.540360
5823  1.539558  1.539558
5824  1.538805  1.538805
5825  1.538096  1.538096
5826  1.537161  1.537161
5827  1.536300  1.536300
5828  1.535040  1.535040
5829  1.533906  1.533906
5830  1.532609  1.532609
5831  1.531468  1.531468
5832  1.530464  1.530464
5833  1.529580  1.529580
5834  1.528803  1.528803
5835  1.528118  1.528118
         psar_tv  psar_ta
psar_tv      1.0      1.0
psar_ta      1.0      1.0

Results 4h data:

       psar_tv   psar_ta
5081  1.608000  1.608000
5082  1.610592  1.610592
5083  1.737600  1.737600
5084  1.733848  1.733848
5085  1.723342  1.723342
5086  1.713256  1.713256
5087  1.703574  1.703574
5088  1.685360  1.685360
5089  1.668238  1.668238
5090  1.652144  1.652144
5091  1.637015  1.637015
5092  1.622794  1.622794
5093  1.400000  1.400000
5094  1.404566  1.404566
5095  1.413783  1.413783
5096  1.422632  1.422632
5097  1.435542  1.435542
5098  1.447678  1.447678
5099  1.459085  1.459085
5100  1.469808  1.469808
         psar_tv  psar_ta
psar_tv      1.0      1.0
psar_ta      1.0      1.0

Hope that’s ok and helpful as well.

Kind regards, AA

Hello, I’ve tried reproduce TV psar from chart today, but i put there no more than half of hour. I wrote code in different language but here is python version (hope that it’s ok). This code produced similar results to TV on some live tests, but I have no time to test it properly. Maybe it’ll help, but I think that definition of psar is slightly different.

def psar(high, low, af=0.02, af_max=0.2):
    falling = True
    sar = high[0]
    ep = low[0]
    af0 = af

    psar_l = [0] * len(high)
    psar_s = [0] * len(high)

    for row in range(1, len(high)):
        HIGH = high[row]
        LOW = low[row]

        if falling:
            if LOW < ep:
                ep = LOW
                af = min(af + af0, af_max)
    
            new_sar = sar + af * (ep - sar)
            new_sar = max(HIGH, new_sar)
    
            reverse = HIGH > sar
        else: 
            if HIGH > ep:
                ep = HIGH
                af = min(af + af0, af_max)
    
            new_sar = sar + af * (ep - sar)
            new_sar = min(LOW, new_sar)
    
            reverse = LOW < sar

        if reverse:
            new_sar = ep
            af = af0
            falling = not falling
    
            if falling:
                ep = LOW
            else:
                ep = HIGH

        sar = new_sar

        if not falling:
            psar_l[row] = sar
        else:
            psar_s[row] = sar

    return psar_l, psar_s

Let me know if there are some incorrections and I’ll take a look.

Best regards, AA