def test_stoploss_from_open(): open_price_ranges = [ [0.01, 1.00, 30], [1, 100, 30], [100, 10000, 30], ] current_profit_range = [-0.99, 2, 30] desired_stop_range = [-0.50, 0.50, 30] for open_range in open_price_ranges: for open_price in np.linspace(*open_range): for desired_stop in np.linspace(*desired_stop_range): # -1 is not a valid current_profit, should return 1 assert stoploss_from_open(desired_stop, -1) == 1 for current_profit in np.linspace(*current_profit_range): current_price = open_price * (1 + current_profit) expected_stop_price = open_price * (1 + desired_stop) stoploss = stoploss_from_open(desired_stop, current_profit) assert stoploss >= 0 assert stoploss <= 1 stop_price = current_price * (1 - stoploss) # there is no correct answer if the expected stop price is above # the current price if expected_stop_price > current_price: assert stoploss == 0 else: assert isclose(stop_price, expected_stop_price, rel_tol=0.00001)
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: # hard stoploss profit HSL = self.pHSL.value PF_1 = self.pPF_1.value SL_1 = self.pSL_1.value PF_2 = self.pPF_2.value SL_2 = self.pSL_2.value # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. if (current_profit > PF_2): sl_profit = SL_2 + (current_profit - PF_2) elif (current_profit > PF_1): sl_profit = SL_1 + ((current_profit - PF_1)*(SL_2 - SL_1)/(PF_2 - PF_1)) else: sl_profit = HSL if (current_profit > PF_1): return stoploss_from_open(sl_profit, current_profit) else: return stoploss_from_open(HSL, current_profit) return stoploss_from_open(HSL, current_profit)
def test_stoploss_from_open(): open_price_ranges = [ [0.01, 1.00, 30], [1, 100, 30], [100, 10000, 30], ] # profit range for long is [-1, inf] while for shorts is [-inf, 1] current_profit_range_dict = {'long': [-0.99, 2, 30], 'short': [-2.0, 0.99, 30]} desired_stop_range = [-0.50, 0.50, 30] for side, current_profit_range in current_profit_range_dict.items(): for open_range in open_price_ranges: for open_price in np.linspace(*open_range): for desired_stop in np.linspace(*desired_stop_range): if side == 'long': # -1 is not a valid current_profit, should return 1 assert stoploss_from_open(desired_stop, -1) == 1 else: # 1 is not a valid current_profit for shorts, should return 1 assert stoploss_from_open(desired_stop, 1, True) == 1 for current_profit in np.linspace(*current_profit_range): if side == 'long': current_price = open_price * (1 + current_profit) expected_stop_price = open_price * (1 + desired_stop) stoploss = stoploss_from_open(desired_stop, current_profit) stop_price = current_price * (1 - stoploss) else: current_price = open_price * (1 - current_profit) expected_stop_price = open_price * (1 - desired_stop) stoploss = stoploss_from_open(desired_stop, current_profit, True) stop_price = current_price * (1 + stoploss) assert stoploss >= 0 # Technically the formula can yield values greater than 1 for shorts # eventhough it doesn't make sense because the position would be liquidated if side == 'long': assert stoploss <= 1 # there is no correct answer if the expected stop price is above # the current price if ((side == 'long' and expected_stop_price > current_price) or (side == 'short' and expected_stop_price < current_price)): assert stoploss == 0 else: assert isclose(stop_price, expected_stop_price, rel_tol=0.00001)
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: if current_profit > 0.90: return stoploss_from_open(0.85, current_profit) elif current_profit > 0.80: return stoploss_from_open(0.72, current_profit) elif current_profit > 0.70: return stoploss_from_open(0.62, current_profit) elif current_profit > 0.60: return stoploss_from_open(0.52, current_profit) elif current_profit > 0.50: return stoploss_from_open(0.42, current_profit) elif current_profit > 0.40: return stoploss_from_open(0.32, current_profit) elif current_profit > 0.30: return stoploss_from_open(0.22, current_profit) elif current_profit > 0.20: return stoploss_from_open(0.12, current_profit) elif current_profit > 0.10: return stoploss_from_open(0.05, current_profit) return 1
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: result = 1 custom_info_pair = self.custom_info[pair] if custom_info_pair is not None: # using current_time/open_date directly will only work in backtesting/hyperopt. # in live / dry-run, we have to search for nearest row before tz = custom_info_pair.index.tz open_date = trade.open_date_utc if hasattr( trade, 'open_date_utc') else trade.open_date.replace( tzinfo=custom_info_pair.index.tz) open_date_mask = custom_info_pair.index.unique().get_loc( open_date, method='ffill') open_df = custom_info_pair.iloc[open_date_mask] # trade might be open too long for us to find opening candle if (open_df is None or len(open_df) == 0): logger.debug("No open_df :(") return 1 # oh well # stop out if we have reached our take profit limit take_profit = open_df['take_profit'] if (take_profit is not None): if current_rate > take_profit: logger.debug("take_profit={}, current={}".format( take_profit, current_rate)) return 0.001 # keep trailing stoploss at -stop_pct from the open price stop_pct = open_df['stop_pct'] if (stop_pct is not None): new_stop = stoploss_from_open(-stop_pct, current_profit) logger.debug( "open={}, current={}, profit={}, stop_pct={}, stop={}". format(current_rate / (1 + current_profit), current_rate, current_profit, stop_pct, current_rate * (1 - new_stop))) if new_stop > 0: result = new_stop return result
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: params = self.custom_stop trade_dur = int( (current_time.timestamp() - trade.open_date_utc.timestamp()) // 60) min_profit = trade.calc_profit_ratio(trade.min_rate) max_profit = trade.calc_profit_ratio(trade.max_rate) profit_diff = current_profit - min_profit current_stoploss = trade.stop_loss decay_stoploss = cta.linear_growth(params['decay-start'], params['decay-end'], params['decay-delay'], params['decay-time'], trade_dur) # enable stoploss in positive profits after threshold to trail as specifed distance if params['pos-trail'] == True: if current_profit > params['pos-threshold']: return current_profit - params['pos-trail-dist'] if self.config['runmode'].value in ('live', 'dry_run'): dataframe, last_updated = self.dp.get_analyzed_dataframe( pair=pair, timeframe=self.timeframe) roc = dataframe['roc'].iat[-1] atr = dataframe['atr'].iat[-1] rmi_slow = dataframe['rmi-slow'].iat[-1] # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!) else: roc = self.custom_trade_info[ trade.pair]['roc'].loc[current_time]['roc'] atr = self.custom_trade_info[ trade.pair]['atr'].loc[current_time]['atr'] rmi_slow = self.custom_trade_info[ trade.pair]['rmi-slow'].loc[current_time]['rmi-slow'] if current_profit < params['cur-threshold']: # Dynamic bailout based on rate of change if (roc / 100) <= params['roc-bail']: if params['bail-how'] == 'atr': return ((current_rate - atr) / current_rate) - 1 elif params['bail-how'] == 'immediate': return 0.001 else: return min( max(stoploss_from_open(decay_stoploss, current_profit), 0.001), current_stoploss) # if we might be on a rebound, move the stoploss to the low point or keep it where it was if (current_profit > min_profit) or roc > 0 or rmi_slow >= params['rmi-trend']: if profit_diff > params['cur-min-diff'] and current_profit < 0: return stoploss_from_open(min_profit, current_profit) return 1 if current_profit < 0: return min( max(stoploss_from_open(decay_stoploss, current_profit), 0.001), current_stoploss) return 1
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: is_bull_market = True if self.custom_info and self.MARKET_CAP_REFERENCE_PAIR in self.custom_info and trade: # using current_time directly (like below) will only work in backtesting. # so check "runmode" to make sure that it's only used in backtesting/hyperopt if self.dp and self.dp.runmode.value in ('backtest', 'hyperopt'): sar = self.custom_info[self.MARKET_CAP_REFERENCE_PAIR][ 'sar_1w'].loc[current_time]['sar_1w'] tema = self.custom_info[self.MARKET_CAP_REFERENCE_PAIR][ 'tema_1w'].loc[current_time]['tema_1w'] # in live / dry-run, it'll be really the current time else: # but we can just use the last entry from an already analyzed dataframe instead dataframe, last_updated = self.dp.get_analyzed_dataframe( pair=self.MARKET_CAP_REFERENCE_PAIR, timeframe=self.timeframe) # WARNING # only use .iat[-1] in live mode, not in backtesting/hyperopt # otherwise you will look into the future # see: https://www.freqtrade.io/en/latest/strategy-customization/#common-mistakes-when-developing-strategies sar = dataframe['sar_1w'].iat[-1] tema = dataframe['tema_1w'].iat[-1] if tema is not None and sar is not None: is_bull_market = tema > sar # evaluate highest to lowest, so that highest possible stop is used if current_profit > 5.00: return -0.2 elif current_profit > 4.00: return stoploss_from_open(3.50, current_profit) elif current_profit > 3.50: return stoploss_from_open(3.00, current_profit) elif current_profit > 3.00: return stoploss_from_open(2.50, current_profit) elif current_profit > 2.50: return stoploss_from_open(2.00, current_profit) elif current_profit > 2.00: return stoploss_from_open(1.50, current_profit) elif current_profit > 1.50: return stoploss_from_open(1.25, current_profit) elif current_profit > 1.25: return stoploss_from_open(1.00, current_profit) elif current_profit > 1.00: return stoploss_from_open(0.75, current_profit) elif current_profit > 0.75: return stoploss_from_open(0.50, current_profit) elif current_profit > 0.50 and is_bull_market: return stoploss_from_open(0.25, current_profit) elif current_profit > 0.25 and is_bull_market: return stoploss_from_open(0.05, current_profit) elif -0.05 < current_profit < 0.05 and not is_bull_market: if current_time - timedelta(hours=24 * 7) > trade.open_date_utc: return -0.0125 elif current_time - timedelta(hours=24 * 5) > trade.open_date_utc: return -0.025 elif current_time - timedelta(hours=24 * 3) > trade.open_date_utc: return -0.05 # return maximum stoploss value, keeping current stoploss price unchanged return -1