Add an example how to use custom_info[trade.open_date] in custom_stoploss()

by implementing a fixed risk/reward ratio.
This commit is contained in:
Joe Schr
2021-03-15 20:35:14 +01:00
parent c94b2c588c
commit b8475b46da
@@ -0,0 +1,123 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# isort: skip_file
# --- Do not remove these libs ---
import numpy as np # noqa
import pandas as pd # noqa
from pandas import DataFrame
from freqtrade.strategy.interface import IStrategy
# --------------------------------
# Add your lib to import here
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from datetime import datetime
from freqtrade.persistence import Trade
from freqtrade.state import RunMode
import logging
logger = logging.getLogger(__name__)
class FixedRiskRewardLoss(IStrategy):
"""
This strategy uses custom_stoploss() to enforce a fixed risk/reward ratio
by first calculating a dynamic initial stoploss via ATR - last negative peak
After that, we caculate that initial risk and multiply it with an risk_reward_ratio
Once this is reached, stoploss is set to it and sell signal is enabled
Also there is a break even ratio. Once this is reached, the stoploss is adjusted to minimize
losses by setting it to the buy rate + fees.
"""
custom_info = {
'risk_reward_ratio': 3.5,
'set_to_break_even_at_profit': 1,
}
use_custom_stoploss = True
stoploss = -0.9
sell_profit_only = True
sell_profit_offset = 1 # 100%, get's set dynamically in trail
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
"""
custom_stoploss using a risk/reward ratio
"""
result = break_even_sl = takeprofit_sl = -1
custom_info_pair = self.custom_info[pair]
if custom_info_pair is not None:
# using current_time/open_date directly via custom_info_pair[trade.open_daten]
# would only work in backtesting/hyperopt.
# in live/dry-run, we have to search for nearest row before it
timezone = custom_info_pair.index.tz
open_date = trade.open_date.replace(tzinfo=timezone)
open_date_mask = custom_info_pair.index.unique().get_loc(open_date, method='ffill')
open_df = custom_info_pair.iloc[open_date_mask]
initial_sl_abs = open_df['stoploss_rate']
# calculate initial stoploss at open_date
initial_sl = initial_sl_abs/current_rate-1
# calculate take profit treshold
# by using the initial risk and multiplying it
risk_distance = trade.open_rate-initial_sl_abs
reward_distance = risk_distance*self.custom_info['risk_reward_ratio']
# take_profit tries to lock in profit once price gets over
# risk/reward ratio treshold
take_profit_price_abs = trade.open_rate+reward_distance
# take_profit gets triggerd at this profit
take_profit_pct = take_profit_price_abs/trade.open_rate-1
# break_even tries to set sl at open_rate+fees (0 loss)
break_even_profit_distance = risk_distance*self.custom_info['set_to_break_even_at_profit']
# break_even gets triggerd at this profit
break_even_profit_pct = (break_even_profit_distance+current_rate)/current_rate-1
result = initial_sl
if(current_profit >= break_even_profit_pct):
break_even_sl = (trade.open_rate*(1+trade.fee_open+trade.fee_close) / current_rate)-1
result = break_even_sl
if(current_profit >= take_profit_pct):
takeprofit_sl = take_profit_price_abs/current_rate-1
result = takeprofit_sl
# enable sell signal only after take_profit treshold is reached
self.sell_profit_offset = take_profit_pct
return result
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['min'] = dataframe['low'].rolling(48).min()
dataframe['atr'] = ta.ATR(dataframe)
dataframe['stoploss_rate'] = dataframe['min']-(dataframe['atr'])
self.custom_info[metadata['pair']] = dataframe[['date', 'stoploss_rate']].copy().set_index('date')
# all "normal" indicators:
# e.g.
# dataframe['rsi'] = ta.RSI(dataframe)
return dataframe
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Placeholder Strategy: buys when SAR is smaller then candle before
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
# Allways buys
dataframe.loc[:, 'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Placeholder Strategy: does nothing
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
# Always sells
dataframe.loc[:, 'sell'] = 1
return dataframe