pacificbay / sar

Lucid SAR - a parabolic SAR indicator, written in Pine Script, based on section II of J. Welles Wilder, Jr.'s book “New Concepts in Technical Trading Systems” (1978)
MIT License
21 stars 9 forks source link

Lucid sar to Python #7

Open illi4 opened 2 months ago

illi4 commented 2 months ago

I am trying to refactor the pinescript code to have an ability to calculate lucid SAR in Python. I am not having success and need some guidance / advice. Could someone please advice what I am doing wrong here?

def sar_v2(data, initial=0.02, increment=0.02, maximum=0.2):
  """
  Calculates the Lucid SAR indicator for a given DataFrame.

  Args:
      data: DataFrame containing columns for high, close, open, and low prices.
      initial: Initial acceleration factor.
      increment: Increment for acceleration factor.
      maximum: Maximum value for acceleration factor.

  Returns:
      A new DataFrame with an additional column named 'SAR' containing the Lucid SAR values.
  """
  data['SAR'] = float(data.loc[1, 'low'])  # Initialize SAR column with NaNs
  data['EP'] = float(data.loc[1, 'high'])  # Initialize EP column with NaNs
  data['AF'] = float(initial)  # Initialize AF column with NaNs
  data['uptrend'] = True     # Initialize uptrend flag (assuming uptrend starts)
  data['new_trend'] = False  # Initialize new_trend flag
  reversal_state = 0

  # Start from the 3rd candle to avoid NaNs
  for i in range(2, len(data)):
    # Handle previous values (avoid indexing issues)
    prev_uptrend = data.loc[i-1, 'uptrend']
    prev_new_trend = data.loc[i-1, 'new_trend']
    prev_EP = data.loc[i-1, 'EP']
    prev_SAR = data.loc[i-1, 'SAR']
    prev_AF = data.loc[i-1, 'AF']

    # Check reversal state
    if reversal_state == 0:
        if prev_uptrend:
            data.loc[i, 'EP'] = max(data.loc[i, 'high'], prev_EP)
        else:
            data.loc[i, 'EP'] = min(data.loc[i, 'low'], prev_EP)

        if prev_new_trend:
            data.loc[i, 'AF'] = initial
        else:
            if data.loc[i, 'EP'] != prev_EP:
                data.loc[i, 'AF'] = min(maximum, prev_AF + increment)
            else:
                data.loc[i, 'AF'] = prev_AF

        data.loc[i, 'SAR'] = prev_SAR + data.loc[i, 'AF'] * (data.loc[i, 'EP'] - prev_SAR)

        if prev_uptrend:
            data.loc[i, 'SAR'] = min(data.loc[i, 'SAR'], data.loc[i-1, 'low'])
            data.loc[i, 'SAR'] = min(data.loc[i, 'SAR'], data.loc[i-2, 'low'])
            if data.loc[i, 'SAR'] > data.loc[i, 'low']:
                data.loc[i, 'uptrend'] = False
                data.loc[i, 'new_trend'] = True
                data.loc[i, 'SAR'] = max(data.loc[i, 'high'], data.loc[i-1, 'EP'])
                data.loc[i, 'EP'] = min(data.loc[i, 'low'], data.loc[i-1, 'low'])
                reversal_state = 2
            else:
                data.loc[i, 'uptrend'] = True
                data.loc[i, 'new_trend'] = False
        else: #99
            data.loc[i, 'SAR'] = max(data.loc[i, 'SAR'], data.loc[i-1, 'high'])
            data.loc[i, 'SAR'] = max(data.loc[i, 'SAR'], data.loc[i-2, 'high'])
            if data.loc[i, 'SAR'] < data.loc[i, 'high']:
                data.loc[i, 'uptrend'] = True
                data.loc[i, 'new_trend'] = True
                data.loc[i, 'SAR'] = min(data.loc[i, 'low'], data.loc[i-1, 'EP'])
                data.loc[i, 'EP'] = max(data.loc[i, 'high'], data.loc[i-1, 'high'])
                reversal_state = 1
            else:
                data.loc[i, 'uptrend'] = False
                data.loc[i, 'new_trend'] = False
    # 111
    else:
        if reversal_state == 1:
            data.loc[i, 'EP'] = data.loc[i, 'high']
            if data.loc[i, 'low'] < data.loc[i, 'SAR']:
                data.loc[i, 'SAR'] = data.loc[i, 'EP']
                data.loc[i, 'EP'] = data.loc[i, 'low']
                reversal_state = 2  
                data.loc[i, 'uptrend'] = False
        else:
            data.loc[i, 'EP'] = data.loc[i, 'low']
            if data.loc[i, 'high'] > data.loc[i, 'SAR']:
                data.loc[i, 'SAR'] = data.loc[i, 'EP']
                data.loc[i, 'EP'] = data.loc[i, 'high']
                reversal_state = 1
                data.loc[i, 'uptrend'] = True

  return data
illi4 commented 2 months ago

And another attempt that gives PSAR result rather than Lucid SAR even though the rule to never move the SAR into the previous day’s range or today’s range are in:

def get_psar(barsdata, iaf=0.02, maxaf=0.2):

    length = len(barsdata)
    high = list(barsdata['high'])
    low = list(barsdata['low'])
    close = list(barsdata['close'])
    psar = close[0:len(close)]
    psarbull = [None] * length
    psarbear = [None] * length
    bull = True
    af = iaf
    ep = low[0]
    hp = high[0]
    lp = low[0]

    for i in range(2,length):
        if bull:
            psar[i] = psar[i - 1] + af * (hp - psar[i - 1])
        else:
            psar[i] = psar[i - 1] + af * (lp - psar[i - 1])

        reverse = False

        if bull:
            if low[i] < psar[i]:
                bull = False
                reverse = True
                psar[i] = hp
                lp = low[i]
                af = iaf
        else:
            if high[i] > psar[i]:
                bull = True
                reverse = True
                psar[i] = lp
                hp = high[i]
                af = iaf

        if not reverse:
            if bull:
                if high[i] > hp:
                    hp = high[i]
                    af = min(af + iaf, maxaf)
                if low[i - 1] < psar[i]:
                    psar[i] = low[i - 1]
                if low[i - 2] < psar[i]:
                    psar[i] = low[i - 2]
            else:
                if low[i] < lp:
                    lp = low[i]
                    af = min(af + iaf, maxaf)
                if high[i - 1] > psar[i]:
                    psar[i] = high[i - 1]
                if high[i - 2] > psar[i]:
                    psar[i] = high[i - 2]

        if bull:
            psarbull[i] = psar[i]
            barsdata.loc[i, 'PSAR_direction'] = 1
            barsdata.loc[i, 'PSAR_val'] = psar[i]
        else:
            psarbear[i] = psar[i]
            barsdata.loc[i, 'PSAR_direction'] = -1
            barsdata.loc[i, 'PSAR_val'] = psar[i]

    return barsdata