357 lines
17 KiB
Python
357 lines
17 KiB
Python
# Zeus Strategy: First Generation of GodStra Strategy with maximum
|
|
# AVG/MID profit in USDT
|
|
# Author: @Mablue (Masoud Azizi)
|
|
# github: https://github.com/mablue/
|
|
# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta)
|
|
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus
|
|
# --- Do not remove these libs ---
|
|
from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter
|
|
|
|
from freqtrade.strategy.interface import IStrategy
|
|
from pandas import DataFrame
|
|
# --------------------------------
|
|
|
|
# Add your lib to import here
|
|
import ta
|
|
from functools import reduce
|
|
import numpy as np
|
|
import talib.abstract as talib
|
|
from freqtrade.strategy.strategy_helper import merge_informative_pair
|
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
|
|
|
|
|
class Zeus_2_1(IStrategy):
|
|
|
|
# * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793
|
|
# "max_open_trades": 10,
|
|
# "stake_currency": "BTC",
|
|
# "stake_amount": 0.01,
|
|
# "tradable_balance_ratio": 0.99,
|
|
# "timeframe": "4h",
|
|
# "dry_run_wallet": 0.1,
|
|
|
|
# Buy hyperspace params:
|
|
buy_params = {
|
|
"buy_cat": "<R",
|
|
"buy_real": 0.0128,
|
|
}
|
|
|
|
# Sell hyperspace params:
|
|
sell_params = {
|
|
"sell_cat": "=R",
|
|
"sell_real": 0.9455,
|
|
}
|
|
|
|
# ROI table:
|
|
minimal_roi = {
|
|
"0": 0.564,
|
|
"567": 0.273,
|
|
"2814": 0.12,
|
|
"7675": 0
|
|
}
|
|
|
|
# Stoploss:
|
|
stoploss = -0.256
|
|
|
|
buy_real = DecimalParameter(0.001, 0.999, decimals=4, default=0.11908, space='buy')
|
|
buy_cat = CategoricalParameter([">R", "=R", "<R"], default='<R', space='buy')
|
|
buy_pct = DecimalParameter(0.001, 0.02, decimals=3, default=0.005, space='buy')
|
|
buy_pct_1 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
|
|
buy_pct_3 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
|
|
buy_pct_5 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
|
|
buy_bb_lowerband = DecimalParameter(1, 1.05, default=1, decimals=2, space='buy')
|
|
buy_bb_width = DecimalParameter(0.01, 0.15, default=0.065, decimals=2, space='buy')
|
|
|
|
# sell_real = DecimalParameter(0.001, 0.999, decimals=4, default=0.59608, space='sell')
|
|
# sell_cat = CategoricalParameter([">R", "=R", "<R"], default='>R', space='sell')
|
|
|
|
# Buy hypers
|
|
timeframe = '4h'
|
|
|
|
plot_config = {
|
|
"main_plot": {
|
|
"min200": {"color": "#86c932"},
|
|
"max50": {"color": "white"},
|
|
"max200": {"color": "yellow"},
|
|
"sma3_1d": {"color": "pink"},
|
|
"sma5_1d": {"color": "blue"},
|
|
"sma10_1d": {"color": "orange"},
|
|
},
|
|
"subplots": {
|
|
"Ind": {
|
|
"trend_ichimoku_base": {
|
|
"color": "#dd1384",
|
|
},
|
|
"trend_kst_diff": {
|
|
"color": "#850678",
|
|
},
|
|
},
|
|
"Percent": {
|
|
"max_min": {"color": "#74effc"},
|
|
"pct_change_1_1d": {"color": "green"},
|
|
"pct_change_3_1d": {"color": "orange"},
|
|
"pct_change_5_1d": {"color": "red"},
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
profit_no_change = BooleanParameter(default=True, space="sell")
|
|
profit_quick_lost = BooleanParameter(default=True, space="sell")
|
|
profit_sma5 = BooleanParameter(default=True, space="sell")
|
|
profit_sma10 = BooleanParameter(default=True, space="sell")
|
|
profit_sma20 = BooleanParameter(default=True, space="sell")
|
|
profit_quick_gain = BooleanParameter(default=True, space="sell")
|
|
profit_quick_gain_3 = BooleanParameter(default=True, space="sell")
|
|
profit_old_sma10 = BooleanParameter(default=True, space="sell")
|
|
profit_very_old_sma10 = BooleanParameter(default=True, space="sell")
|
|
profit_over_rsi = BooleanParameter(default=True, space="sell")
|
|
profit_short_loss = BooleanParameter(default=True, space="sell")
|
|
|
|
sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
|
|
sell_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
|
|
sell_candels = IntParameter(0, 48, default=12, space='sell')
|
|
|
|
sell_too_old_day = IntParameter(0, 10, default=5, space='sell')
|
|
sell_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
|
|
|
|
sell_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell')
|
|
sell_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell')
|
|
|
|
sell_RSI = IntParameter(70, 98, default=88, space='sell')
|
|
sell_RSI2 = IntParameter(70, 98, default=88, space='sell')
|
|
sell_RSI3 = IntParameter(70, 98, default=80, space='sell')
|
|
|
|
sell_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell')
|
|
# sell_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell')
|
|
|
|
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, self.timeframe)
|
|
last_candle = dataframe.iloc[-1].squeeze()
|
|
previous_last_candle = dataframe.iloc[-2].squeeze()
|
|
previous_5_candle = dataframe.iloc[-5].squeeze()
|
|
|
|
expected_profit = 0.01
|
|
# if (last_candle['pct_change_1_1d'] < 0):
|
|
# expected_profit = self.sell_expected_profit.value
|
|
#
|
|
# if (current_profit > expected_profit) & (last_candle['pct_change_1_1d'] < 0):
|
|
# return "exp_profit_down"
|
|
|
|
if (current_profit >= - self.sell_too_old_percent.value) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value)\
|
|
& ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 2)\
|
|
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
|
|
return "too_old_0.01"
|
|
if (current_profit >= - self.sell_too_old_percent.value * 2) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 2)\
|
|
& ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 3) \
|
|
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
|
|
return "too_old_0.02"
|
|
if (current_profit >= - self.sell_too_old_percent.value * 3) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 3) \
|
|
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
|
|
return "too_old_0.03"
|
|
|
|
if self.profit_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005):
|
|
return "quick_lost"
|
|
|
|
if self.profit_no_change.value and (current_profit > self.sell_profit_no_change.value) \
|
|
& (last_candle['percent10'] < self.sell_profit_percent10.value) & (last_candle['percent5'] < 0) \
|
|
& ((current_time - trade.open_date_utc).seconds >= 3600):
|
|
return "no_change"
|
|
|
|
if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \
|
|
& ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value):
|
|
return "quick_gain_param"
|
|
|
|
if self.profit_sma5.value:
|
|
if (current_profit > expected_profit) \
|
|
& ((previous_5_candle['sma5'] > last_candle['sma5']) \
|
|
| (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \
|
|
& ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
|
|
# print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'sma5'
|
|
|
|
if self.profit_sma10.value:
|
|
if (current_profit > expected_profit) \
|
|
& ((previous_5_candle['sma10'] > last_candle['sma10']) \
|
|
| (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \
|
|
& ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
|
|
# print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'sma10'
|
|
|
|
if self.profit_sma20.value:
|
|
if (current_profit > 0.005) \
|
|
& (previous_last_candle['sma10'] > last_candle['sma10']) \
|
|
& ((current_time - trade.open_date_utc).seconds >= 3600) \
|
|
& ((previous_last_candle['sma20'] > last_candle['sma20']) &
|
|
((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))):
|
|
# print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'sma20'
|
|
|
|
if self.profit_over_rsi.value:
|
|
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
|
|
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'over_rsi'
|
|
|
|
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \
|
|
(last_candle['percent'] < - self.sell_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
|
|
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'over_rsi_2'
|
|
|
|
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI3.value) & \
|
|
(last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
|
|
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'over_rsi_max'
|
|
|
|
if self.profit_short_loss.value:
|
|
if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\
|
|
& ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
|
|
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'short_lost'
|
|
|
|
def informative_pairs(self):
|
|
# get access to all pairs available in whitelist.
|
|
pairs = self.dp.current_whitelist()
|
|
# Assign tf to each pair so they can be downloaded and cached for strategy.
|
|
# informative_pairs = [(pair, "5m") for pair in pairs]
|
|
informative_pairs = [(pair, '1d') for pair in pairs]
|
|
# informative_pairs += [(pair, '1w') for pair in pairs]
|
|
|
|
# Optionally Add additional "static" pairs
|
|
# informative_pairs = [("BTC/USDT", "1w"), ("BTC/USDT", "1d"), ("BTC/USDT", "5m")]
|
|
|
|
return informative_pairs
|
|
|
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
# Add all ta features
|
|
|
|
dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line(
|
|
dataframe['high'],
|
|
dataframe['low'],
|
|
window1=9,
|
|
window2=26,
|
|
visual=False,
|
|
fillna=False
|
|
)
|
|
KST = ta.trend.KSTIndicator(
|
|
close=dataframe['close'],
|
|
roc1=10,
|
|
roc2=15,
|
|
roc3=20,
|
|
roc4=30,
|
|
window1=10,
|
|
window2=10,
|
|
window3=10,
|
|
window4=15,
|
|
nsig=9,
|
|
fillna=False
|
|
)
|
|
|
|
dataframe['trend_kst_diff'] = KST.kst_diff()
|
|
dataframe['pct_change'] = dataframe['close'].pct_change(5);
|
|
dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10)
|
|
dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20)
|
|
dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50)
|
|
dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200)
|
|
dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50)
|
|
dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200)
|
|
dataframe['rsi'] = talib.RSI(dataframe)
|
|
dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5)
|
|
dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10)
|
|
dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20)
|
|
dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50)
|
|
dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100)
|
|
dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"]
|
|
dataframe["percent5"] = dataframe["percent"].rolling(5).sum()
|
|
dataframe["percent3"] = dataframe["percent"].rolling(3).sum()
|
|
dataframe["percent10"] = dataframe["percent"].rolling(10).sum()
|
|
dataframe["percent20"] = dataframe["percent"].rolling(20).sum()
|
|
dataframe["percent50"] = dataframe["percent"].rolling(50).sum()
|
|
dataframe['sar'] = talib.SAR(dataframe)
|
|
|
|
# Normalization
|
|
tib = dataframe['trend_ichimoku_base']
|
|
dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min())
|
|
tkd = dataframe['trend_kst_diff']
|
|
dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min())
|
|
|
|
informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d")
|
|
informative["rsi"] = talib.RSI(informative)
|
|
informative["max3"] = talib.MAX(informative['close'], timeperiod=3)
|
|
informative["min3"] = talib.MIN(informative['close'], timeperiod=3)
|
|
informative['pct_change_1'] = informative['close'].pct_change(1)
|
|
informative['pct_change_3'] = informative['close'].pct_change(3)
|
|
informative['pct_change_5'] = informative['close'].pct_change(5)
|
|
informative['sma3'] = talib.SMA(informative, timeperiod=3)
|
|
informative['sma5'] = talib.SMA(informative, timeperiod=5)
|
|
informative['sma10'] = talib.SMA(informative, timeperiod=10)
|
|
informative['sar'] = talib.SAR(informative)
|
|
|
|
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2)
|
|
informative['bb_lowerband'] = bollinger['lower']
|
|
informative['bb_middleband'] = bollinger['mid']
|
|
informative['bb_upperband'] = bollinger['upper']
|
|
informative["bb_percent"] = (
|
|
(informative["close"] - informative["bb_lowerband"]) /
|
|
(informative["bb_upperband"] - informative["bb_lowerband"])
|
|
)
|
|
informative["bb_width"] = (
|
|
(informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"]
|
|
)
|
|
|
|
dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True)
|
|
return dataframe
|
|
|
|
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
conditions = []
|
|
IND = 'trend_ichimoku_base'
|
|
REAL = self.buy_real.value
|
|
OPR = self.buy_cat.value
|
|
DFIND = dataframe[IND]
|
|
# print(DFIND.mean())
|
|
if OPR == ">R":
|
|
conditions.append(DFIND > REAL)
|
|
elif OPR == "=R":
|
|
conditions.append(np.isclose(DFIND, REAL))
|
|
elif OPR == "<R":
|
|
conditions.append(DFIND < REAL)
|
|
|
|
if conditions:
|
|
dataframe.loc[
|
|
(reduce(lambda x, y: x & y, conditions))
|
|
& (dataframe['pct_change'] < - self.buy_pct.value)
|
|
& (dataframe['close'] <= dataframe['min200'] * 1.002)
|
|
& (dataframe['pct_change_1_1d'] > self.buy_pct_1.value)
|
|
& (dataframe['pct_change_3_1d'] > self.buy_pct_3.value)
|
|
& (dataframe['pct_change_5_1d'] > 0) #self.buy_pct_5.value)
|
|
#& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_bb_lowerband.value)
|
|
& (dataframe['bb_width_1d'] >= self.buy_bb_width.value)
|
|
& (dataframe['close'] <= dataframe['sma5_1d'])
|
|
& (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d'])
|
|
,
|
|
'buy'] = 1
|
|
|
|
return dataframe
|
|
|
|
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
# conditions = []
|
|
# IND = 'trend_kst_diff'
|
|
# REAL = self.sell_real.value
|
|
# OPR = self.sell_cat.value
|
|
# DFIND = dataframe[IND]
|
|
# # print(DFIND.mean())
|
|
#
|
|
# if OPR == ">R":
|
|
# conditions.append(DFIND > REAL)
|
|
# elif OPR == "=R":
|
|
# conditions.append(np.isclose(DFIND, REAL))
|
|
# elif OPR == "<R":
|
|
# conditions.append(DFIND < REAL)
|
|
#
|
|
# if conditions:
|
|
# dataframe.loc[
|
|
# reduce(lambda x, y: x & y, conditions),
|
|
# 'sell'] = 1
|
|
|
|
return dataframe
|