Add entry_tag to "entry" callbacks

This commit is contained in:
Matthias 2022-01-23 19:32:38 +01:00
parent 6d91ceb28c
commit e252830229
5 changed files with 31 additions and 18 deletions

View File

@ -362,8 +362,8 @@ class AwesomeStrategy(IStrategy):
# ... populate_* methods # ... populate_* methods
def custom_entry_price(self, pair: str, current_time: datetime, def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
proposed_rate, **kwargs) -> float: entry_tag: Optional[str], **kwargs) -> float:
dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair,
timeframe=self.timeframe) timeframe=self.timeframe)
@ -502,7 +502,8 @@ class AwesomeStrategy(IStrategy):
# ... populate_* methods # ... populate_* methods
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, **kwargs) -> bool: time_in_force: str, current_time: datetime, entry_tag: Optional[str],
**kwargs) -> bool:
""" """
Called right before placing a buy order. Called right before placing a buy order.
Timing for this function is critical, so avoid doing heavy computations or Timing for this function is critical, so avoid doing heavy computations or
@ -620,7 +621,7 @@ class DigDeeperStrategy(IStrategy):
# This is called when placing the initial order (opening trade) # This is called when placing the initial order (opening trade)
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float, proposed_stake: float, min_stake: float, max_stake: float,
**kwargs) -> float: entry_tag: Optional[str], **kwargs) -> float:
# We need to leave most of the funds for possible further DCA orders # We need to leave most of the funds for possible further DCA orders
# This also applies to fixed stakes # This also applies to fixed stakes

View File

@ -531,7 +531,7 @@ class FreqtradeBot(LoggingMixin):
pos_adjust = trade is not None pos_adjust = trade is not None
enter_limit_requested, stake_amount = self.get_valid_enter_price_and_stake( enter_limit_requested, stake_amount = self.get_valid_enter_price_and_stake(
pair, price, stake_amount, trade) pair, price, stake_amount, buy_tag, trade)
if not stake_amount: if not stake_amount:
return False return False
@ -550,7 +550,8 @@ class FreqtradeBot(LoggingMixin):
if not pos_adjust and not strategy_safe_wrapper( if not pos_adjust and not strategy_safe_wrapper(
self.strategy.confirm_trade_entry, default_retval=True)( self.strategy.confirm_trade_entry, default_retval=True)(
pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested, pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested,
time_in_force=time_in_force, current_time=datetime.now(timezone.utc)): time_in_force=time_in_force, current_time=datetime.now(timezone.utc),
entry_tag=buy_tag):
logger.info(f"User requested abortion of buying {pair}") logger.info(f"User requested abortion of buying {pair}")
return False return False
amount = self.exchange.amount_to_precision(pair, amount) amount = self.exchange.amount_to_precision(pair, amount)
@ -660,6 +661,7 @@ class FreqtradeBot(LoggingMixin):
def get_valid_enter_price_and_stake( def get_valid_enter_price_and_stake(
self, pair: str, price: Optional[float], stake_amount: float, self, pair: str, price: Optional[float], stake_amount: float,
entry_tag: Optional[str],
trade: Optional[Trade]) -> Tuple[float, float]: trade: Optional[Trade]) -> Tuple[float, float]:
if price: if price:
enter_limit_requested = price enter_limit_requested = price
@ -669,7 +671,7 @@ class FreqtradeBot(LoggingMixin):
custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price,
default_retval=proposed_enter_rate)( default_retval=proposed_enter_rate)(
pair=pair, current_time=datetime.now(timezone.utc), pair=pair, current_time=datetime.now(timezone.utc),
proposed_rate=proposed_enter_rate) proposed_rate=proposed_enter_rate, entry_tag=entry_tag)
enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate) enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate)
if not enter_limit_requested: if not enter_limit_requested:
@ -682,7 +684,7 @@ class FreqtradeBot(LoggingMixin):
default_retval=stake_amount)( default_retval=stake_amount)(
pair=pair, current_time=datetime.now(timezone.utc), pair=pair, current_time=datetime.now(timezone.utc),
current_rate=enter_limit_requested, proposed_stake=stake_amount, current_rate=enter_limit_requested, proposed_stake=stake_amount,
min_stake=min_stake_amount, max_stake=max_stake_amount) min_stake=min_stake_amount, max_stake=max_stake_amount, entry_tag=entry_tag)
stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount) stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount)
return enter_limit_requested, stake_amount return enter_limit_requested, stake_amount

View File

@ -462,12 +462,14 @@ class Backtesting:
def _enter_trade(self, pair: str, row: Tuple, stake_amount: Optional[float] = None, def _enter_trade(self, pair: str, row: Tuple, stake_amount: Optional[float] = None,
trade: Optional[LocalTrade] = None) -> Optional[LocalTrade]: trade: Optional[LocalTrade] = None) -> Optional[LocalTrade]:
current_time = row[DATE_IDX].to_pydatetime() current_time = row[DATE_IDX].to_pydatetime()
entry_tag = row[BUY_TAG_IDX] if len(row) >= BUY_TAG_IDX + 1 else None
# let's call the custom entry price, using the open price as default price # let's call the custom entry price, using the open price as default price
propose_rate = strategy_safe_wrapper(self.strategy.custom_entry_price, propose_rate = strategy_safe_wrapper(self.strategy.custom_entry_price,
default_retval=row[OPEN_IDX])( default_retval=row[OPEN_IDX])(
pair=pair, current_time=current_time, pair=pair, current_time=current_time,
proposed_rate=row[OPEN_IDX]) # default value is the open rate proposed_rate=row[OPEN_IDX], entry_tag=entry_tag) # default value is the open rate
# Move rate to within the candle's low/high rate # Move rate to within the candle's low/high rate
propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX]) propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX])
@ -485,7 +487,8 @@ class Backtesting:
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
default_retval=stake_amount)( default_retval=stake_amount)(
pair=pair, current_time=current_time, current_rate=propose_rate, pair=pair, current_time=current_time, current_rate=propose_rate,
proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount,
entry_tag=entry_tag)
stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount) stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount)
@ -500,14 +503,14 @@ class Backtesting:
if not pos_adjust: if not pos_adjust:
if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)( if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)(
pair=pair, order_type=order_type, amount=stake_amount, rate=propose_rate, pair=pair, order_type=order_type, amount=stake_amount, rate=propose_rate,
time_in_force=time_in_force, current_time=current_time): time_in_force=time_in_force, current_time=current_time,
entry_tag=entry_tag):
return None return None
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount): if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
amount = round(stake_amount / propose_rate, 8) amount = round(stake_amount / propose_rate, 8)
if trade is None: if trade is None:
# Enter trade # Enter trade
has_buy_tag = len(row) >= BUY_TAG_IDX + 1
trade = LocalTrade( trade = LocalTrade(
pair=pair, pair=pair,
open_rate=propose_rate, open_rate=propose_rate,
@ -517,7 +520,7 @@ class Backtesting:
fee_open=self.fee, fee_open=self.fee,
fee_close=self.fee, fee_close=self.fee,
is_open=True, is_open=True,
buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None, buy_tag=entry_tag,
exchange='backtesting', exchange='backtesting',
orders=[] orders=[]
) )

View File

@ -238,7 +238,8 @@ class IStrategy(ABC, HyperStrategyMixin):
return False return False
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, **kwargs) -> bool: time_in_force: str, current_time: datetime, entry_tag: Optional[str],
**kwargs) -> bool:
""" """
Called right before placing a buy order. Called right before placing a buy order.
Timing for this function is critical, so avoid doing heavy computations or Timing for this function is critical, so avoid doing heavy computations or
@ -254,6 +255,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param rate: Rate that's going to be used when using limit orders :param rate: Rate that's going to be used when using limit orders
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return bool: When True is returned, then the buy-order is placed on the exchange. :return bool: When True is returned, then the buy-order is placed on the exchange.
False aborts the process False aborts the process
@ -311,7 +313,7 @@ class IStrategy(ABC, HyperStrategyMixin):
return self.stoploss return self.stoploss
def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
**kwargs) -> float: entry_tag: Optional[str], **kwargs) -> float:
""" """
Custom entry price logic, returning the new entry price. Custom entry price logic, returning the new entry price.
@ -322,6 +324,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param pair: Pair that's currently analyzed :param pair: Pair that's currently analyzed
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param proposed_rate: Rate, calculated based on pricing settings in ask_strategy. :param proposed_rate: Rate, calculated based on pricing settings in ask_strategy.
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New entry price value if provided :return float: New entry price value if provided
""" """
@ -373,7 +376,7 @@ class IStrategy(ABC, HyperStrategyMixin):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float, proposed_stake: float, min_stake: float, max_stake: float,
**kwargs) -> float: entry_tag: Optional[str], **kwargs) -> float:
""" """
Customize stake size for each new trade. This method is not called when edge module is Customize stake size for each new trade. This method is not called when edge module is
enabled. enabled.
@ -384,6 +387,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param proposed_stake: A stake amount proposed by the bot. :param proposed_stake: A stake amount proposed by the bot.
:param min_stake: Minimal stake size allowed by exchange. :param min_stake: Minimal stake size allowed by exchange.
:param max_stake: Balance available for trading. :param max_stake: Balance available for trading.
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:return: A stake size, which is between min_stake and max_stake. :return: A stake size, which is between min_stake and max_stake.
""" """
return proposed_stake return proposed_stake

View File

@ -14,7 +14,7 @@ def bot_loop_start(self, **kwargs) -> None:
def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float, def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float, proposed_stake: float, min_stake: float, max_stake: float,
**kwargs) -> float: entry_tag: Optional[str], **kwargs) -> float:
""" """
Customize stake size for each new trade. This method is not called when edge module is Customize stake size for each new trade. This method is not called when edge module is
enabled. enabled.
@ -25,6 +25,7 @@ def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate:
:param proposed_stake: A stake amount proposed by the bot. :param proposed_stake: A stake amount proposed by the bot.
:param min_stake: Minimal stake size allowed by exchange. :param min_stake: Minimal stake size allowed by exchange.
:param max_stake: Balance available for trading. :param max_stake: Balance available for trading.
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:return: A stake size, which is between min_stake and max_stake. :return: A stake size, which is between min_stake and max_stake.
""" """
return proposed_stake return proposed_stake
@ -78,7 +79,8 @@ def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', curre
return None return None
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: 'datetime', **kwargs) -> bool: time_in_force: str, current_time: 'datetime', entry_tag: Optional[str],
**kwargs) -> bool:
""" """
Called right before placing a buy order. Called right before placing a buy order.
Timing for this function is critical, so avoid doing heavy computations or Timing for this function is critical, so avoid doing heavy computations or
@ -94,6 +96,7 @@ def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: f
:param rate: Rate that's going to be used when using limit orders :param rate: Rate that's going to be used when using limit orders
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return bool: When True is returned, then the buy-order is placed on the exchange. :return bool: When True is returned, then the buy-order is placed on the exchange.
False aborts the process False aborts the process