# 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 datetime import timedelta, datetime
from typing import Optional
from freqtrade import data
from freqtrade.persistence import Trade
from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter
from numpy.lib import math
from freqtrade.strategy.interface import IStrategy
import pandas
from pandas import DataFrame
import time
import logging
import calendar
from freqtrade.loggers import setup_logging
from freqtrade.strategy.strategy_helper import merge_informative_pair
# --------------------------------
# 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
from random import shuffle
logger = logging.getLogger(__name__)
operators = [
"D", # Disabled gene
">", # Indicator, bigger than cross indicator
"<", # Indicator, smaller than cross indicator
"=", # Indicator, equal with cross indicator
"C", # Indicator, crossed the cross indicator
"CA", # Indicator, crossed above the cross indicator
"CB", # Indicator, crossed below the cross indicator
">R", # Normalized indicator, bigger than real number
"=R", # Normalized indicator, equal with real number
"R", # Normalized indicator devided to cross indicator, bigger than real number
"/=R", # Normalized indicator devided to cross indicator, equal with real number
"/ 10)
# TODO : it ill callculated in populate indicators.
dataframe[indicator] = gene_calculator(dataframe, indicator)
dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator)
indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}"
if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]:
dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma)
if operator == ">":
condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage))
elif operator == "=":
condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage)))
elif operator == "<":
condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage))
elif operator == "C":
condition = (
(qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) |
(qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage)))
)
elif operator == "CA":
condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage)))
elif operator == "CB":
condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage)))
elif operator == ">R":
condition = (dataframe[indicator].shift(decalage) > real_num)
elif operator == "=R":
condition = (np.isclose(dataframe[indicator].shift(decalage), real_num))
elif operator == "R":
condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num)
elif operator == "/=R":
condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num))
elif operator == "/ dataframe[indicator_trend_sma].shift(decalage))
elif operator == "DT":
condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage))
elif operator == "OT":
condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)))
elif operator == "CUT":
condition = (
(
qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
) &
(
dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage)
)
)
elif operator == "CDT":
condition = (
(
qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
) &
(
dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)
)
)
elif operator == "COT":
condition = (
(
(
qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
) |
(
qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
)
) &
(
np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
)
)
return condition, dataframe
class Zeus_9(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_b_params = {
"buy_b_cat": "R", "=R", " 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)):
return 'b_percent_quick'
if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\
& (days < self.sell_b_too_old_day.value * 2)\
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
return "b_too_old_0.01"
if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\
& (days < self.sell_b_too_old_day.value * 3) \
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
return "b_too_old_0.02"
if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
return "b_too_old_0.03"
if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005):
return "b_quick_lost"
if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \
& (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \
& ((current_time - trade.open_date_utc).seconds >= 3600):
return "b_no_change"
if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \
& ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value):
return "b_quick_gain_param"
if self.profit_b_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 'b_sma5'
if self.profit_b_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 'b_sma10'
if self.profit_b_sma20.value:
if (current_profit > last_candle['bb_width'] / 1.3) \
& (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 'b_sma20'
if self.profit_b_over_rsi.value:
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_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 'b_over_rsi'
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \
(last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'b_over_rsi_2'
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \
(last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'b_over_rsi_max'
if self.profit_b_short_loss.value:
if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\
& (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'b_short_lost'
else:
if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)):
return 'h_percent_quick'
if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\
& (days < self.sell_h_too_old_day.value * 2)\
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
return "h_too_old_0.01"
if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\
& (days < self.sell_h_too_old_day.value * 3) \
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
return "h_too_old_0.02"
if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \
& (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
return "h_too_old_0.03"
if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005):
return "h_quick_lost"
if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \
& (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \
& ((current_time - trade.open_date_utc).seconds >= 3600):
return "h_no_change"
if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \
& ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value):
return "h_quick_gain_param"
if self.profit_h_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 'h_sma5'
if self.profit_h_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 'h_sma10'
if self.profit_h_sma20.value:
if (current_profit > last_candle['bb_width'] / 0.8) \
& (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 'h_sma20'
if self.profit_h_over_rsi.value:
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_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 'h_over_rsi'
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \
(last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'h_over_rsi_2'
if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_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 'h_over_rsi_max'
if self.profit_h_short_loss.value:
if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\
& (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'h_short_lost'
def informative_pairs(self):
# get access to all pairs available in whitelist.
pairs = self.dp.current_whitelist()
informative_pairs = [(pair, '4h') for pair in pairs]
informative_pairs += [(pair, '1h') for pair in pairs]
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['rsi'] = talib.RSI(dataframe)
dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5)
dataframe['pct_change'] = dataframe['close'].pct_change(5)
dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200)
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['min200_5'] = dataframe['min200'] * 1.005
dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean()
dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50)
dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200)
dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200']
dataframe['min_max200_5'] = (dataframe['min200'] * (1 + dataframe['min_max200'] / 5))
dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5)
dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10)
dataframe['sma10xpct+'] = dataframe['sma10'] * 1.0075
dataframe['sma10xpct-'] = dataframe['sma10'] * 0.9925
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['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum()
dataframe["volume10"] = dataframe["volume"].rolling(10).mean()
# 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"]
# )
#
# dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close']
# dataframe['min1.1'] = 1.01 * dataframe['min']
# 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())
dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0)
dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1)
dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2)
dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0)
dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1)
dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2)
dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0])
######################## INFORMATIVE 4h
informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h")
informative["rsi"] = talib.RSI(informative)
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)
dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True)
################### INFORMATIVE BTC 1H
# informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h")
# informative["rsi"] = talib.RSI(informative)
# informative["rsi_5"] = talib.RSI(informative, timeperiod=5)
# informative['pct_change_1'] = informative['close'].pct_change(1)
# dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True)
return dataframe
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(dataframe['close'] <= dataframe['sma10xpct-'])
& (dataframe['close'].shift(1) <= dataframe['close'])
& (dataframe['min50'].shift(2) == dataframe['min50'])
), ['buy', 'buy_tag']] = (1, 'buy_sma10')
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
return dataframe