# Guru Skippyasurmuni Strategy # Author: AZcoigreach # github: https://github.com/azcoigreach/ # ___ _ _ ___ _ _ # / __|| | | || _ \| | | | # | (_ || |_| || /| |_| | # \___| \___/ |_|_\ \___/ # ___ _ __ ___ ___ ___ __ __ _ ___ _ _ ___ __ __ _ _ _ _ ___ # / __|| |/ /|_ _|| _ \| _ \\ \ / //_\ / __|| | | || _ \| \/ || | | || \| ||_ _| # \__ \| ' < | | | _/| _/ \ V // _ \ \__ \| |_| || /| |\/| || |_| || .` | | | # |___/|_|\_\|___||_| |_| |_|/_/ \_\|___/ \___/ |_|_\|_| |_| \___/ |_|\_||___| # ''' Guru Skippyasurmuni - Even though Skippy is designed to be easily modified and hyperopted. However, Skippy has alread spent a lot of time thinking about the best way to trade crypto. He has come to the conclusing - simple is better. A couple basic indicators and some simple rules will yield the best results in every condition. ''' # Hyperopting ''' docker-compose run --rm freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell --strategy GuruSkippyasurmuni -e 2000 --timerange=20220314- --eps ''' # Backtesting - Powershell ''' $1 = 20220314 ; $2 = "day" ; docker-compose run --rm freqtrade backtesting --datadir user_data/data/binanceus --config /freqtrade/user_data/SkippyGod_config.json --export trades -s SkippyGod --fee 0.00075 --timerange=$1- --breakdown $2 --eps ; docker-compose run --rm freqtrade plot-dataframe -s SkippyGod --config /freqtrade/user_data/SkippyGod_config.json -i 5m --timerange=$1- --indicators2 AROONOSC-5 RSI-5 ; docker-compose run --rm freqtrade plot-profit -s SkippyGod --config /freqtrade/user_data/SkippyGod_config.json -i 5m --timerange=$1- ''' # --- Do not remove these libs --- from freqtrade.strategy.interface import IStrategy from typing import Dict, List from functools import reduce from pandas import DataFrame # -------------------------------- import talib.abstract as ta import numpy as np import freqtrade.vendor.qtpylib.indicators as qtpylib import datetime from technical.util import resample_to_interval, resampled_merge from datetime import datetime, timedelta from freqtrade.persistence import Trade from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter import technical.indicators as ftt from freqtrade.exchange import timeframe_to_prev_date import warnings from pandas.core.common import SettingWithCopyWarning warnings.simplefilter(action="ignore", category=SettingWithCopyWarning) from random import shuffle # Initiate genes splicing --> Source: MaBlue GodStrNew strategy --> Target: Guru Skippyasurmuni strategy. god_genes = set() ########################### SETTINGS ############################## # RSI and an Aroon Oscillator are the only two metrics you need to catch the highs and lows god_genes = { 'RSI', # Relative Strength Index 'AROONOSC', # Aroon Oscillator } timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] 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] > dataframe[crossed_indicator] ) elif operator == "=": condition = ( np.isclose(dataframe[indicator], dataframe[crossed_indicator]) ) elif operator == "<": condition = ( dataframe[indicator] < dataframe[crossed_indicator] ) elif operator == "C": condition = ( (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | (qtpylib.crossed_above(dataframe[indicator], dataframe[crossed_indicator])) ) elif operator == "CA": condition = ( qtpylib.crossed_above(dataframe[indicator], dataframe[crossed_indicator]) ) elif operator == "CB": condition = ( qtpylib.crossed_below( dataframe[indicator], dataframe[crossed_indicator]) ) elif operator == ">R": condition = ( dataframe[indicator] > real_num ) elif operator == "=R": condition = ( np.isclose(dataframe[indicator], real_num) ) elif operator == "R": condition = ( dataframe[indicator].div(dataframe[crossed_indicator]) > real_num ) elif operator == "/=R": condition = ( np.isclose(dataframe[indicator].div(dataframe[crossed_indicator]), real_num) ) elif operator == "/ dataframe[indicator_trend_sma] ) elif operator == "DT": condition = ( dataframe[indicator] < dataframe[indicator_trend_sma] ) elif operator == "OT": condition = ( np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) ) elif operator == "CUT": condition = ( ( qtpylib.crossed_above( dataframe[indicator], dataframe[indicator_trend_sma] ) ) & ( dataframe[indicator] > dataframe[indicator_trend_sma] ) ) elif operator == "CDT": condition = ( ( qtpylib.crossed_below( dataframe[indicator], dataframe[indicator_trend_sma] ) ) & ( dataframe[indicator] < dataframe[indicator_trend_sma] ) ) elif operator == "COT": condition = ( ( ( qtpylib.crossed_below( dataframe[indicator], dataframe[indicator_trend_sma] ) ) | ( qtpylib.crossed_above( dataframe[indicator], dataframe[indicator_trend_sma] ) ) ) & ( np.isclose( dataframe[indicator], dataframe[indicator_trend_sma] ) ) ) return condition, dataframe class GuruSkippyasurmuni(IStrategy): INTERFACE_VERSION = 2 # Variables DATESTAMP = 0 COUNT = 0 custom_info = { } # Skippy's BUY and SELL conditions # Buy hyperspace params: buy_params = { "buy_crossed_indicator0": "STOCHRSI-0-15", "buy_crossed_indicator1": "MACDFIX-0-15", "buy_crossed_indicator2": "STOCHRSI-0-55", "buy_indicator0": "MACDFIX-0-15", "buy_indicator1": "RSI-5", "buy_indicator2": "AROONOSC-5", "buy_operator0": "D", "buy_operator1": "R", "sell_operator2": "=R", "sell_real_num0": 0.65, "sell_real_num1": 0.9, "sell_real_num2": 1.0, } # ROI table: minimal_roi = { "0": 1000.0 } # Stoploss: stoploss = -1.0 # Trailing stop: trailing_stop = False trailing_stop_positive = 0.001 trailing_stop_positive_offset = 0.01 trailing_only_offset_is_reached = True # Sell signal use_sell_signal = True sell_profit_only = True ignore_roi_if_buy_signal = False # Optimal timeframe for the strategy timeframe = '5m' # timeframe = '1m' process_only_new_candles = True # DCA config position_adjustment_enable = True max_entry_position_adjustment = 24 dca_multiplier = 0.15 # Protections @property def protections(self): return [ { "method": "CooldownPeriod", "stop_duration_candles": 3 } ] # TODO: Its not dry code! # Buy Hyperoptable Parameters/Spaces. buy_crossed_indicator0 = CategoricalParameter( god_genes_with_timeperiod, default="STOCHRSI-0-15", space='buy') buy_crossed_indicator1 = CategoricalParameter( god_genes_with_timeperiod, default="MACDFIX-0-15", space='buy') buy_crossed_indicator2 = CategoricalParameter( god_genes_with_timeperiod, default="STOCHRSI-0-55", space='buy') buy_indicator0 = CategoricalParameter( god_genes_with_timeperiod, default="MACDFIX-0-15", space='buy') buy_indicator1 = CategoricalParameter( god_genes_with_timeperiod, default="RSI-5", space='buy') buy_indicator2 = CategoricalParameter( god_genes_with_timeperiod, default="AROONOSC-5", space='buy') buy_operator0 = CategoricalParameter(operators, default="D", space='buy') buy_operator1 = CategoricalParameter(operators, default=" DataFrame: # Check if the entry already exists pair = metadata['pair'] if not pair in self.custom_info: # Create empty entry for this pair {DATESTAMP} self.custom_info[pair] = [''] return dataframe def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, current_profit: float, **kwargs) -> bool: # Minimum 1% profit before Sell trigger if current_profit < 0.01: return False return True def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, **kwargs) -> float: # Split total stake amounst the bots and the max trades per bot custom_stake = self.wallets.get_total_stake_amount() / self.config['max_open_trades'] / (self.max_entry_position_adjustment + 1) if custom_stake >= min_stake: return custom_stake elif custom_stake < min_stake: return min_stake else: return proposed_stake # Skippy's has finally figured out how to stake positions. # You would think this is less difficult than forming a wormhole. You would be wrong.. def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: float, max_stake: float, **kwargs): dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) if(len(dataframe) < 1): return None last_candle = dataframe.iloc[-1].squeeze() if(self.custom_info[trade.pair][self.DATESTAMP] != last_candle['date']): # Trigger once per cnadle self.custom_info[trade.pair][self.DATESTAMP] = last_candle['date'] # If current total profit is greater than value don't adjust. if current_profit > -0.01: return None # If last candle had 'buy' indicator adjust stake by original stake_amount if last_candle['buy'] > 0: filled_buys = trade.select_filled_orders('buy') count_of_buys = trade.nr_of_successful_buys try: stake_amount = ((count_of_buys * self.dca_multiplier) + 1) * filled_buys[0].cost if stake_amount < min_stake: return min_stake else: return stake_amount except Exception as exception: return None return None def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = list() # Buy Condition 0 buy_indicator = self.buy_indicator0.value buy_crossed_indicator = self.buy_crossed_indicator0.value buy_operator = self.buy_operator0.value buy_real_num = self.buy_real_num0.value condition, dataframe = condition_generator( dataframe, buy_operator, buy_indicator, buy_crossed_indicator, buy_real_num ) conditions.append(condition) # Buy Condition 1 buy_indicator = self.buy_indicator1.value buy_crossed_indicator = self.buy_crossed_indicator1.value buy_operator = self.buy_operator1.value buy_real_num = self.buy_real_num1.value condition, dataframe = condition_generator( dataframe, buy_operator, buy_indicator, buy_crossed_indicator, buy_real_num ) conditions.append(condition) # Buy Condition 2 buy_indicator = self.buy_indicator2.value buy_crossed_indicator = self.buy_crossed_indicator2.value buy_operator = self.buy_operator2.value buy_real_num = self.buy_real_num2.value condition, dataframe = condition_generator( dataframe, buy_operator, buy_indicator, buy_crossed_indicator, buy_real_num ) conditions.append(condition) if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), 'buy']=1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = list() # Sell Condition 0 sell_indicator = self.sell_indicator0.value sell_crossed_indicator = self.sell_crossed_indicator0.value sell_operator = self.sell_operator0.value sell_real_num = self.sell_real_num0.value condition, dataframe = condition_generator( dataframe, sell_operator, sell_indicator, sell_crossed_indicator, sell_real_num ) conditions.append(condition) # Sell Condition 1 sell_indicator = self.sell_indicator1.value sell_crossed_indicator = self.sell_crossed_indicator1.value sell_operator = self.sell_operator1.value sell_real_num = self.sell_real_num1.value condition, dataframe = condition_generator( dataframe, sell_operator, sell_indicator, sell_crossed_indicator, sell_real_num ) conditions.append(condition) # Sell Condition 2 sell_indicator = self.sell_indicator2.value sell_crossed_indicator = self.sell_crossed_indicator2.value sell_operator = self.sell_operator2.value sell_real_num = self.sell_real_num2.value condition, dataframe = condition_generator( dataframe, sell_operator, sell_indicator, sell_crossed_indicator, sell_real_num ) conditions.append(condition) if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), 'sell']=1 return dataframe