Timeframe
5m
Direction
Long Only
Stoploss
-10.0%
Trailing Stop
No
ROI
0m: 7.8%, 40m: 6.2%, 99m: 3.9%, 218m: 0.0%
Interface Version
N/A
Startup Candles
N/A
Indicators
10
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
from datetime import datetime, timedelta
from typing import Optional, Union
import random
import talib.abstract as ta
import pandas as pd
from pandas import DataFrame
import numpy as np
from freqtrade.persistence import Trade
from freqtrade.strategy import IStrategy, DecimalParameter, IntParameter, CategoricalParameter, merge_informative_pair
from freqtrade.exchange import timeframe_to_prev_date
from technical.util import resample_to_interval, resampled_merge
from technical.indicators import zema, vfi
from sklearn import preprocessing
import freqtrade.vendor.qtpylib.indicators as qtpylib
# Hyperopt instructions
# 0) set ROI to "0": 1, set stoploss to -0.3, and comment out trailing stoploss
# 1) hyperopt on buy and sell spaces only and update params
# 2) hyperopt on ROI only and update table
# 3) hyperopt on stoploss and update
# 4) hyperopt on trailing only if required
#
# author @jimmynixx and his special sauce - inspired by @rextea, rk2 version by @rk, roi and custom_sell from @your_secret_admirer, zema7 exit check from @zx, munged into being by @froggleston with Solipsis inspiration and memey goodness from @werkkrew
class zorkv7_0_0(IStrategy):
# ROI table:
minimal_roi = {
"0":1
# "0": 0.078,
# "40": 0.062,
# "99": 0.039,
# "218": 0
}
stoploss = -0.1
# Buy hyperspace params:
buy_params = {
'low_offset': 0.964,
'wma_low_offset': 0.979,
'ma_len_buy': 51,
'buy_ma_type': 'zema',
}
# Sell hyperspace params:
# sell_params = {
# "max_roi_time": 95,
# "max_stoploss_time": 204,
# "roi_factor_bear": 1.145,
# "roi_factor_bull": 6.21,
# "sl_factor_bear": 1.054,
# "sl_factor_bull": 9.22,
# "high_offset": 1.004, # value loaded from strategy
# "ma_len_sell": 72, # value loaded from strategy
# "msq_normabs_sell": 1.95, # value loaded from strategy
# "sell_ma_type": "zema", # value loaded from strategy
# "trail_amount": 0.006, # value loaded from strategy
# "wma_high_offset": 1.04, # value loaded from strategy
# }
# Sell hyperspace params:
sell_params = {
"max_roi_time": 36,
"max_stoploss_time": 65,
"roi_factor_bear": 4.045,
"roi_factor_bull": 4.75,
"sl_factor_bear": 2.932,
"sl_factor_bull": 8.648,
"high_offset": 1.004, # value loaded from strategy
"ma_len_sell": 72, # value loaded from strategy
"msq_normabs_sell": 1.95, # value loaded from strategy
"sell_ma_type": "zema", # value loaded from strategy
"trail_amount": 0.006, # value loaded from strategy
"wma_high_offset": 1.04, # value loaded from strategy
}
# Trailing stop:
trailing_stop = False
trailing_stop_positive = 0.02 # 0.02
trailing_stop_positive_offset = 0.045 # 0.045
trailing_only_offset_is_reached = False
# Custom stoploss
use_custom_stoploss = False
use_sell_signal = True
sell_profit_only = True
ignore_roi_if_buy_signal = True
# Run "populate_indicators()" only for new candle.
process_only_new_candles = False
# Number of candles the strategy requires before producing valid signals
startup_candle_count: int = 300
timeframe = '5m'
informative_timeframe = '5m'
pair_informative_timeframe = '1h'
informative_coin = "ETH"
high_offset = DecimalParameter(1, 1.20, default=1.004, space='sell', optimize=False)
wma_high_offset = DecimalParameter(1, 1.20, default=1.01, space='sell', optimize=False)
low_offset = DecimalParameter(0.80, 0.99, default=0.974, space='buy', optimize=False)
wma_low_offset = DecimalParameter(0.80, 0.99, default=0.944, space='buy', optimize=False)
ma_len_buy = IntParameter(30, 90, default=72, space='buy', optimize=False)
ma_len_sell = IntParameter(30, 90, default=51, space='sell', optimize=False)
buy_ma_type = CategoricalParameter(['zema', 'wma'], space='buy', default='zema', optimize=False)
sell_ma_type = CategoricalParameter(['zema', 'wma'], space='sell', default='zema', optimize=False)
msq_normabs_sell = DecimalParameter(1.0, 4.0, default=2, space='sell', optimize=False)
roi_factor_bear = DecimalParameter(0.5, 5, default=2, space='sell', optimize=True, load=True)
roi_factor_bull = DecimalParameter(0.5, 5, default=2, space='sell', optimize=True, load=True)
sl_factor_bear = DecimalParameter(1, 10, default=3, space='sell', optimize=True, load=True)
sl_factor_bull = DecimalParameter(1, 10, default=3, space='sell', optimize=True, load=True)
max_stoploss_time = IntParameter(30, 240, default=100, space='sell', optimize=True, load=True)
max_roi_time = IntParameter(30, 240, default=100, space='sell', optimize=True, load=True)
trail_amount = DecimalParameter(0.005, 0.03, default=0.01, space='sell', load=True, optimize=False)
market_df = None
plot_config = {
'main_plot': {
'ma_buy': {'color': 'green'},
'ma_sell': {'color': 'red'},
},
'subplots': {
}
}
def get_ticker_indicator(self):
return int(self.timeframe[:-1])
def informative_pairs(self):
pairs = self.dp.current_whitelist()
informative_pairs = [(pair, self.pair_informative_timeframe) for pair in pairs]
return informative_pairs
def inf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.pair_informative_timeframe)
informative['ema_50'] = ta.EMA(informative, timeperiod=50)
informative['ema_200'] = ta.EMA(informative, timeperiod=200)
return informative
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
pinformative = self.inf_indicators(dataframe, metadata)
dataframe = merge_informative_pair(dataframe, pinformative, self.timeframe, self.pair_informative_timeframe, ffill=True)
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
dataframe['zema7'] = zema(dataframe, period=7)
dataframe['zema100'] = ta.EMA(dataframe, timeperiod=100)
dataframe['wma100'] = ta.WMA(dataframe, timeperiod=300)
informative = self.dp.get_pair_dataframe(f'{self.informative_coin}/USDT', self.informative_timeframe)
informative['sma50'] = ta.SMA(informative, timeperiod=50)
informative['sma100'] = ta.SMA(informative, timeperiod=100)
informative['sma200'] = ta.SMA(informative, timeperiod=200)
informative['rsi'] = ta.RSI(informative, timeperiod=14)
# Bollinger Bands because obviously
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=1)
informative['bb_lowerband'] = bollinger['lower']
informative['bb_middleband'] = bollinger['mid']
informative['bb_upperband'] = bollinger['upper']
# informative SAR if roi_decay<0.005: roi_estimate = 0.005
informative[f'{self.informative_coin}_sar'] = ta.SAR(informative)
informative[f'{self.informative_coin}_dmi_plus'] = ta.PLUS_DI(informative, timeperiod=14)
informative[f'{self.informative_coin}_dmi_minus'] = ta.MINUS_DI(informative, timeperiod=14)
informative[f'{self.informative_coin}_adx'] = ta.ADX(informative, timeperiod=9)
informative[f'{self.informative_coin}_roc'] = ta.ROC(informative, timeperiod=6)
dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.informative_timeframe, ffill=True)
for length in self.ma_len_buy.range:
dataframe[f'zema_{length}'] = zema(dataframe, period=length)
dataframe[f'wma_{length}'] = ta.WMA(dataframe, timeperiod=length)
for length in self.ma_len_sell.range:
if f'zema_{length}' not in dataframe:
dataframe[f'zema_{length}'] = zema(dataframe, period=length)
dataframe[f'wma_{length}'] = ta.WMA(dataframe, timeperiod=length)
dataframe['zema_buy'] = dataframe[f'zema_{self.ma_len_buy.value}'] * self.low_offset.value
dataframe['zema_sell'] = dataframe[f'zema_{self.ma_len_sell.value}'] * self.high_offset.value
dataframe['wma_buy'] = dataframe[f'wma_{self.ma_len_buy.value}'] * self.wma_low_offset.value
dataframe['wma_sell'] = dataframe[f'wma_{self.ma_len_sell.value}'] * self.wma_high_offset.value
msq_normabs = self.MadSqueeze(dataframe, period=14, ref=13, sqzlen=5)
dataframe['msq_normabs'] = msq_normabs
dataframe['sar'] = ta.SAR(dataframe)
dataframe['mfi'] = ta.MFI(dataframe)
#dataframe_macro = resample_to_interval(dataframe, self.get_ticker_indicator())
dataframe['high-low'] = dataframe['high'] - dataframe['low']
dataframe['open-close'] = dataframe['open'] - dataframe['close']
dataframe['avg_candle_size'] = dataframe['high-low'].rolling(20).mean()
dataframe['pct_change'] = (dataframe['close'] - dataframe['close'].shift(1)) / dataframe['close']
#dataframe = resampled_merge(dataframe, dataframe_macro)
dataframe = self.custom_roi_stoploss_bmsb_ATR(dataframe,12)
dataframe = self.get_market_trend(dataframe)
return dataframe
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
ma_buy = f'{self.buy_ma_type.value}_{self.ma_len_buy.value}'
outside_range = 4
dataframe.loc[
(
## JIMMY'S SPECIAL SAUCE DO NOT TOUCH
(dataframe['ema_50_1h'] > dataframe['ema_200_1h'])
&
(dataframe[f'{self.informative_coin}_dmi_minus_5m'] > dataframe[f'{self.informative_coin}_dmi_plus_5m'])
&
(dataframe[f'{self.informative_coin}_dmi_minus_5m'] > 25)
&
(dataframe[f'{self.informative_coin}_dmi_plus_5m'] < 10)
&
(
(
((dataframe['open'] < (dataframe[ma_buy] * self.low_offset.value)).rolling(window=outside_range).sum() == outside_range)
&
((dataframe['close'] < (dataframe[ma_buy] * self.low_offset.value)).rolling(window=outside_range).sum() == outside_range)
&
(dataframe['close'] < dataframe[f'{self.buy_ma_type.value}100'])
)
|
(
(dataframe['close'] < (dataframe[ma_buy] * self.low_offset.value))
&
(dataframe['close'] > dataframe[f'{self.buy_ma_type.value}100'])
)
)
&
(dataframe['volume'] > 0)
),
'buy'] = 1
## comment out to be less strict
dataframe.loc[
(
## don't buy shitecoins - thanks @rextea!
(dataframe['up-ratio'] <= 0.15)
|
# low activity sideways periods - reduce to allow more buys
(dataframe['msq_normabs'] < 1.2)
|
(
# wacky shit happening
(dataframe['zema_sell'] > dataframe['wma_sell'])
|
(dataframe['zema_buy'] > dataframe['wma_buy'])
)
&
(
# informative guards (BTC/ETH)
(dataframe[f'{self.informative_coin}_roc_5m'] < -5)
&
(dataframe[f'{self.informative_coin}_roc_5m'] < dataframe[f'{self.informative_coin}_roc_5m'].shift())
)
),'buy'] = 0
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
ma_sell = f'{self.sell_ma_type.value}_{self.ma_len_sell.value}'
#stock zemaoff_rk sell
dataframe.loc[
(
(dataframe['close'] > (dataframe[ma_sell] * self.high_offset.value))
&
(dataframe['close'] < dataframe[f'{self.sell_ma_type.value}100'])
&
(dataframe['volume'] > 0)
),
'sell'] = 1
## comment out to be less strict
dataframe.loc[
(
(dataframe['close'] < dataframe['wma100'])
|
(
(dataframe['close'] < dataframe['zema_sell'])
&
(dataframe['close'] < dataframe['sar'])
)
),
'sell'] = 0
return dataframe
## drafty's custom_sell from golden_corralation
def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
current_profit: float, **kwargs):
dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
current_candle = dataframe.iloc[-1].squeeze()
current_profit = trade.calc_profit_ratio(current_candle['close'])
trade_date = timeframe_to_prev_date("5m",trade.open_date_utc)
trade_candle = dataframe.loc[(dataframe['date'] == trade_date)]
if trade_candle.empty: return None
trade_candle = trade_candle.squeeze()
open_price = (1 - current_profit) * current_rate
roi_price = trade_candle['roi_price']
roi_estimate = ( roi_price / open_price ) - 1
if roi_estimate > 0.08: roi_estimate=0.08
custom_roi = roi_estimate
stoploss_price = trade_candle['custom_stop_loss']
if stoploss_price<0: stoploss_price = -stoploss_price
stoploss_estimate = 1 - (stoploss_price / open_price)
if stoploss_estimate>-self.stoploss: stoploss_estimate = -self.stoploss
stoploss_decay = stoploss_estimate * ( 1 - ((current_time - trade.open_date_utc).seconds) / (self.max_stoploss_time.value*60)) # linear decay
if (current_profit < -stoploss_decay):
if trade_candle['resample_{}_bmsb'.format(self.get_ticker_indicator()*12)] == 0:
return 'custom_bear_stop_loss'
else: return 'custom_bull_stop_loss'
roi_decay = roi_estimate * ( 1 - ((current_time - trade.open_date_utc).seconds) / (self.max_roi_time.value*60)) # linear decay
#roi_decay = roi_estimate * np.exp(-(current_time - trade.open_date_utc).seconds) / (120*60) # exponential decay
if roi_decay < 0.005: roi_estimate = 0.005
else: roi_estimate = roi_decay
if current_profit > roi_estimate:
if trade_candle['resample_{}_bmsb'.format(self.get_ticker_indicator()*12)] == 0:
return 'roi_custom_bear'
else: return 'roi_custom_bull'
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
rate: float, time_in_force: str, sell_reason: str, **kwargs) -> bool:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
# if rate < trade.stop_loss: return True
if (last_candle is not None):
current_profit = trade.calc_profit_ratio(rate)
max_profit = max(0, trade.calc_profit_ratio(trade.max_rate))
pullback_value = max(0, (max_profit - self.trail_amount.value))
if (sell_reason in ('force_sell', 'sell_signal')) or (sell_reason.endswith('stop_loss')):
return True
# Ignore ROI if seems to go up:
if sell_reason.startswith('roi'):
if last_candle['buy']: return False
if (current_profit >= pullback_value):
if (last_candle['rsi'] > 40):
if (last_candle['close'] > last_candle['wma100']) & (last_candle['close'] > last_candle['zema7']):
return False
if (last_candle['mfi'] < 70):
return False
return True
return False
return True
## comment in for @rextea's random coin purse sampler
# def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
# time_in_force: str, current_time: datetime, **kwargs) -> bool:
# rand = random.randint(1, 10)
# if rand == 1:
# return False
#
# return True
##
def custom_roi_stoploss_bmsb_ATR(self, dataframe, window):
dataframe_macro = resample_to_interval(dataframe, self.get_ticker_indicator()*window) # 1 hr
dataframe_macro['ATR'] = ta.ATR(dataframe_macro, timeperiod=14)
dataframe_macro['20sma'] = ta.SMA(dataframe_macro,timeperiod=20)
dataframe_macro['21ema'] = ta.EMA(dataframe_macro,timeperiod=21)
dataframe_macro['bmsb'] = np.where((dataframe_macro['20sma'] > dataframe_macro['21ema']) & (dataframe_macro['close'] > dataframe_macro['20sma']), 1, 0)
dataframe = resampled_merge(dataframe, dataframe_macro)
dataframe['custom_stop_loss'] = np.where(dataframe['resample_{}_bmsb'.format(self.get_ticker_indicator()*window)] == 0, dataframe['low'] - dataframe['resample_{}_ATR'.format(self.get_ticker_indicator()*window)] * self.sl_factor_bear.value, dataframe['low'] - dataframe['resample_{}_ATR'.format(self.get_ticker_indicator()*window)] * self.sl_factor_bull.value)
dataframe['roi_price'] = np.where(dataframe['resample_{}_bmsb'.format(self.get_ticker_indicator()*window)] == 0, dataframe['close'] + dataframe['resample_{}_ATR'.format(self.get_ticker_indicator()*window)] * self.roi_factor_bear.value, dataframe['close'] + dataframe['resample_{}_ATR'.format(self.get_ticker_indicator()*window)] * self.roi_factor_bull.value)
return dataframe
def get_market_trend(self, dataframe):
pairs = self.dp.current_whitelist()
top20 = pairs[:20]
if self.market_df is None:
self.market_df = dataframe.copy()[['date']]
self.market_df['up'] = 0
for pair in top20:
df = self.dp.get_pair_dataframe(pair=pair, timeframe=self.informative_timeframe)
df['up'] = np.where(df['close'] > df['close'].shift(12), 1, 0)
self.market_df['up'] = self.market_df['up'].add(df['up'])
self.market_df['up-ratio'] = self.market_df['up'] / len(top20)
dataframe = pd.merge(dataframe, self.market_df[['date', 'up', 'up-ratio']], on='date', how='left')
return dataframe
def SSLChannels_ATR(self, 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 SROC(self, 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
## based on https://www.tradingview.com/script/9bUUSzM3-Madrid-Trend-Squeeze/
def MadSqueeze(self, dataframe, period=34, ref=13, sqzlen=5):
df = dataframe.copy()
# min period force
if period < 14:
period = 14
ma = ta.EMA(df['close'], period)
closema = df['close'] - ma
df['msq_closema'] = closema
refma = ta.EMA(df['close'], ref) - ma
df['msq_refma'] = refma
sqzma = ta.EMA(df['close'], sqzlen) - ma
df['msq_sqzma'] = sqzma
## Apply a non-parametric transformation to map the Madrid Trend Squeeze data to a Gaussian.
## We do this to even out the peaks across the dataframe, and end up with a normally distributed measure of the variance
## between ma, the reference EMA and the squeezed EMA
## The bigger the number, the bigger the peak detected. Buys and sells tend to happen at the peaks.
# max samples 100000
n_quants = 100000 if df.shape[0] >= 100000 else df.shape[0]-1
quantt = preprocessing.QuantileTransformer(output_distribution='normal', random_state=0, n_quantiles=n_quants)
df['msq_abs'] = (df['msq_closema'].fillna(0).abs() + df['msq_refma'].fillna(0).abs() + df['msq_sqzma'].fillna(0).abs())
df['msq_normabs'] = quantt.fit_transform(df[['msq_abs']])
return df['msq_normabs']