Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PMAX indicator #148

Closed
tarantula3535 opened this issue Feb 2, 2021 · 8 comments
Closed

PMAX indicator #148

tarantula3535 opened this issue Feb 2, 2021 · 8 comments

Comments

@tarantula3535
Copy link

I mentioned here #97. an indicator that I like and use..

def PMAX(dataframe, period = 10, multiplier = 3, length=12, MAtype=1, src=1):
    """
    Function to compute PMAX
    
    Args :
        df : Pandas DataFrame which contains ['date', 'open', 'high', 'low', 'close', 'volume'] columns
        period : Integer indicates the period of computation in terms of number of candles
        multiplier : Integer indicates value to multiply the ATR
        length: moving averages length
        MAtype: type of the moving average
        
    Returns :
        df : Pandas DataFrame with new columns added for 
            True Range (TR), ATR (ATR_$period)
            PMAX (pm_$period_$multiplier_$length_$Matypeint)
            PMAX Direction (pmX_$period_$multiplier_$length_$Matypeint)
    """
    import talib.abstract as ta
    df = dataframe.copy()
    mavalue = 'MA_' + str(MAtype) + '_' + str(length)
    atr = 'ATR_' + str(period)
    df[atr]=ta.ATR(df , timeperiod = period)
    pm = 'pm_' + str(period) + '_' + str(multiplier) + '_' + str(length) + '_' + str(MAtype)
    pmx = 'pmX_' + str(period) + '_' + str(multiplier) + '_' + str(length) + '_' + str(MAtype)
    # MAtype==1 --> EMA
    # MAtype==2 --> DEMA
    # MAtype==3 --> T3
    # MAtype==4 --> SMA
    # MAtype==5 --> VIDYA
    # MAtype==6 --> TEMA
    # MAtype==7 --> WMA
    # MAtype==8 --> VWMA
    # MAtype==9 --> zema
    if src == 1:
        masrc=df["close"]
    elif src == 2:
        masrc = (df["high"] + df["low"]) / 2
    elif src == 3:
        masrc = (df["high"] + df["low"]+ df["close"] + df["open"]) / 4
    if MAtype==1:
        df[mavalue]= ta.EMA(masrc , timeperiod = length)
    elif MAtype==2:
        df[mavalue]= ta.DEMA(masrc , timeperiod = length)
    elif MAtype==3:
        df[mavalue]= ta.T3(masrc , timeperiod = length)
    elif MAtype==4:
        df[mavalue]= ta.SMA(masrc , timeperiod = length)
    elif MAtype==5:
        df[mavalue]= VIDYA(df , length= length)
    elif MAtype==6:
        df[mavalue]= ta.TEMA(masrc , timeperiod = length)
    elif MAtype==7:
        df[mavalue]= ta.WMA(df , timeperiod = length)
    elif MAtype==8:
        df[mavalue]= vwma(df , length)
    elif MAtype==9:
        df[mavalue]= zema(df , period=length)
    # Compute basic upper and lower bands
    df['basic_ub'] = df[mavalue] + (multiplier * df[atr])
    df['basic_lb'] = df[mavalue] - (multiplier * df[atr])
    # Compute final upper and lower bands
    df['final_ub'] = 0.00
    df['final_lb'] = 0.00
    for i in range(period, len(df)):
        df['final_ub'].iat[i] = df['basic_ub'].iat[i] if df['basic_ub'].iat[i] < df['final_ub'].iat[i - 1] or df[mavalue].iat[i - 1] > df['final_ub'].iat[i - 1] else df['final_ub'].iat[i - 1]
        df['final_lb'].iat[i] = df['basic_lb'].iat[i] if df['basic_lb'].iat[i] > df['final_lb'].iat[i - 1] or df[mavalue].iat[i - 1] < df['final_lb'].iat[i - 1] else df['final_lb'].iat[i - 1]
       
    # Set the Pmax value
    df[pm] = 0.00
    for i in range(period, len(df)):
        df[pm].iat[i] = df['final_ub'].iat[i] if df[pm].iat[i - 1] == df['final_ub'].iat[i - 1] and df[mavalue].iat[i] <= df['final_ub'].iat[i] else \
                        df['final_lb'].iat[i] if df[pm].iat[i - 1] == df['final_ub'].iat[i - 1] and df[mavalue].iat[i] >  df['final_ub'].iat[i] else \
                        df['final_lb'].iat[i] if df[pm].iat[i - 1] == df['final_lb'].iat[i - 1] and df[mavalue].iat[i] >= df['final_lb'].iat[i] else \
                        df['final_ub'].iat[i] if df[pm].iat[i - 1] == df['final_lb'].iat[i - 1] and df[mavalue].iat[i] <  df['final_lb'].iat[i] else 0.00 
    # Mark the trend direction up/down
    df[pmx] = np.where((df[pm] > 0.00), np.where((df[mavalue] < df[pm]), 'down',  'up'), np.NaN)
    # Remove basic and final bands from the columns
    df.drop(['basic_ub', 'basic_lb', 'final_ub', 'final_lb'], inplace=True, axis=1)
    
    df.fillna(0, inplace=True)

    return df
@tarantula3535
Copy link
Author

https://tr.tradingview.com/v/sU9molfV/
it is the tradingview' link..
https://www.youtube.com/watch?v=yR6tkDTTjCQ
this link is the explanations with subtitles..
I hope it will be useful...

@xmatthias
Copy link
Member

please do not post it here as issue (moving the work to others) - but implement this as a Pull request - so we can review and merge it eventually.
I don't see why i should do the work of "copy-pasting" indicators from issues into code just because you don't like doing Pull requests.

@tarantula3535
Copy link
Author

sorry i close the issue..

@xmatthias
Copy link
Member

@tarantula3535 now don't get me wrong - i'm not saying this indicator is not useful - but you can basically use the "edit file" button on github to add this in there.

Now doing this on a cloned version has advantages and is probably easier to get right (as you can run flake8 to check if the layout is correct) - but the overall flow to create a pull request is a lot less work than actually writing this indicator.

pfakanator added a commit to pfakanator/technical that referenced this issue Feb 20, 2021
Added PMAX from @tarantula3535 as discussed in freqtrade#148
@AetherWaves
Copy link

I played with the indicator some time and figured out the recursive calculation of pmax data is very heavy making parameter optimization quite difficult. The range for i in range(period, len(df)): i the for loop will be growing with the size of the dataframe ? Some ideas to speed up the recursive indicator calculation (numpy, cython, vectorization etc.) ?

@xmatthias
Copy link
Member

cython shuld work. Numpy is already used - and vectorization is not possible if one column's result depends on the same column from the prior row (df['aaa'].iat[i] = df['aaa'].iat[i -1] +/- whatever).

I've been playing with the idea of using cython for a while in my head - but never got around to actually do something with it.

If you're familiar with that - please submit a PR adding this (results should be identical, obviously - but the speed should be superior).
We're not depending on cython at the moment, so some dependencies might change (i think i can help with that eventually) - and if this works well, it can for sure serve as a sample for other inicators which would also greatly benefit from this.

@AetherWaves
Copy link

AetherWaves commented Aug 2, 2021

Hello Matthias,
here some value to represent the porformance advantages of cython in case of the PMAX indicator:

Array size: 5000, Python: 1.048s, Cython: 8.976ms, Speed up factor: 116.7
Array size: 10000, Python: 2.132s, Cython: 12.939ms, Speed up factor: 164.7
Array size: 15000, Python: 3.237s, Cython: 16.991ms, Speed up factor: 190.5
Array size: 20000, Python: 4.374s, Cython: 21.910ms, Speed up factor: 199.6
Array size: 25000, Python: 5.531s, Cython: 25.956ms, Speed up factor: 213.1
Array size: 30000, Python: 6.866s, Cython: 32.886ms, Speed up factor: 208.8
Array size: 35000, Python: 8.114s, Cython: 35.908ms, Speed up factor: 226.0
Array size: 40000, Python: 9.474s, Cython: 40.879ms, Speed up factor: 231.7
Array size: 45000, Python: 10.650s, Cython: 42.916ms, Speed up factor: 248.2

Cython is quite easy to implement. the compiled library under windows 10 gets tha name .cp39-win_amd64.pyd
I guess the other indicators could benefit from it too.

def PMAX(dataframe, period=10, multiplier=3, length=12, MAtype=1, src=1):  # noqa: C901
    """
    Function to compute PMAX
    Source: https://www.tradingview.com/script/sU9molfV/
    Pinescript Author: KivancOzbilgic

    Args :
        df : Pandas DataFrame with the columns ['date', 'open', 'high', 'low', 'close', 'volume']
        period : Integer indicates the period of computation in terms of number of candles
        multiplier : Integer indicates value to multiply the ATR
        length: moving averages length
        MAtype: type of the moving average

    Returns :
        df : Pandas DataFrame with new columns added for
            True Range (TR), ATR (ATR_$period)
            PMAX (pm_$period_$multiplier_$length_$Matypeint)
            PMAX Direction (pmX_$period_$multiplier_$length_$Matypeint)
    """

    df = dataframe.copy()

    mavalue = "MA_" + str(MAtype) + "_" + str(length)
    atr = "ATR_" + str(period)
    df[atr] = ta.ATR(df, timeperiod=period)
    pm = "pm_" + str(period) + "_" + str(multiplier) + "_" + str(length) + "_" + str(MAtype)
    pmx = "pmX_" + str(period) + "_" + str(multiplier) + "_" + str(length) + "_" + str(MAtype)
    # MAtype==1 --> EMA
    # MAtype==2 --> DEMA
    # MAtype==3 --> T3
    # MAtype==4 --> SMA
    # MAtype==5 --> VIDYA
    # MAtype==6 --> TEMA
    # MAtype==7 --> WMA
    # MAtype==8 --> VWMA
    # MAtype==9 --> zema
    if src == 1:
        masrc = df["close"]
    elif src == 2:
        masrc = (df["high"] + df["low"]) / 2
    elif src == 3:
        masrc = (df["high"] + df["low"] + df["close"] + df["open"]) / 4
    if MAtype == 1:
        df[mavalue] = ta.EMA(masrc, timeperiod=length)
    elif MAtype == 2:
        df[mavalue] = ta.DEMA(masrc, timeperiod=length)
    elif MAtype == 3:
        df[mavalue] = ta.T3(masrc, timeperiod=length)
    elif MAtype == 4:
        df[mavalue] = ta.SMA(masrc, timeperiod=length)
    elif MAtype == 5:
        df[mavalue] = ta.VIDYA(df, length=length)
    elif MAtype == 6:
        df[mavalue] = ta.TEMA(masrc, timeperiod=length)
    elif MAtype == 7:
        df[mavalue] = ta.WMA(df, timeperiod=length)
    elif MAtype == 8:
        df[mavalue] = ta.vwma(df, length)
    elif MAtype == 9:
        df[mavalue] = ta.zema(df, period=length)

    # Compute basic upper and lower bands
    df["basic_ub"] = df[mavalue] + (multiplier * df[atr])
    df["basic_lb"] = df[mavalue] - (multiplier * df[atr])
    # Compute final upper and lower bands
    df["final_ub"] = 0.00
    df["final_lb"] = 0.00

    df[pm] = 0.00

    # calling the cython function
    pm_array = PMAXCython.pmax(df["basic_ub"].to_numpy(), df["basic_lb"].to_numpy(),
                                             df["final_ub"].to_numpy(), df["final_lb"].to_numpy(),
                                             df[mavalue].to_numpy(), period=period, rtol=1e-1, atol=1e-5)

    df[pm] = pm_array

    # Mark the trend direction up/down
    df[pmx] = np.where((df[pm] > 0.00), np.where((df[mavalue] < df[pm]), "down", "up"), np.NaN)

    # T1.append(time.time() - T0)
    # Remove basic and final bands from the columns
    df.drop(["basic_ub", "basic_lb", "final_ub", "final_lb"], inplace=True, axis=1)

    df.fillna(0, inplace=True)

    return df

@xmatthias
Copy link
Member

xmatthias commented Aug 2, 2021

here some value to represent the porformance advantages of cython in case of the PMAX indicator:

Array size: 5000, Python: 1.048s, Cython: 8.976ms, Speed up factor: 116.7
Array size: 10000, Python: 2.132s, Cython: 12.939ms, Speed up factor: 164.7
Array size: 15000, Python: 3.237s, Cython: 16.991ms, Speed up factor: 190.5
Array size: 20000, Python: 4.374s, Cython: 21.910ms, Speed up factor: 199.6
Array size: 25000, Python: 5.531s, Cython: 25.956ms, Speed up factor: 213.1
Array size: 30000, Python: 6.866s, Cython: 32.886ms, Speed up factor: 208.8
Array size: 35000, Python: 8.114s, Cython: 35.908ms, Speed up factor: 226.0
Array size: 40000, Python: 9.474s, Cython: 40.879ms, Speed up factor: 231.7
Array size: 45000, Python: 10.650s, Cython: 42.916ms, Speed up factor: 248.2

Cython is quite easy to implement. the compiled library under windows 10 gets tha name .cp39-win_amd64.pyd
I guess the other indicators could benefit from it too.

I've never doubted the speed improvement 😆 although it's quite nice to see it in numbers.

What's missing in my opinion however is the cython code itself - while you point out the resulting file - without the code, just the filename, there's little i can do with it - assuming PMAXCython.pmax will not be "magically" available in all python installations around the world ...

Best create a Pull request with this (including the cython code however) - that's the easiest way to test and comment on lines where there's some doubts...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants