Merge pull request #2253 from hroff-1902/backtesting-improve-logs

Improve logs for backtesting
This commit is contained in:
hroff-1902 2019-09-14 11:23:46 +03:00 committed by GitHub
commit 76e45883bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 91 deletions

View File

@ -239,14 +239,16 @@ class Backtesting:
stake_amount: float, max_open_trades: int) -> Optional[BacktestResult]: stake_amount: float, max_open_trades: int) -> Optional[BacktestResult]:
trade = Trade( trade = Trade(
pair=pair,
open_rate=buy_row.open, open_rate=buy_row.open,
open_date=buy_row.date, open_date=buy_row.date,
stake_amount=stake_amount, stake_amount=stake_amount,
amount=stake_amount / buy_row.open, amount=stake_amount / buy_row.open,
fee_open=self.fee, fee_open=self.fee,
fee_close=self.fee fee_close=self.fee,
is_open=True,
) )
logger.debug(f"{pair} - Backtesting emulates creation of new trade: {trade}.")
# calculate win/lose forwards from buy point # calculate win/lose forwards from buy point
for sell_row in partial_ticker: for sell_row in partial_ticker:
if max_open_trades > 0: if max_open_trades > 0:
@ -289,23 +291,25 @@ class Backtesting:
if partial_ticker: if partial_ticker:
# no sell condition found - trade stil open at end of backtest period # no sell condition found - trade stil open at end of backtest period
sell_row = partial_ticker[-1] sell_row = partial_ticker[-1]
btr = BacktestResult(pair=pair, bt_res = BacktestResult(pair=pair,
profit_percent=trade.calc_profit_percent(rate=sell_row.open), profit_percent=trade.calc_profit_percent(rate=sell_row.open),
profit_abs=trade.calc_profit(rate=sell_row.open), profit_abs=trade.calc_profit(rate=sell_row.open),
open_time=buy_row.date, open_time=buy_row.date,
close_time=sell_row.date, close_time=sell_row.date,
trade_duration=int(( trade_duration=int((
sell_row.date - buy_row.date).total_seconds() // 60), sell_row.date - buy_row.date).total_seconds() // 60),
open_index=buy_row.Index, open_index=buy_row.Index,
close_index=sell_row.Index, close_index=sell_row.Index,
open_at_end=True, open_at_end=True,
open_rate=buy_row.open, open_rate=buy_row.open,
close_rate=sell_row.open, close_rate=sell_row.open,
sell_reason=SellType.FORCE_SELL sell_reason=SellType.FORCE_SELL
) )
logger.debug('Force_selling still open trade %s with %s perc - %s', btr.pair, logger.debug(f"{pair} - Force selling still open trade, "
btr.profit_percent, btr.profit_abs) f"profit percent: {bt_res.profit_percent}, "
return btr f"profit abs: {bt_res.profit_abs}")
return bt_res
return None return None
def backtest(self, args: Dict) -> DataFrame: def backtest(self, args: Dict) -> DataFrame:
@ -384,6 +388,8 @@ class Backtesting:
max_open_trades) max_open_trades)
if trade_entry: if trade_entry:
logger.debug(f"{pair} - Locking pair till "
f"close_time={trade_entry.close_time}")
lock_pair_until[pair] = trade_entry.close_time lock_pair_until[pair] = trade_entry.close_time
trades.append(trade_entry) trades.append(trade_entry)
else: else:

View File

@ -1,7 +1,6 @@
""" """
This module contains the class to persist trades into SQLite This module contains the class to persist trades into SQLite
""" """
import logging import logging
from datetime import datetime from datetime import datetime
from decimal import Decimal from decimal import Decimal
@ -19,8 +18,10 @@ from sqlalchemy.pool import StaticPool
from freqtrade import OperationalException from freqtrade import OperationalException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_DECL_BASE: Any = declarative_base() _DECL_BASE: Any = declarative_base()
_SQL_DOCS_URL = 'http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls' _SQL_DOCS_URL = 'http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls'
@ -209,7 +210,7 @@ class Trade(_DECL_BASE):
ticker_interval = Column(Integer, nullable=True) ticker_interval = Column(Integer, nullable=True)
def __repr__(self): def __repr__(self):
open_since = arrow.get(self.open_date).humanize() if self.is_open else 'closed' open_since = self.open_date.strftime('%Y-%m-%d %H:%M:%S') if self.is_open else 'closed'
return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, ' return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
f'open_rate={self.open_rate:.8f}, open_since={open_since})') f'open_rate={self.open_rate:.8f}, open_since={open_since})')
@ -250,7 +251,6 @@ class Trade(_DECL_BASE):
:param initial: Called to initiate stop_loss. :param initial: Called to initiate stop_loss.
Skips everything if self.stop_loss is already set. Skips everything if self.stop_loss is already set.
""" """
if initial and not (self.stop_loss is None or self.stop_loss == 0): if initial and not (self.stop_loss is None or self.stop_loss == 0):
# Don't modify if called with initial and nothing to do # Don't modify if called with initial and nothing to do
return return
@ -259,7 +259,7 @@ class Trade(_DECL_BASE):
# no stop loss assigned yet # no stop loss assigned yet
if not self.stop_loss: if not self.stop_loss:
logger.debug("assigning new stop loss") logger.debug(f"{self.pair} - Assigning new stoploss...")
self.stop_loss = new_loss self.stop_loss = new_loss
self.stop_loss_pct = -1 * abs(stoploss) self.stop_loss_pct = -1 * abs(stoploss)
self.initial_stop_loss = new_loss self.initial_stop_loss = new_loss
@ -269,21 +269,20 @@ class Trade(_DECL_BASE):
# evaluate if the stop loss needs to be updated # evaluate if the stop loss needs to be updated
else: else:
if new_loss > self.stop_loss: # stop losses only walk up, never down! if new_loss > self.stop_loss: # stop losses only walk up, never down!
logger.debug(f"{self.pair} - Adjusting stoploss...")
self.stop_loss = new_loss self.stop_loss = new_loss
self.stop_loss_pct = -1 * abs(stoploss) self.stop_loss_pct = -1 * abs(stoploss)
self.stoploss_last_update = datetime.utcnow() self.stoploss_last_update = datetime.utcnow()
logger.debug("adjusted stop loss")
else: else:
logger.debug("keeping current stop loss") logger.debug(f"{self.pair} - Keeping current stoploss...")
logger.debug( logger.debug(
f"{self.pair} - current price {current_price:.8f}, " f"{self.pair} - Stoploss adjusted. current_price={current_price:.8f}, "
f"bought at {self.open_rate:.8f} and calculated " f"open_rate={self.open_rate:.8f}, max_rate={self.max_rate:.8f}, "
f"stop loss is at: {self.initial_stop_loss:.8f} initial " f"initial_stop_loss={self.initial_stop_loss:.8f}, "
f"stop at {self.stop_loss:.8f}. " f"stop_loss={self.stop_loss:.8f}. "
f"trailing stop loss saved us: " f"Trailing stoploss saved us: "
f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f} " f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.")
f"and max observed rate was {self.max_rate:.8f}")
def update(self, order: Dict) -> None: def update(self, order: Dict) -> None:
""" """
@ -331,24 +330,19 @@ class Trade(_DECL_BASE):
self self
) )
def calc_open_trade_price( def calc_open_trade_price(self, fee: Optional[float] = None) -> float:
self,
fee: Optional[float] = None) -> float:
""" """
Calculate the open_rate including fee. Calculate the open_rate including fee.
:param fee: fee to use on the open rate (optional). :param fee: fee to use on the open rate (optional).
If rate is not set self.fee will be used If rate is not set self.fee will be used
:return: Price in of the open trade incl. Fees :return: Price in of the open trade incl. Fees
""" """
buy_trade = (Decimal(self.amount) * Decimal(self.open_rate)) buy_trade = (Decimal(self.amount) * Decimal(self.open_rate))
fees = buy_trade * Decimal(fee or self.fee_open) fees = buy_trade * Decimal(fee or self.fee_open)
return float(buy_trade + fees) return float(buy_trade + fees)
def calc_close_trade_price( def calc_close_trade_price(self, rate: Optional[float] = None,
self, fee: Optional[float] = None) -> float:
rate: Optional[float] = None,
fee: Optional[float] = None) -> float:
""" """
Calculate the close_rate including fee Calculate the close_rate including fee
:param fee: fee to use on the close rate (optional). :param fee: fee to use on the close rate (optional).
@ -357,7 +351,6 @@ class Trade(_DECL_BASE):
If rate is not set self.close_rate will be used If rate is not set self.close_rate will be used
:return: Price in BTC of the open trade :return: Price in BTC of the open trade
""" """
if rate is None and not self.close_rate: if rate is None and not self.close_rate:
return 0.0 return 0.0
@ -365,10 +358,8 @@ class Trade(_DECL_BASE):
fees = sell_trade * Decimal(fee or self.fee_close) fees = sell_trade * Decimal(fee or self.fee_close)
return float(sell_trade - fees) return float(sell_trade - fees)
def calc_profit( def calc_profit(self, rate: Optional[float] = None,
self, fee: Optional[float] = None) -> float:
rate: Optional[float] = None,
fee: Optional[float] = None) -> float:
""" """
Calculate the absolute profit in stake currency between Close and Open trade Calculate the absolute profit in stake currency between Close and Open trade
:param fee: fee to use on the close rate (optional). :param fee: fee to use on the close rate (optional).
@ -385,10 +376,8 @@ class Trade(_DECL_BASE):
profit = close_trade_price - open_trade_price profit = close_trade_price - open_trade_price
return float(f"{profit:.8f}") return float(f"{profit:.8f}")
def calc_profit_percent( def calc_profit_percent(self, rate: Optional[float] = None,
self, fee: Optional[float] = None) -> float:
rate: Optional[float] = None,
fee: Optional[float] = None) -> float:
""" """
Calculates the profit in percentage (including fee). Calculates the profit in percentage (including fee).
:param rate: rate to compare with (optional). :param rate: rate to compare with (optional).
@ -396,7 +385,6 @@ class Trade(_DECL_BASE):
:param fee: fee to use on the close rate (optional). :param fee: fee to use on the close rate (optional).
:return: profit in percentage as float :return: profit in percentage as float
""" """
open_trade_price = self.calc_open_trade_price() open_trade_price = self.calc_open_trade_price()
close_trade_price = self.calc_close_trade_price( close_trade_price = self.calc_close_trade_price(
rate=(rate or self.close_rate), rate=(rate or self.close_rate),
@ -436,8 +424,8 @@ class Trade(_DECL_BASE):
and trade.initial_stop_loss_pct != desired_stoploss): and trade.initial_stop_loss_pct != desired_stoploss):
# Stoploss value got changed # Stoploss value got changed
logger.info(f"Stoploss for {trade} needs adjustment.") logger.info(f"Stoploss for {trade} needs adjustment...")
# Force reset of stoploss # Force reset of stoploss
trade.stop_loss = None trade.stop_loss = None
trade.adjust_stop_loss(trade.open_rate, desired_stoploss) trade.adjust_stop_loss(trade.open_rate, desired_stoploss)
logger.info(f"new stoploss: {trade.stop_loss}, ") logger.info(f"New stoploss: {trade.stop_loss}.")

View File

@ -202,7 +202,6 @@ class IStrategy(ABC):
:param metadata: Metadata dictionary with additional data (e.g. 'pair') :param metadata: Metadata dictionary with additional data (e.g. 'pair')
:return: DataFrame with ticker data and indicator data :return: DataFrame with ticker data and indicator data
""" """
pair = str(metadata.get('pair')) pair = str(metadata.get('pair'))
# Test if seen this pair and last candle before. # Test if seen this pair and last candle before.
@ -292,7 +291,6 @@ class IStrategy(ABC):
:param force_stoploss: Externally provided stoploss :param force_stoploss: Externally provided stoploss
:return: True if trade should be sold, False otherwise :return: True if trade should be sold, False otherwise
""" """
# Set current rate to low for backtesting sell # Set current rate to low for backtesting sell
current_rate = low or rate current_rate = low or rate
current_profit = trade.calc_profit_percent(current_rate) current_profit = trade.calc_profit_percent(current_rate)
@ -304,6 +302,8 @@ class IStrategy(ABC):
force_stoploss=force_stoploss, high=high) force_stoploss=force_stoploss, high=high)
if stoplossflag.sell_flag: if stoplossflag.sell_flag:
logger.debug(f"{trade.pair} - Stoploss hit. sell_flag=True, "
f"sell_type={stoplossflag.sell_type}")
return stoplossflag return stoplossflag
# Set current rate to high for backtesting sell # Set current rate to high for backtesting sell
@ -312,22 +312,31 @@ class IStrategy(ABC):
experimental = self.config.get('experimental', {}) experimental = self.config.get('experimental', {})
if buy and experimental.get('ignore_roi_if_buy_signal', False): if buy and experimental.get('ignore_roi_if_buy_signal', False):
logger.debug('Buy signal still active - not selling.') # This one is noisy, commented out
# logger.debug(f"{trade.pair} - Buy signal still active. sell_flag=False")
return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
# Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee) # Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date): if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
logger.debug('Required profit reached. Selling..') logger.debug(f"{trade.pair} - Required profit reached. sell_flag=True, "
f"sell_type=SellType.ROI")
return SellCheckTuple(sell_flag=True, sell_type=SellType.ROI) return SellCheckTuple(sell_flag=True, sell_type=SellType.ROI)
if experimental.get('sell_profit_only', False): if experimental.get('sell_profit_only', False):
logger.debug('Checking if trade is profitable..') # This one is noisy, commented out
# logger.debug(f"{trade.pair} - Checking if trade is profitable...")
if trade.calc_profit(rate=rate) <= 0: if trade.calc_profit(rate=rate) <= 0:
# This one is noisy, commented out
# logger.debug(f"{trade.pair} - Trade is not profitable. sell_flag=False")
return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
if sell and not buy and experimental.get('use_sell_signal', False): if sell and not buy and experimental.get('use_sell_signal', False):
logger.debug('Sell signal received. Selling..') logger.debug(f"{trade.pair} - Sell signal received. sell_flag=True, "
f"sell_type=SellType.SELL_SIGNAL")
return SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL) return SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL)
# This one is noisy, commented out...
# logger.debug(f"{trade.pair} - No sell signal. sell_flag=False")
return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
def stop_loss_reached(self, current_rate: float, trade: Trade, def stop_loss_reached(self, current_rate: float, trade: Trade,
@ -338,7 +347,6 @@ class IStrategy(ABC):
decides to sell or not decides to sell or not
:param current_profit: current profit in percent :param current_profit: current profit in percent
""" """
trailing_stop = self.config.get('trailing_stop', False) trailing_stop = self.config.get('trailing_stop', False)
stop_loss_value = force_stoploss if force_stoploss else self.stoploss stop_loss_value = force_stoploss if force_stoploss else self.stoploss
@ -359,7 +367,7 @@ class IStrategy(ABC):
if 'trailing_stop_positive' in self.config and high_profit > sl_offset: if 'trailing_stop_positive' in self.config and high_profit > sl_offset:
# Ignore mypy error check in configuration that this is a float # Ignore mypy error check in configuration that this is a float
stop_loss_value = self.config.get('trailing_stop_positive') # type: ignore stop_loss_value = self.config.get('trailing_stop_positive') # type: ignore
logger.debug(f"using positive stop loss: {stop_loss_value} " logger.debug(f"{trade.pair} - Using positive stoploss: {stop_loss_value} "
f"offset: {sl_offset:.4g} profit: {current_profit:.4f}%") f"offset: {sl_offset:.4g} profit: {current_profit:.4f}%")
trade.adjust_stop_loss(high or current_rate, stop_loss_value) trade.adjust_stop_loss(high or current_rate, stop_loss_value)
@ -369,20 +377,20 @@ class IStrategy(ABC):
(trade.stop_loss >= current_rate) and (trade.stop_loss >= current_rate) and
(not self.order_types.get('stoploss_on_exchange'))): (not self.order_types.get('stoploss_on_exchange'))):
selltype = SellType.STOP_LOSS sell_type = SellType.STOP_LOSS
# If initial stoploss is not the same as current one then it is trailing. # If initial stoploss is not the same as current one then it is trailing.
if trade.initial_stop_loss != trade.stop_loss: if trade.initial_stop_loss != trade.stop_loss:
selltype = SellType.TRAILING_STOP_LOSS sell_type = SellType.TRAILING_STOP_LOSS
logger.debug( logger.debug(
f"HIT STOP: current price at {current_rate:.6f}, " f"{trade.pair} - HIT STOP: current price at {current_rate:.6f}, "
f"stop loss is {trade.stop_loss:.6f}, " f"stoploss is {trade.stop_loss:.6f}, "
f"initial stop loss was at {trade.initial_stop_loss:.6f}, " f"initial stoploss was at {trade.initial_stop_loss:.6f}, "
f"trade opened at {trade.open_rate:.6f}") f"trade opened at {trade.open_rate:.6f}")
logger.debug(f"trailing stop saved {trade.stop_loss - trade.initial_stop_loss:.6f}") logger.debug(f"{trade.pair} - Trailing stop saved "
f"{trade.stop_loss - trade.initial_stop_loss:.6f}")
logger.debug('Stop loss hit.') return SellCheckTuple(sell_flag=True, sell_type=sell_type)
return SellCheckTuple(sell_flag=True, sell_type=selltype)
return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)

View File

@ -1590,6 +1590,8 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No
Trade.session = MagicMock() Trade.session = MagicMock()
trade.open_order_id = '123' trade.open_order_id = '123'
trade.open_fee = 0.001 trade.open_fee = 0.001
# Add datetime explicitly since sqlalchemy defaults apply only once written to database
trade.open_date = arrow.utcnow().datetime
freqtrade.update_trade_state(trade) freqtrade.update_trade_state(trade)
# Test amount not modified by fee-logic # Test amount not modified by fee-logic
assert not log_has_re(r'Applying fee to .*', caplog) assert not log_has_re(r'Applying fee to .*', caplog)
@ -1823,7 +1825,8 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
# if ROI is reached we must sell # if ROI is reached we must sell
patch_get_signal(freqtrade, value=(False, True)) patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) assert freqtrade.handle_trade(trade)
assert log_has('Required profit reached. Selling..', caplog) assert log_has("ETH/BTC - Required profit reached. sell_flag=True, sell_type=SellType.ROI",
caplog)
def test_handle_trade_experimental( def test_handle_trade_experimental(
@ -1853,7 +1856,8 @@ def test_handle_trade_experimental(
patch_get_signal(freqtrade, value=(False, True)) patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) assert freqtrade.handle_trade(trade)
assert log_has('Sell signal received. Selling..', caplog) assert log_has("ETH/BTC - Sell signal received. sell_flag=True, sell_type=SellType.SELL_SIGNAL",
caplog)
def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
@ -2132,6 +2136,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
) )
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
open_date = arrow.utcnow().shift(minutes=-601)
trade_buy = Trade( trade_buy = Trade(
pair='ETH/BTC', pair='ETH/BTC',
open_rate=0.00001099, open_rate=0.00001099,
@ -2141,16 +2146,18 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
fee_open=0.0, fee_open=0.0,
fee_close=0.0, fee_close=0.0,
stake_amount=1, stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime, open_date=open_date.datetime,
is_open=True is_open=True
) )
Trade.session.add(trade_buy) Trade.session.add(trade_buy)
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
assert log_has_re(r'Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, ' assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, "
r'open_rate=0.00001099, open_since=10 hours ago\) due to Traceback \(most ' r"open_rate=0.00001099, open_since="
r'recent call last\):\n.*', caplog) f"{open_date.strftime('%Y-%m-%d %H:%M:%S')}"
r"\) due to Traceback \(most recent call last\):\n*",
caplog)
def test_handle_timedout_limit_buy(mocker, default_conf) -> None: def test_handle_timedout_limit_buy(mocker, default_conf) -> None:
@ -2793,8 +2800,9 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog,
# Sell as trailing-stop is reached # Sell as trailing-stop is reached
assert freqtrade.handle_trade(trade) is True assert freqtrade.handle_trade(trade) is True
assert log_has( assert log_has(
f'HIT STOP: current price at 0.000012, stop loss is 0.000015, ' f"ETH/BTC - HIT STOP: current price at 0.000012, "
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog) f"stoploss is 0.000015, "
f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
@ -2836,8 +2844,8 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
})) }))
# stop-loss not reached, adjusted stoploss # stop-loss not reached, adjusted stoploss
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert log_has(f'using positive stop loss: 0.01 offset: 0 profit: 0.2666%', caplog) assert log_has(f"ETH/BTC - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog)
assert log_has(f'adjusted stop loss', caplog) assert log_has(f"ETH/BTC - Adjusting stoploss...", caplog)
assert trade.stop_loss == 0.0000138501 assert trade.stop_loss == 0.0000138501
mocker.patch('freqtrade.exchange.Exchange.get_ticker', mocker.patch('freqtrade.exchange.Exchange.get_ticker',
@ -2849,9 +2857,9 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
# Lower price again (but still positive) # Lower price again (but still positive)
assert freqtrade.handle_trade(trade) is True assert freqtrade.handle_trade(trade) is True
assert log_has( assert log_has(
f'HIT STOP: current price at {buy_price + 0.000002:.6f}, ' f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
f'stop loss is {trade.stop_loss:.6f}, ' f"stoploss is {trade.stop_loss:.6f}, "
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog) f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
@ -2894,8 +2902,9 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
})) }))
# stop-loss not reached, adjusted stoploss # stop-loss not reached, adjusted stoploss
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert log_has(f'using positive stop loss: 0.01 offset: 0.011 profit: 0.2666%', caplog) assert log_has(f"ETH/BTC - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%",
assert log_has(f'adjusted stop loss', caplog) caplog)
assert log_has(f"ETH/BTC - Adjusting stoploss...", caplog)
assert trade.stop_loss == 0.0000138501 assert trade.stop_loss == 0.0000138501
mocker.patch('freqtrade.exchange.Exchange.get_ticker', mocker.patch('freqtrade.exchange.Exchange.get_ticker',
@ -2907,9 +2916,9 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
# Lower price again (but still positive) # Lower price again (but still positive)
assert freqtrade.handle_trade(trade) is True assert freqtrade.handle_trade(trade) is True
assert log_has( assert log_has(
f'HIT STOP: current price at {buy_price + 0.000002:.6f}, ' f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
f'stop loss is {trade.stop_loss:.6f}, ' f"stoploss is {trade.stop_loss:.6f}, "
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog) f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
@ -2960,7 +2969,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
# stop-loss should not be adjusted as offset is not reached yet # stop-loss should not be adjusted as offset is not reached yet
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert not log_has(f'adjusted stop loss', caplog) assert not log_has(f"ETH/BTC - Adjusting stoploss...", caplog)
assert trade.stop_loss == 0.0000098910 assert trade.stop_loss == 0.0000098910
# price rises above the offset (rises 12% when the offset is 5.5%) # price rises above the offset (rises 12% when the offset is 5.5%)
@ -2972,8 +2981,9 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
})) }))
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert log_has(f'using positive stop loss: 0.05 offset: 0.055 profit: 0.1218%', caplog) assert log_has(f"ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%",
assert log_has(f'adjusted stop loss', caplog) caplog)
assert log_has(f"ETH/BTC - Adjusting stoploss...", caplog)
assert trade.stop_loss == 0.0000117705 assert trade.stop_loss == 0.0000117705
@ -3342,8 +3352,8 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets)
default_conf['telegram']['enabled'] = False default_conf['telegram']['enabled'] = False
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
# ordrebook shall be used even if tickers would be lower. # orderbook shall be used even if tickers would be lower.
assert freqtrade.get_target_bid('ETH/BTC', ) != 0.042 assert freqtrade.get_target_bid('ETH/BTC') != 0.042
assert ticker_mock.call_count == 0 assert ticker_mock.call_count == 0