Files
Freqtrade/custom_indicators.py
Jérôme Delacotte 7c239227d8 first commit
2025-03-06 11:01:43 +01:00

223 lines
6.8 KiB
Python

"""
Solipsis Custom Indicators and Maths
"""
import numpy as np
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from pandas import DataFrame, Series
"""
Misc. Helper Functions
"""
def same_length(bigger, shorter):
return np.concatenate((np.full((bigger.shape[0] - shorter.shape[0]), np.nan), shorter))
"""
Maths
"""
def linear_growth(start: float, end: float, start_time: int, end_time: int, trade_time: int) -> float:
"""
Simple linear growth function. Grows from start to end after end_time minutes (starts after start_time minutes)
"""
time = max(0, trade_time - start_time)
rate = (end - start) / (end_time - start_time)
return min(end, start + (rate * time))
def linear_decay(start: float, end: float, start_time: int, end_time: int, trade_time: int) -> float:
"""
Simple linear decay function. Decays from start to end after end_time minutes (starts after start_time minutes)
"""
time = max(0, trade_time - start_time)
rate = (start - end) / (end_time - start_time)
return max(end, start - (rate * time))
"""
TA Indicators
"""
def zema(dataframe, period, field='close'):
"""
Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/overlap_studies.py#L79
Modified slightly to use ta.EMA instead of technical ema
"""
df = dataframe.copy()
df['ema1'] = ta.EMA(df[field], timeperiod=period)
df['ema2'] = ta.EMA(df['ema1'], timeperiod=period)
df['d'] = df['ema1'] - df['ema2']
df['zema'] = df['ema1'] + df['d']
return df['zema']
def RMI(dataframe, *, length=20, mom=5):
"""
Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/indicators.py#L912
"""
df = dataframe.copy()
df['maxup'] = (df['close'] - df['close'].shift(mom)).clip(lower=0)
df['maxdown'] = (df['close'].shift(mom) - df['close']).clip(lower=0)
df.fillna(0, inplace=True)
df["emaInc"] = ta.EMA(df, price='maxup', timeperiod=length)
df["emaDec"] = ta.EMA(df, price='maxdown', timeperiod=length)
df['RMI'] = np.where(df['emaDec'] == 0, 0, 100 - 100 / (1 + df["emaInc"] / df["emaDec"]))
return df["RMI"]
def mastreak(dataframe: DataFrame, period: int = 4, field='close') -> Series:
"""
MA Streak
Port of: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/
"""
df = dataframe.copy()
avgval = zema(df, period, field)
arr = np.diff(avgval)
pos = np.clip(arr, 0, 1).astype(bool).cumsum()
neg = np.clip(arr, -1, 0).astype(bool).cumsum()
streak = np.where(arr >= 0, pos - np.maximum.accumulate(np.where(arr <= 0, pos, 0)),
-neg + np.maximum.accumulate(np.where(arr >= 0, neg, 0)))
res = same_length(df['close'], streak)
return res
def pcc(dataframe: DataFrame, period: int = 20, mult: int = 2):
"""
Percent Change Channel
PCC is like KC unless it uses percentage changes in price to set channel distance.
https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/
"""
df = dataframe.copy()
df['previous_close'] = df['close'].shift()
df['close_change'] = (df['close'] - df['previous_close']) / df['previous_close'] * 100
df['high_change'] = (df['high'] - df['close']) / df['close'] * 100
df['low_change'] = (df['low'] - df['close']) / df['close'] * 100
df['delta'] = df['high_change'] - df['low_change']
mid = zema(df, period, 'close_change')
rangema = zema(df, period, 'delta')
upper = mid + rangema * mult
lower = mid - rangema * mult
return upper, rangema, lower
def SSLChannels(dataframe, length=10, mode='sma'):
"""
Source: https://www.tradingview.com/script/xzIoaIJC-SSL-channel/
Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/indicators.py#L1025
Usage:
dataframe['sslDown'], dataframe['sslUp'] = SSLChannels(dataframe, 10)
"""
if mode not in ('sma'):
raise ValueError(f"Mode {mode} not supported yet")
df = dataframe.copy()
if mode == 'sma':
df['smaHigh'] = df['high'].rolling(length).mean()
df['smaLow'] = df['low'].rolling(length).mean()
df['hlv'] = np.where(df['close'] > df['smaHigh'], 1,
np.where(df['close'] < df['smaLow'], -1, np.NAN))
df['hlv'] = df['hlv'].ffill()
df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow'])
df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh'])
return df['sslDown'], df['sslUp']
def SSLChannels_ATR(dataframe, length=7):
"""
SSL Channels with ATR: https://www.tradingview.com/script/SKHqWzql-SSL-ATR-channel/
Credit to @JimmyNixx for python
"""
df = dataframe.copy()
df['ATR'] = ta.ATR(df, timeperiod=14)
df['smaHigh'] = df['high'].rolling(length).mean() + df['ATR']
df['smaLow'] = df['low'].rolling(length).mean() - df['ATR']
df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, np.where(df['close'] < df['smaLow'], -1, np.NAN))
df['hlv'] = df['hlv'].ffill()
df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow'])
df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh'])
return df['sslDown'], df['sslUp']
def WaveTrend(dataframe, chlen=10, avg=21, smalen=4):
"""
WaveTrend Ocillator by LazyBear
https://www.tradingview.com/script/2KE8wTuF-Indicator-WaveTrend-Oscillator-WT/
"""
df = dataframe.copy()
df['hlc3'] = (df['high'] + df['low'] + df['close']) / 3
df['esa'] = ta.EMA(df['hlc3'], timeperiod=chlen)
df['d'] = ta.EMA((df['hlc3'] - df['esa']).abs(), timeperiod=chlen)
df['ci'] = (df['hlc3'] - df['esa']) / (0.015 * df['d'])
df['tci'] = ta.EMA(df['ci'], timeperiod=avg)
df['wt1'] = df['tci']
df['wt2'] = ta.SMA(df['wt1'], timeperiod=smalen)
df['wt1-wt2'] = df['wt1'] - df['wt2']
return df['wt1'], df['wt2']
def T3(dataframe, length=5):
"""
T3 Average by HPotter on Tradingview
https://www.tradingview.com/script/qzoC9H1I-T3-Average/
"""
df = dataframe.copy()
df['xe1'] = ta.EMA(df['close'], timeperiod=length)
df['xe2'] = ta.EMA(df['xe1'], timeperiod=length)
df['xe3'] = ta.EMA(df['xe2'], timeperiod=length)
df['xe4'] = ta.EMA(df['xe3'], timeperiod=length)
df['xe5'] = ta.EMA(df['xe4'], timeperiod=length)
df['xe6'] = ta.EMA(df['xe5'], timeperiod=length)
b = 0.7
c1 = -b * b * b
c2 = 3 * b * b + 3 * b * b * b
c3 = -6 * b * b - 3 * b - 3 * b * b * b
c4 = 1 + 3 * b + b * b * b + 3 * b * b
df['T3Average'] = c1 * df['xe6'] + c2 * df['xe5'] + c3 * df['xe4'] + c4 * df['xe3']
return df['T3Average']
def SROC(dataframe, roclen=21, emalen=13, smooth=21):
df = dataframe.copy()
roc = ta.ROC(df, timeperiod=roclen)
ema = ta.EMA(df, timeperiod=emalen)
sroc = ta.ROC(ema, timeperiod=smooth)
return sroc