Timeframe
30m
Direction
Long Only
Stoploss
-99.0%
Trailing Stop
No
ROI
0m: 1.0%
Interface Version
3
Startup Candles
500
Indicators
21
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np
import talib.abstract as ta
from freqtrade.strategy import IStrategy, informative
from freqtrade.strategy import (merge_informative_pair,
DecimalParameter, IntParameter, BooleanParameter, CategoricalParameter, stoploss_from_open)
from pandas import DataFrame, Series
from typing import Dict, List, Optional, Tuple, Union
from functools import reduce
from freqtrade.persistence import Trade
from datetime import datetime, timedelta, timezone
from freqtrade.exchange import timeframe_to_prev_date, timeframe_to_minutes
import talib.abstract as ta
import math
import pandas_ta as pta
import logging
from logging import FATAL
import pandas as pd
def smi_momentum(dataframe: DataFrame, k_length=9, d_length=3):
"""
The Stochastic Momentum Index (SMI) Indicator was developed by
William Blau in 1993 and is considered to be a momentum indicator
that can help identify trend reversal points
:return: DataFrame with smi column populated
"""
df = dataframe.copy()
ll = df['low'].rolling(window=k_length).min()
hh = df['high'].rolling(window=k_length).max()
diff = hh - ll
rdiff = df['close'] - (hh + ll) / 2
avgrel = rdiff.ewm(span=d_length).mean().ewm(span=d_length).mean()
avgdiff = diff.ewm(span=d_length).mean().ewm(span=d_length).mean()
df['smi'] = np.where(avgdiff != 0, (avgrel / (avgdiff / 2) * 100), 0)
return df['smi']
def EWO(dataframe, ema_length=5, ema2_length=35):
df = dataframe.copy()
ema1 = ta.EMA(df, timeperiod=ema_length)
ema2 = ta.EMA(df, timeperiod=ema2_length)
emadif = (ema1 - ema2) / df['low'] * 100
return emadif
def tv_wma(df, length = 9) -> DataFrame:
"""
Source: Tradingview "Moving Average Weighted"
Pinescript Author: Unknown
Args :
dataframe : Pandas Dataframe
length : WMA length
field : Field to use for the calculation
Returns :
dataframe : Pandas DataFrame with new columns 'tv_wma'
"""
norm = 0
sum = 0
for i in range(1, length - 1):
weight = (length - i) * length
norm = norm + weight
sum = sum + df.shift(i) * weight
tv_wma = (sum / norm) if norm > 0 else 0
return tv_wma
def tv_hma(dataframe, length = 9, field = 'close') -> DataFrame:
"""
Source: Tradingview "Hull Moving Average"
Pinescript Author: Unknown
Args :
dataframe : Pandas Dataframe
length : HMA length
field : Field to use for the calculation
Returns :
dataframe : Pandas DataFrame with new columns 'tv_hma'
"""
h = 2 * tv_wma(dataframe[field], math.floor(length / 2)) - tv_wma(dataframe[field], length)
tv_hma = tv_wma(h, math.floor(math.sqrt(length)))
# dataframe.drop("h", inplace=True, axis=1)
return tv_hma
# Range midpoint acts as Support
def is_support(row_data) -> bool:
conditions = []
for row in range(len(row_data)-1):
if row < len(row_data)//2:
conditions.append(row_data[row] > row_data[row+1])
else:
conditions.append(row_data[row] < row_data[row+1])
result = reduce(lambda x, y: x & y, conditions)
return result
# Range midpoint acts as Resistance
def is_resistance(row_data) -> bool:
conditions = []
for row in range(len(row_data)-1):
if row < len(row_data)//2:
conditions.append(row_data[row] < row_data[row+1])
else:
conditions.append(row_data[row] > row_data[row+1])
result = reduce(lambda x, y: x & y, conditions)
return result
# Chaikin Money Flow
def chaikin_money_flow(dataframe, n=20, fillna=False) -> Series:
"""Chaikin Money Flow (CMF)
It measures the amount of Money Flow Volume over a specific period.
http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:chaikin_money_flow_cmf
Args:
dataframe(pandas.Dataframe): dataframe containing ohlcv
n(int): n period.
fillna(bool): if True, fill nan values.
Returns:
pandas.Series: New feature generated.
"""
mfv = ((dataframe['close'] - dataframe['low']) - (dataframe['high'] - dataframe['close'])) / (dataframe['high'] - dataframe['low'])
mfv = mfv.fillna(0.0) # float division by zero
mfv *= dataframe['volume']
cmf = (mfv.rolling(n, min_periods=0).sum()
/ dataframe['volume'].rolling(n, min_periods=0).sum())
if fillna:
cmf = cmf.replace([np.inf, -np.inf], np.nan).fillna(0)
return Series(cmf, name='cmf')
# Williams %R
def williams_r(dataframe: DataFrame, period: int = 14) -> Series:
"""Williams %R, or just %R, is a technical analysis oscillator showing the current closing price in relation to the high and low
of the past N days (for a given N). It was developed by a publisher and promoter of trading materials, Larry Williams.
Its purpose is to tell whether a stock or commodity market is trading near the high or the low, or somewhere in between,
of its recent trading range.
The oscillator is on a negative scale, from −100 (lowest) up to 0 (highest).
"""
highest_high = dataframe["high"].rolling(center=False, window=period).max()
lowest_low = dataframe["low"].rolling(center=False, window=period).min()
WR = Series(
(highest_high - dataframe["close"]) / (highest_high - lowest_low),
name=f"{period} Williams %R",
)
return WR * -100
def williams_fractals(dataframe: pd.DataFrame, period: int = 2) -> tuple:
"""Williams Fractals implementation
:param dataframe: OHLC data
:param period: number of lower (or higher) points on each side of a high (or low)
:return: tuple of boolean Series (bearish, bullish) where True marks a fractal pattern
"""
window = 2 * period + 1
bears = dataframe['high'].rolling(window, center=True).apply(lambda x: x[period] == max(x), raw=True)
bulls = dataframe['low'].rolling(window, center=True).apply(lambda x: x[period] == min(x), raw=True)
return bears, bulls
# Volume Weighted Moving Average
def vwma(dataframe: DataFrame, length: int = 10):
"""Indicator: Volume Weighted Moving Average (VWMA)"""
# Calculate Result
pv = dataframe['close'] * dataframe['volume']
vwma = Series(ta.SMA(pv, timeperiod=length) / ta.SMA(dataframe['volume'], timeperiod=length))
vwma = vwma.fillna(0, inplace=True)
return vwma
# Exponential moving average of a volume weighted simple moving average
def ema_vwma_osc(dataframe, len_slow_ma):
slow_ema = Series(ta.EMA(vwma(dataframe, len_slow_ma), len_slow_ma))
return ((slow_ema - slow_ema.shift(1)) / slow_ema.shift(1)) * 100
def t3_average(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['xe1'].fillna(0, inplace=True)
df['xe2'] = ta.EMA(df['xe1'], timeperiod=length)
df['xe2'].fillna(0, inplace=True)
df['xe3'] = ta.EMA(df['xe2'], timeperiod=length)
df['xe3'].fillna(0, inplace=True)
df['xe4'] = ta.EMA(df['xe3'], timeperiod=length)
df['xe4'].fillna(0, inplace=True)
df['xe5'] = ta.EMA(df['xe4'], timeperiod=length)
df['xe5'].fillna(0, inplace=True)
df['xe6'] = ta.EMA(df['xe5'], timeperiod=length)
df['xe6'].fillna(0, inplace=True)
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']
# Pivot Points - 3 variants - daily recommended
def pivot_points(dataframe: DataFrame, mode = 'fibonacci') -> Series:
if mode == 'simple':
hlc3_pivot = (dataframe['high'] + dataframe['low'] + dataframe['close']).shift(1) / 3
res1 = hlc3_pivot * 2 - dataframe['low'].shift(1)
sup1 = hlc3_pivot * 2 - dataframe['high'].shift(1)
res2 = hlc3_pivot + (dataframe['high'] - dataframe['low']).shift()
sup2 = hlc3_pivot - (dataframe['high'] - dataframe['low']).shift()
res3 = hlc3_pivot * 2 + (dataframe['high'] - 2 * dataframe['low']).shift()
sup3 = hlc3_pivot * 2 - (2 * dataframe['high'] - dataframe['low']).shift()
return hlc3_pivot, res1, res2, res3, sup1, sup2, sup3
elif mode == 'fibonacci':
hlc3_pivot = (dataframe['high'] + dataframe['low'] + dataframe['close']).shift(1) / 3
hl_range = (dataframe['high'] - dataframe['low']).shift(1)
res1 = hlc3_pivot + 0.382 * hl_range
sup1 = hlc3_pivot - 0.382 * hl_range
res2 = hlc3_pivot + 0.618 * hl_range
sup2 = hlc3_pivot - 0.618 * hl_range
res3 = hlc3_pivot + 1 * hl_range
sup3 = hlc3_pivot - 1 * hl_range
return hlc3_pivot, res1, res2, res3, sup1, sup2, sup3
elif mode == 'DeMark':
demark_pivot_lt = (dataframe['low'] * 2 + dataframe['high'] + dataframe['close'])
demark_pivot_eq = (dataframe['close'] * 2 + dataframe['low'] + dataframe['high'])
demark_pivot_gt = (dataframe['high'] * 2 + dataframe['low'] + dataframe['close'])
demark_pivot = np.where((dataframe['close'] < dataframe['open']), demark_pivot_lt, np.where((dataframe['close'] > dataframe['open']), demark_pivot_gt, demark_pivot_eq))
dm_pivot = demark_pivot / 4
dm_res = demark_pivot / 2 - dataframe['low']
dm_sup = demark_pivot / 2 - dataframe['high']
return dm_pivot, dm_res, dm_sup
# Heikin Ashi candles
def heikin_ashi(dataframe, smooth_inputs = False, smooth_outputs = False, length = 10):
df = dataframe[['open','close','high','low']].copy().fillna(0)
if smooth_inputs:
df['open_s'] = ta.EMA(df['open'], timeframe = length)
df['high_s'] = ta.EMA(df['high'], timeframe = length)
df['low_s'] = ta.EMA(df['low'], timeframe = length)
df['close_s'] = ta.EMA(df['close'],timeframe = length)
open_ha = (df['open_s'].shift(1) + df['close_s'].shift(1)) / 2
high_ha = df.loc[:, ['high_s', 'open_s', 'close_s']].max(axis=1)
low_ha = df.loc[:, ['low_s', 'open_s', 'close_s']].min(axis=1)
close_ha = (df['open_s'] + df['high_s'] + df['low_s'] + df['close_s'])/4
else:
open_ha = (df['open'].shift(1) + df['close'].shift(1)) / 2
high_ha = df.loc[:, ['high', 'open', 'close']].max(axis=1)
low_ha = df.loc[:, ['low', 'open', 'close']].min(axis=1)
close_ha = (df['open'] + df['high'] + df['low'] + df['close'])/4
open_ha = open_ha.fillna(0)
high_ha = high_ha.fillna(0)
low_ha = low_ha.fillna(0)
close_ha = close_ha.fillna(0)
if smooth_outputs:
open_sha = ta.EMA(open_ha, timeframe = length)
high_sha = ta.EMA(high_ha, timeframe = length)
low_sha = ta.EMA(low_ha, timeframe = length)
close_sha = ta.EMA(close_ha, timeframe = length)
return open_sha, close_sha, low_sha
else:
return open_ha, close_ha, low_ha
# Peak Percentage Change
def range_percent_change(self, dataframe: DataFrame, method, length: int) -> float:
"""
Rolling Percentage Change Maximum across interval.
:param dataframe: DataFrame The original OHLC dataframe
:param method: High to Low / Open to Close
:param length: int The length to look back
"""
if method == 'HL':
return (dataframe['high'].rolling(length).max() - dataframe['low'].rolling(length).min()) / dataframe['low'].rolling(length).min()
elif method == 'OC':
return (dataframe['open'].rolling(length).max() - dataframe['close'].rolling(length).min()) / dataframe['close'].rolling(length).min()
else:
raise ValueError(f"Method {method} not defined!")
# Percentage distance to top peak
def top_percent_change(self, dataframe: DataFrame, length: int) -> float:
"""
Percentage change of the current close from the range maximum Open price
:param dataframe: DataFrame The original OHLC dataframe
:param length: int The length to look back
"""
if length == 0:
return (dataframe['open'] - dataframe['close']) / dataframe['close']
else:
return (dataframe['open'].rolling(length).max() - dataframe['close']) / dataframe['close']
def smma(s: Series, length):
smma = s.copy()
smma[:length - 1] = np.nan
smma.iloc[length - 1] = ta.SMA(s, length)[length]
for i in range(length, len(s)):
smma.iloc[i] = ((length - 1) * smma.iloc[i - 1] + smma.iloc[i]) / length
return smma
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']
# exclusive vwap indicator for zond by @rk
def vwap_fast(dataframe: DataFrame):
split_indices = list(dataframe.loc[
(
(dataframe['date'].dt.second == 0)
&
(dataframe['date'].dt.minute == 0)
&
(dataframe['date'].dt.hour == 0)
)].index)
split_indices.insert(0, 0)
split_indices.append(len(dataframe))
vwap_slices = []
for i in range(1, len(split_indices)):
start_idx = split_indices[i - 1]
end_idx = split_indices[i]
slice = dataframe[start_idx:end_idx]
hlc3 = (slice['high'] + slice['low'] + slice['close']) / 3
wp = hlc3 * slice['volume']
vwap = wp.cumsum() / slice['volume'].cumsum()
vwap_slices.append(vwap)
vwap = pd.concat(vwap_slices)
return vwap
def chaikin_mf(dataframe, periods=20):
close = dataframe['close']
low = dataframe['low']
high = dataframe['high']
volume = dataframe['volume']
mfv = ((close - low) - (high - close)) / (high - low)
mfv = mfv.fillna(0.0)# float division by zero
mfv *= volume
cmf = mfv.rolling(periods).sum() / volume.rolling(periods).sum()
return Series(cmf, name='cmf')
logger = logging.getLogger(__name__)
class test_recursive (IStrategy):
def version(self) -> str:
return "test_recursive"
INTERFACE_VERSION = 3
# ROI table:
minimal_roi = {
"0": 0.01
}
# Stoploss:
stoploss = -0.99
# Trailing stop:
trailing_stop = False
trailing_stop_positive = 0.01
trailing_stop_positive_offset = 0.03
trailing_only_offset_is_reached = True
# Sell signal
use_exit_signal = True
exit_profit_only = False
exit_profit_offset = 0.01
ignore_roi_if_entry_signal = False
timeframe = '30m'
process_only_new_candles = True
startup_candle_count = 500
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['ema_25'] = ta.EMA(dataframe, 25)
dataframe['ema_50'] = ta.EMA(dataframe, 50)
dataframe['ema_100'] = ta.EMA(dataframe, 100)
dataframe['ema_200'] = ta.EMA(dataframe, 200)
dataframe['tema_25'] = ta.TEMA(dataframe, 25)
dataframe['dema_25'] = ta.DEMA(dataframe, 25)
dataframe['tema_50'] = ta.TEMA(dataframe, 50)
dataframe['dema_50'] = ta.DEMA(dataframe, 50)
dataframe['tema_100'] = ta.TEMA(dataframe, 100)
dataframe['dema_100'] = ta.DEMA(dataframe, 100)
dataframe['tema_200'] = ta.TEMA(dataframe, 200)
dataframe['dema_200'] = ta.DEMA(dataframe, 200)
dataframe['hma_100'] = tv_hma(dataframe, 100)
dataframe['ewo_50_200'] = EWO(dataframe, 50, 200)
dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50)
dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9)
dataframe['sma_50'] = ta.SMA(dataframe, timeperiod=50)
dataframe['sma_100'] = ta.SMA(dataframe, timeperiod=100)
dataframe['sma_200'] = ta.SMA(dataframe, timeperiod=200)
dataframe['close_9_mean'] = dataframe['close'].rolling(9).mean()
dataframe['rsi_14'] = ta.RSI(dataframe, timeperiod=14)
dataframe['rsi_4'] = ta.RSI(dataframe, timeperiod=4)
dataframe['rsi_20'] = ta.RSI(dataframe, timeperiod=20)
dataframe['rsi_45'] = ta.RSI(dataframe, timeperiod=45)
dataframe['mfi_14'] = ta.MFI(dataframe, 14)
dataframe['mfi_45'] = ta.MFI(dataframe, 45)
dataframe['live_data_ok'] = (dataframe['volume'].rolling(window=72, min_periods=72).min() > 0)
# BB 20 - STD2
bb_20_std2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb20_2_low'] = bb_20_std2['lower']
dataframe['bb20_2_mid'] = bb_20_std2['mid']
dataframe['bb20_2_upp'] = bb_20_std2['upper']
# BB 40 - STD2
bb_40_std2 = qtpylib.bollinger_bands(dataframe['close'], window=40, stds=2)
dataframe['bb40_2_low'] = bb_40_std2['lower']
dataframe['bb40_2_mid'] = bb_40_std2['mid']
dataframe['bb40_2_delta'] = (bb_40_std2['mid'] - dataframe['bb40_2_low']).abs()
dataframe['bb40_2_tail'] = (dataframe['close'] - dataframe['bb40_2_low']).abs()
# Williams %R
dataframe['r_14'] = williams_r(dataframe, period=14)
dataframe['r_480'] = williams_r(dataframe, period=480)
# CTI
dataframe['cti_20'] = pta.cti(dataframe["close"], length=20)
# SAR
dataframe['sar'] = ta.SAR(dataframe)
# CCI
dataframe['cci_20'] = ta.CCI(dataframe, source='hlc3', timeperiod=20)
# TSI
tsi = pta.tsi(dataframe["close"])
dataframe['tsi'] = tsi.iloc[:, 0]
dataframe['tsi_signal'] = tsi.iloc[:, 1]
# Heiken Ashi
heikinashi = qtpylib.heikinashi(dataframe)
dataframe['ha_open'] = heikinashi['open']
dataframe['ha_close'] = heikinashi['close']
dataframe['ha_high'] = heikinashi['high']
dataframe['ha_low'] = heikinashi['low']
# Dip protection
dataframe['tpct_change_0'] = top_percent_change(self, dataframe, 0)
dataframe['tpct_change_2'] = top_percent_change(self, dataframe, 2)
# Close max
dataframe['close_max_12'] = dataframe['close'].rolling(12).max()
dataframe['close_max_24'] = dataframe['close'].rolling(24).max()
dataframe['close_max_48'] = dataframe['close'].rolling(48).max()
# Close min
dataframe['close_min_12'] = dataframe['close'].rolling(12).min()
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
return dataframe