''' 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. ''' ''' docker-compose run --rm freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell --strategy GuruSkippyasurmuni -e 2000 --timerange=20220314- --eps ''' ''' $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- ''' 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 from random import shuffle import logging god_genes = set() 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) 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_strategy_4(IStrategy): INTERFACE_VERSION = 2 DATESTAMP = 0 COUNT = 0 custom_info = { } 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.90, "sell_real_num2": 1.0, } minimal_roi = { "0": 1000.0 } stoploss = -0.35 trailing_stop = False trailing_stop_positive = 0.001 trailing_stop_positive_offset = 0.01 trailing_only_offset_is_reached = True use_sell_signal = True exit_profit_only = True ignore_roi_if_entry_signal = False timeframe = '5m' process_only_new_candles = True position_adjustment_enable = True max_entry_position_adjustment = 4 dca_multiplier = 0.5 @property def protections(self): return [ { "method": "CooldownPeriod", "stop_duration_candles": 12 } ] 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: pair = metadata['pair'] if not pair in self.custom_info: 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_time: datetime, **kwargs) -> bool: if sell_reason == 'sell_signal' and trade.calc_profit_ratio(rate) < 0.055: 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: 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 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']): self.custom_info[trade.pair][self.DATESTAMP] = last_candle['date'] if current_profit > -0.065: return None 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_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = list() 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_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_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_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = list() 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_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_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