363 lines
12 KiB
Python
363 lines
12 KiB
Python
from freqtrade.strategy.interface import IStrategy
|
|
from pandas import DataFrame
|
|
# --------------------------------
|
|
|
|
import talib.abstract as ta
|
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
|
import numpy as np # noqa
|
|
|
|
|
|
def activate(x):
|
|
return np.tanh(x) # tanh
|
|
|
|
params = {
|
|
'0-0-0-w': -0.53814,
|
|
'0-0-bias': -0.96407,
|
|
'1-0-0-w': -0.49249,
|
|
'10-0-0-w': 0.08845,
|
|
'11-0-0-w': -0.14317,
|
|
'12-0-0-w': 0.00923,
|
|
'13-0-0-w': 0.30464,
|
|
'14-0-0-w': -0.35835,
|
|
'15-0-0-w': -0.49712,
|
|
'16-0-0-w': 0.76135,
|
|
'17-0-0-w': -0.75257,
|
|
'18-0-0-w': -0.04622,
|
|
'19-0-0-w': 0.10012,
|
|
'2-0-0-w': -0.23534,
|
|
'20-0-0-w': -0.04553,
|
|
'21-0-0-w': -0.35334,
|
|
'22-0-0-w': 0.17952,
|
|
'23-0-0-w': 0.44446,
|
|
'24-0-0-w': -0.15875,
|
|
'25-0-0-w': 0.97565,
|
|
'26-0-0-w': -0.89948,
|
|
'27-0-0-w': 0.61777,
|
|
'28-0-0-w': -0.60204,
|
|
'29-0-0-w': -0.85229,
|
|
'3-0-0-w': 0.47262,
|
|
'30-0-0-w': -0.52791,
|
|
'31-0-0-w': 0.98494,
|
|
'4-0-0-w': -0.54942,
|
|
'5-0-0-w': 0.40523,
|
|
'6-0-0-w': 0.4723,
|
|
'7-0-0-w': 0.63297,
|
|
'8-0-0-w': 0.07159,
|
|
'9-0-0-w': -0.86791,
|
|
'adx-bias': -0.48719,
|
|
'ao-bias': -0.87518,
|
|
'aroonosc-bias': -0.56096,
|
|
'bb_percent-bias': -0.98703,
|
|
'bb_width-bias': -0.73742,
|
|
'cci-bias': 0.47039,
|
|
'end-0-w': -0.81658,
|
|
'end-bias': 0.74656,
|
|
'fastd-bias': -0.2793,
|
|
'fisher_rsi_norm-bias': -0.36065,
|
|
'kc_percent-bias': 0.76707,
|
|
'kc_width-bias': 0.5489,
|
|
'macd-bias': 0.55448,
|
|
'macdhist-bias': -0.83133,
|
|
'macdsignal-bias': 0.30828,
|
|
'mfi-bias': -0.13097,
|
|
'roc-bias': -0.78885,
|
|
'rsi-bias': 0.9856,
|
|
'sar-bias': 0.43812,
|
|
'sma10-bias': -0.39019,
|
|
'sma100-bias': 0.03558,
|
|
'sma21-bias': 0.07457,
|
|
'sma3-bias': 0.93633,
|
|
'sma5-bias': -0.93329,
|
|
'sma50-bias': -0.60637,
|
|
'tema10-bias': -0.45946,
|
|
'tema100-bias': 0.1662,
|
|
'tema21-bias': 0.68466,
|
|
'tema3-bias': 0.25368,
|
|
'tema5-bias': -0.88818,
|
|
'tema50-bias': 0.3019,
|
|
'uo-bias': 0.71019,
|
|
'wbb_percent-bias': -0.55964,
|
|
'wbb_width-bias': 0.23523,
|
|
|
|
's-0-0-0-w': 0.85409,
|
|
's-0-0-bias': -0.04613,
|
|
's-1-0-0-w': -0.14997,
|
|
's-10-0-0-w': -0.67008,
|
|
's-11-0-0-w': -0.40221,
|
|
's-12-0-0-w': 0.64553,
|
|
's-13-0-0-w': 0.22838,
|
|
's-14-0-0-w': 0.99977,
|
|
's-15-0-0-w': 0.89363,
|
|
's-16-0-0-w': -0.88212,
|
|
's-17-0-0-w': -0.71813,
|
|
's-18-0-0-w': 0.41602,
|
|
's-19-0-0-w': -0.48389,
|
|
's-2-0-0-w': 0.09649,
|
|
's-20-0-0-w': 0.64273,
|
|
's-21-0-0-w': -0.31671,
|
|
's-22-0-0-w': 0.9663,
|
|
's-23-0-0-w': 0.00229,
|
|
's-24-0-0-w': 0.96244,
|
|
's-25-0-0-w': -0.24513,
|
|
's-26-0-0-w': 0.52312,
|
|
's-27-0-0-w': 0.44742,
|
|
's-28-0-0-w': -0.03916,
|
|
's-29-0-0-w': 0.88882,
|
|
's-3-0-0-w': -0.32112,
|
|
's-30-0-0-w': -0.70886,
|
|
's-31-0-0-w': -0.42672,
|
|
's-4-0-0-w': -0.55265,
|
|
's-5-0-0-w': 0.56105,
|
|
's-6-0-0-w': 0.47436,
|
|
's-7-0-0-w': 0.58136,
|
|
's-8-0-0-w': -0.48308,
|
|
's-9-0-0-w': -0.16024,
|
|
's-adx-bias': -0.4091,
|
|
's-ao-bias': 0.76889,
|
|
's-aroonosc-bias': 0.16228,
|
|
's-bb_percent-bias': 0.19407,
|
|
's-bb_width-bias': 0.11795,
|
|
's-cci-bias': 0.8379,
|
|
's-end-0-w': -0.14648,
|
|
's-end-bias': -0.85697,
|
|
's-fastd-bias': -0.00581,
|
|
's-fisher_rsi_norm-bias': -0.05253,
|
|
's-kc_percent-bias': -0.3562,
|
|
's-kc_width-bias': 0.67451,
|
|
's-macd-bias': -0.17742,
|
|
's-macdhist-bias': -0.58328,
|
|
's-macdsignal-bias': -0.79847,
|
|
's-mfi-bias': -0.48236,
|
|
's-roc-bias': -0.5914,
|
|
's-rsi-bias': -0.9618,
|
|
's-sar-bias': 0.57033,
|
|
's-sma10-bias': 0.14349,
|
|
's-sma100-bias': 0.02401,
|
|
's-sma21-bias': 0.78191,
|
|
's-sma3-bias': 0.72279,
|
|
's-sma5-bias': -0.19383,
|
|
's-sma50-bias': 0.63697,
|
|
's-tema10-bias': 0.96837,
|
|
's-tema100-bias': 0.77171,
|
|
's-tema21-bias': 0.67279,
|
|
's-tema3-bias': -0.24583,
|
|
's-tema5-bias': -0.08997,
|
|
's-tema50-bias': 0.65532,
|
|
's-uo-bias': 0.67701,
|
|
's-wbb_percent-bias': -0.658,
|
|
's-wbb_width-bias': -0.71056
|
|
}
|
|
|
|
network_shape = [1]
|
|
|
|
class MyStrategy(IStrategy):
|
|
# ROI table:
|
|
minimal_roi = {
|
|
"0": 0.21029,
|
|
"11": 0.05876,
|
|
"57": 0.02191,
|
|
"281": 0
|
|
}
|
|
|
|
# Stoploss:
|
|
stoploss = -0.07693
|
|
|
|
# Optimal ticker interval for the strategy
|
|
ticker_interval = '2h'
|
|
|
|
# Trailing stop:
|
|
trailing_only_offset_is_reached = False
|
|
trailing_stop = True
|
|
trailing_stop_positive = 0.01019
|
|
trailing_stop_positive_offset = 0.01164
|
|
|
|
# run "populate_indicators" only for new candle
|
|
process_only_new_candles = True
|
|
|
|
# Experimental settings (configuration will overide these if set)
|
|
use_sell_signal = True
|
|
sell_profit_only = True
|
|
ignore_roi_if_buy_signal = True
|
|
|
|
startup_candle_count = 100
|
|
|
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
"""
|
|
Adds several different TA indicators to the given DataFrame
|
|
|
|
Performance Note: For the best performance be frugal on the number of indicators
|
|
you are using. Let uncomment only the indicator you are using in your strategies
|
|
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
|
|
"""
|
|
|
|
# Momentum Indicators
|
|
# ------------------------------------
|
|
|
|
# ADX
|
|
dataframe['adx'] = ta.ADX(dataframe) / 100
|
|
|
|
# # Plus Directional Indicator / Movement
|
|
# dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
|
# dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
|
|
|
# # Minus Directional Indicator / Movement
|
|
# dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
|
|
# dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
|
|
|
# # Aroon, Aroon Oscillator
|
|
# aroon = ta.AROON(dataframe)
|
|
# dataframe['aroonup'] = aroon['aroonup']
|
|
# dataframe['aroondown'] = aroon['aroondown']
|
|
dataframe['aroonosc'] = ta.AROONOSC(dataframe) / 100
|
|
|
|
# # Awesome Oscillator
|
|
dataframe['ao'] = ((qtpylib.awesome_oscillator(dataframe) > 0).astype(int) - 0.5) * 2
|
|
|
|
# # Keltner Channel
|
|
keltner = qtpylib.keltner_channel(dataframe)
|
|
dataframe["kc_upperband"] = keltner["upper"]
|
|
dataframe["kc_lowerband"] = keltner["lower"]
|
|
dataframe["kc_middleband"] = keltner["mid"]
|
|
dataframe["kc_percent"] = (
|
|
(dataframe["close"] - dataframe["kc_lowerband"]) /
|
|
(dataframe["kc_upperband"] - dataframe["kc_lowerband"])
|
|
)
|
|
dataframe["kc_width"] = (
|
|
(dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"]
|
|
)
|
|
|
|
# # Ultimate Oscillator
|
|
dataframe['uo'] = ta.ULTOSC(dataframe) / 100
|
|
|
|
# # Commodity Channel Index: values [Oversold:-100, Overbought:100]
|
|
dataframe['cci'] = ta.CCI(dataframe) / 200
|
|
|
|
# RSI
|
|
dataframe['rsi'] = ta.RSI(dataframe) / 100
|
|
|
|
# # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy)
|
|
rsi = 0.1 * (dataframe['rsi'] * 100 - 50)
|
|
fisher_rsi = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1)
|
|
|
|
# # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy)
|
|
dataframe['fisher_rsi_norm'] = 50 * (fisher_rsi + 1) / 100
|
|
|
|
# # Stochastic Slow
|
|
# stoch = ta.STOCH(dataframe)
|
|
# dataframe['slowd'] = stoch['slowd']
|
|
# dataframe['slowk'] = stoch['slowk']
|
|
|
|
# Stochastic Fast
|
|
stoch_fast = ta.STOCHF(dataframe)
|
|
dataframe['fastd'] = stoch_fast['fastd'] / 100
|
|
|
|
# # Stochastic RSI
|
|
# stoch_rsi = ta.STOCHRSI(dataframe)
|
|
# dataframe['fastd_rsi'] = stoch_rsi['fastd']
|
|
# dataframe['fastk_rsi'] = stoch_rsi['fastk']
|
|
|
|
# MACD
|
|
macd = ta.MACD(dataframe)
|
|
dataframe['macd'] = macd['macd']
|
|
dataframe['macdsignal'] = macd['macdsignal']
|
|
dataframe['macdhist'] = macd['macdhist']
|
|
|
|
# MFI
|
|
dataframe['mfi'] = ta.MFI(dataframe) / 100
|
|
|
|
# # ROC
|
|
dataframe['roc'] = ta.ROC(dataframe) / 100
|
|
|
|
# Overlap Studies
|
|
# ------------------------------------
|
|
|
|
# Bollinger Bands
|
|
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
|
dataframe['bb_lowerband'] = bollinger['lower']
|
|
dataframe['bb_middleband'] = bollinger['mid']
|
|
dataframe['bb_upperband'] = bollinger['upper']
|
|
dataframe["bb_percent"] = (
|
|
(dataframe["close"] - dataframe["bb_lowerband"]) /
|
|
(dataframe["bb_upperband"] - dataframe["bb_lowerband"])
|
|
)
|
|
dataframe["bb_width"] = (
|
|
(dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"]
|
|
)
|
|
|
|
# Bollinger Bands - Weighted (EMA based instead of SMA)
|
|
weighted_bollinger = qtpylib.weighted_bollinger_bands(
|
|
qtpylib.typical_price(dataframe), window=20, stds=2
|
|
)
|
|
dataframe["wbb_upperband"] = weighted_bollinger["upper"]
|
|
dataframe["wbb_lowerband"] = weighted_bollinger["lower"]
|
|
dataframe["wbb_middleband"] = weighted_bollinger["mid"]
|
|
dataframe["wbb_percent"] = (
|
|
(dataframe["close"] - dataframe["wbb_lowerband"]) /
|
|
(dataframe["wbb_upperband"] - dataframe["wbb_lowerband"])
|
|
)
|
|
dataframe["wbb_width"] = (
|
|
(dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"]
|
|
)
|
|
|
|
# # SMA - Simple Moving Average
|
|
dataframe['sma3'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=3) - 1
|
|
dataframe['sma5'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=5) - 1
|
|
dataframe['sma10'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=10) - 1
|
|
dataframe['sma21'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=21) - 1
|
|
dataframe['sma50'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=50) - 1
|
|
dataframe['sma100'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=100) - 1
|
|
|
|
# Parabolic SAR
|
|
dataframe['sar'] = dataframe['close'] / ta.SAR(dataframe) - 1
|
|
|
|
# TEMA - Triple Exponential Moving Average
|
|
dataframe['tema3'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=3) - 1
|
|
dataframe['tema5'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=5) - 1
|
|
dataframe['tema10'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=10) - 1
|
|
dataframe['tema21'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=21) - 1
|
|
dataframe['tema50'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=50) - 1
|
|
dataframe['tema100'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=100) - 1
|
|
|
|
return dataframe
|
|
|
|
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
"""
|
|
Based on TA indicators, populates the buy signal for the given dataframe
|
|
:param dataframe: DataFrame
|
|
:return: DataFrame with buy column
|
|
"""
|
|
indicators = ['aroonosc', 'ao', 'uo', 'cci', 'rsi', 'fisher_rsi_norm', 'sar',
|
|
'sma3', 'sma5', 'sma10', 'sma21', 'sma50', 'sma100',
|
|
'tema3', 'tema5', 'tema10', 'tema21', 'tema50', 'tema100',
|
|
'fastd', 'adx', 'bb_percent', 'bb_width', 'macd', 'macdsignal', 'macdhist', 'mfi',
|
|
'wbb_percent', 'wbb_width', 'roc', 'kc_percent', 'kc_width']
|
|
inputs = []
|
|
for indicator in indicators:
|
|
inputs.append(dataframe[indicator] + params[indicator + '-bias'])
|
|
|
|
for index, layer_size in enumerate(network_shape):
|
|
outputs = []
|
|
for n in range(layer_size):
|
|
weight = 0
|
|
for i, input in enumerate(inputs):
|
|
weight += params['{}-{}-{}-w'.format(i, index, n)] * input
|
|
weight += params['{}-{}-bias'.format(index, n)]
|
|
outputs.append(activate(weight))
|
|
inputs = outputs
|
|
|
|
weight = 0
|
|
for i, input in enumerate(inputs):
|
|
weight += params['end-{}-w'.format(i)] * input
|
|
weight += params['end-bias']
|
|
|
|
dataframe.loc[activate(weight) > 0, 'buy'] = 1
|
|
|
|
# Check that the candle had volume
|
|
dataframe.loc[dataframe['volume'] <= 0, 'buy'] = 0
|
|
|
|
return dataframe
|
|
|
|
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
|
|
return dataframe
|