updated tests

This commit is contained in:
மனோஜ்குமார் பழனிச்சாமி 2022-04-04 19:14:52 +05:30
parent b646d8ba0e
commit 606e0f1b68
17 changed files with 141 additions and 10499 deletions

View File

@ -13,6 +13,7 @@ class ExitType(Enum):
FORCE_SELL = "force_sell"
EMERGENCY_SELL = "emergency_sell"
CUSTOM_SELL = "custom_sell"
PARTIAL_SELL = "partial_sell"
NONE = ""
def __str__(self):

View File

@ -1428,8 +1428,9 @@ class Exchange:
except ccxt.BaseError as e:
raise OperationalException(e) from e
def get_rate(self, pair: str, refresh: bool,
side: Literal['entry', 'exit'], is_short: bool, order_book: Optional[dict] = None, ticker: Optional[dict] = Non) -> float:
def get_rate(self, pair: str, refresh: bool, # noqa: max-complexity: 13
side: Literal['entry', 'exit'], is_short: bool,
order_book: Optional[dict] = None, ticker: Optional[dict] = None) -> float:
"""
Calculates bid/ask target
bid rate - between current ask price and last price
@ -1533,7 +1534,8 @@ class Exchange:
if not entry_rate:
entry_rate = self.get_rate(pair, refresh, 'entry', is_short, ticker=ticker)
if not exit_rate:
exit_rate = self.get_rate(pair, refresh, 'exit', order_book=order_book, ticker=ticker)
exit_rate = self.get_rate(pair, refresh, 'exit',
is_short, order_book=order_book, ticker=ticker)
return entry_rate, exit_rate
# Fee handling

File diff suppressed because it is too large Load Diff

View File

@ -459,7 +459,6 @@ class FreqtradeBot(LoggingMixin):
if signal:
stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge)
bid_check_dom = self.config.get('entry_pricing', {}).get('check_depth_of_market', {})
if ((bid_check_dom.get('enabled', False)) and
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
@ -505,7 +504,8 @@ class FreqtradeBot(LoggingMixin):
If the strategy triggers the adjustment, a new order gets issued.
Once that completes, the existing trade is modified to match new data.
"""
current_entry_rate, current_exit_rate = self.exchange.get_rates(trade.pair, True, is_short)
current_entry_rate, current_exit_rate = self.exchange.get_rates(
trade.pair, True, trade.is_short)
current_entry_profit = trade.calc_profit_ratio(current_entry_rate)
current_exit_profit = trade.calc_profit_ratio(current_exit_rate)
@ -528,7 +528,8 @@ class FreqtradeBot(LoggingMixin):
current_entry_rate=current_entry_rate, current_exit_rate=current_exit_rate,
current_entry_profit=current_entry_profit, current_exit_profit=current_exit_profit,
min_entry_stake=min_entry_stake, min_exit_stake=min_exit_stake,
max_entry_stake=min(max_entry_stake, stake_available), max_exit_stake=min(max_exit_stake, stake_available),
max_entry_stake=min(max_entry_stake, stake_available),
max_exit_stake=min(max_exit_stake, stake_available)
)
if stake_amount is not None and stake_amount > 0.0:
@ -540,7 +541,8 @@ max_entry_stake=min(max_entry_stake, stake_available), max_exit_stake=min(max_ex
return
else:
logger.debug("Max adjustment entries is set to unlimited.")
self.execute_entry(trade.pair, stake_amount, current_entry_rate, trade=trade, is_short=trade.is_short)
self.execute_entry(trade.pair, stake_amount, current_entry_rate,
trade=trade, is_short=trade.is_short)
if stake_amount is not None and stake_amount < 0.0:
# We should decrease our position
@ -553,8 +555,8 @@ max_entry_stake=min(max_entry_stake, stake_available), max_exit_stake=min(max_ex
logger.info(
f"Adjusting amount to trade.amount as it is higher. {amount} > {trade.amount}")
amount = trade.amount
self.execute_trade_exit(trade, current_exit_rate, sell_reason=SellCheckTuple(
sell_type=SellType.CUSTOM_SELL), sub_trade_amt=amount)
self.execute_trade_exit(trade, current_exit_rate, exit_check=ExitCheckTuple(
exit_type=ExitType.PARTIAL_SELL), sub_trade_amt=amount)
def _check_depth_of_market(self, pair: str, conf: Dict, side: SignalDirection) -> bool:
"""
@ -628,7 +630,6 @@ max_entry_stake=min(max_entry_stake, stake_available), max_exit_stake=min(max_ex
amount = (stake_amount / enter_limit_requested) * leverage
order_type = ordertype or self.strategy.order_types['entry']
if not pos_adjust and not strategy_safe_wrapper(
self.strategy.confirm_trade_entry, default_retval=True)(
pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested,
@ -648,7 +649,7 @@ max_entry_stake=min(max_entry_stake, stake_available), max_exit_stake=min(max_ex
)
order_obj = Order.parse_from_ccxt_object(order, pair, side)
order_id = order['id']
order_status = order.get('status', None)
order_status = order.get('status')
logger.info(f"Order #{order_id} was created for {pair} and status is {order_status}.")
# we assume the order is executed at the price requested
@ -744,8 +745,8 @@ max_entry_stake=min(max_entry_stake, stake_available), max_exit_stake=min(max_ex
else:
logger.info(f"DCA order {order_status}, will wait for resolution: {trade}")
# Update fees if order is closed
if order_status == 'closed':
# Update fees if order is non-opened
if order_status in constants.NON_OPEN_EXCHANGE_STATES:
self.update_trade_state(trade, order_id, order)
return True
@ -1471,7 +1472,7 @@ max_entry_stake=min(max_entry_stake, stake_available), max_exit_stake=min(max_ex
profit_rate = order.safe_price
if not fill:
trade.process_sell_sub_trade(order, is_closed=False)
trade.process_exit_sub_trade(order, is_closed=False)
profit_ratio = trade.close_profit
profit = trade.close_profit_abs
@ -1637,12 +1638,12 @@ max_entry_stake=min(max_entry_stake, stake_available), max_exit_stake=min(max_ex
# Updating wallets when order is closed
self.wallets.update()
sub_trade = not isclose(order_obj.safe_amount_after_fee,
trade.amount, abs_tol=constants.MATH_CLOSE_PREC)
if not trade.is_open:
self.handle_protections(trade.pair)
sub_trade = order_obj.safe_amount_after_fee != trade.amount
if order.get('side', None) == 'sell':
if send_msg and not stoploss_order and not trade.open_order_id:
self._notify_exit(trade, '', True, sub_trade=sub_trade, order=order_obj)
self.handle_protections(trade.pair)
elif send_msg and not trade.open_order_id:
# Enter fill
self._notify_enter(trade, order_obj, fill=True, sub_trade=sub_trade)

File diff suppressed because it is too large Load Diff

View File

@ -480,7 +480,8 @@ class Backtesting:
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
default_retval=None)(
trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=current_rate,
current_profit=current_profit, min_stake=min_stake, max_stake=min(max_stake, stake_available),
current_profit=current_profit, min_stake=min_stake,
max_stake=min(max_stake, stake_available),
current_entry_rate=current_rate, current_exit_rate=current_rate,
max_entry_stake=min(max_stake, stake_available),
max_exit_stake=min(max_stake, stake_available))
@ -586,7 +587,7 @@ max_exit_stake=min(max_stake, stake_available))
close_rate: float, amount: float = None) -> Optional[LocalTrade]:
self.order_id_counter += 1
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
order_type = self.strategy.order_types['sell']
order_type = self.strategy.order_types['exit']
amount = amount or trade.amount
order = Order(
id=self.order_id_counter,
@ -905,7 +906,7 @@ max_exit_stake=min(max_stake, stake_available))
return None
return row
def backtest(self, processed: Dict,
def backtest(self, processed: Dict, # noqa: max-complexity: 13
start_date: datetime, end_date: datetime,
max_open_trades: int = 0, position_stacking: bool = False,
enable_protections: bool = False) -> Dict[str, Any]:
@ -1007,7 +1008,7 @@ max_exit_stake=min(max_stake, stake_available))
sub_trade = order.safe_amount_after_fee != trade.amount
if sub_trade:
order.close_bt_order(current_time)
trade.process_sell_sub_trade(order)
trade.process_exit_sub_trade(order)
trade.recalc_trade_from_orders()
else:
trade.close_date = current_time

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ This module contains the class to persist trades into SQLite
import logging
from datetime import datetime, timedelta, timezone
from decimal import Decimal
from math import isclose
from typing import Any, Dict, List, Optional
from sqlalchemy import (Boolean, Column, DateTime, Enum, Float, ForeignKey, Integer, String,
@ -13,7 +14,7 @@ from sqlalchemy.orm import Query, declarative_base, relationship, scoped_session
from sqlalchemy.pool import StaticPool
from sqlalchemy.sql.schema import UniqueConstraint
from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES
from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES, MATH_CLOSE_PREC
from freqtrade.enums import ExitType, TradingMode
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.leverage import interest
@ -193,7 +194,7 @@ class Order(_DECL_BASE):
self.order_filled_date = datetime.now(timezone.utc)
self.order_update_date = datetime.now(timezone.utc)
def to_json(self, entry_side: str) -> Dict[str, Any]:
def to_json(self, enter_side: str) -> Dict[str, Any]:
return {
'pair': self.ft_pair,
'order_id': self.order_id,
@ -215,7 +216,7 @@ class Order(_DECL_BASE):
tzinfo=timezone.utc).timestamp() * 1000) if self.order_filled_date else None,
'order_type': self.order_type,
'price': self.price,
'ft_is_entry': self.ft_order_side == entry_side,
'ft_is_entry': self.ft_order_side == enter_side,
'remaining': self.remaining,
}
@ -613,6 +614,9 @@ class LocalTrade():
# condition to avoid reset value when updating fees
if self.open_order_id == order.order_id:
self.open_order_id = None
else:
logger.warning(
f'Got different open_order_id {self.open_order_id} != {order.order_id}')
self.recalc_trade_from_orders()
elif order.ft_order_side == self.exit_side:
if self.is_open:
@ -622,10 +626,16 @@ class LocalTrade():
# condition to avoid reset value when updating fees
if self.open_order_id == order.order_id:
self.open_order_id = None
if self.amount == order.safe_amount_after_fee:
else:
logger.warning(
f'Got different open_order_id {self.open_order_id} != {order.order_id}')
if isclose(order.safe_amount_after_fee,
self.amount, abs_tol=MATH_CLOSE_PREC):
self.close(order.safe_price)
else:
self.process_sell_sub_trade(order)
logger.info((self.amount, self.to_json(), order.to_json(
self.enter_side), order.safe_amount_after_fee))
self.process_exit_sub_trade(order)
elif order.ft_order_side == 'stoploss':
self.stoploss_order_id = None
self.close_rate_requested = self.stop_loss
@ -637,25 +647,29 @@ class LocalTrade():
raise ValueError(f'Unknown order type: {order.order_type}')
Trade.commit()
def process_sell_sub_trade(self, order: Order, is_closed: bool = True) -> None:
sell_amount = order.safe_amount_after_fee
sell_rate = order.safe_price
sell_stake_amount = sell_rate * sell_amount * (1 - self.fee_close)
profit = self.calc_profit2(self.open_rate, sell_rate, sell_amount)
def process_exit_sub_trade(self, order: Order, is_closed: bool = True) -> None:
exit_amount = order.safe_amount_after_fee
exit_rate = order.safe_price
exit_stake_amount = exit_rate * exit_amount * (1 - self.fee_close)
profit = self.calc_profit2(self.open_rate, exit_rate, exit_amount)
if is_closed:
self.amount -= sell_amount
self.amount -= exit_amount
self.stake_amount = self.open_rate * self.amount
self.realized_profit += profit
logger.info(
'Processed exit sub trade for %s',
self
)
self.close_profit_abs = profit
self.close_profit = sell_stake_amount / (sell_stake_amount - profit) - 1
self.close_profit = exit_stake_amount / (exit_stake_amount - profit) - 1
self.recalc_open_trade_value()
def calc_profit2(self, open_rate: float, close_rate: float,
amount: float) -> float:
return float(Decimal(amount) *
(Decimal(1 - self.fee_close) * Decimal(close_rate) -
Decimal(1 + self.fee_open) * Decimal(open_rate)))
return float(Decimal(amount)
* (Decimal(1 - self.fee_close) * Decimal(close_rate)
- Decimal(1 + self.fee_open) * Decimal(open_rate)))
def close(self, rate: float, *, show_msg: bool = True) -> None:
"""
@ -729,7 +743,7 @@ class LocalTrade():
def recalc_open_trade_value(self) -> None:
"""
Recalculate open_trade_value.
Must be called whenever open_rate, fee_open or is_short is changed.
Must be called whenever open_rate, fee_open is changed.
"""
self.open_trade_value = self._calc_open_trade_value()
@ -878,11 +892,11 @@ class LocalTrade():
tmp_amount = o.safe_amount_after_fee
tmp_price = o.safe_price
is_sell = o.ft_order_side != 'buy'
side = -1 if is_sell else 1
is_exit = o.ft_order_side != self.enter_side
side = -1 if is_exit else 1
if tmp_amount > 0.0 and tmp_price is not None:
total_amount += tmp_amount * side
price = avg_price if is_sell else tmp_price
price = avg_price if is_exit else tmp_price
total_stake += price * tmp_amount * side
if total_amount > 0:
avg_price = total_stake / total_amount
@ -932,9 +946,9 @@ class LocalTrade():
:return: array of Order objects
"""
return [o for o in self.orders if ((o.ft_order_side == order_side) or (order_side is None))
and o.ft_is_open is False and
(o.filled or 0) > 0 and
o.status in NON_OPEN_EXCHANGE_STATES]
and o.ft_is_open is False
and o.filled
and o.status in NON_OPEN_EXCHANGE_STATES]
@property
def nr_of_successful_entries(self) -> int:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -969,7 +969,12 @@ class IStrategy(ABC, HyperStrategyMixin):
# Make sure current_profit is calculated using high for backtesting.
bound = low if trade.is_short else high
bound_profit = current_profit if not bound else trade.calc_profit_ratio(bound)
from pprint import pformat
logger.info(pformat(trade.to_json()))
logger.info((self.trailing_only_offset_is_reached,
self.trailing_stop_positive, bound_profit, sl_offset))
logger.debug(f"{trade.pair} - Using positive stoploss: {stop_loss_value} "
f"offset: {sl_offset:.4g} profit: {current_profit:.2%}")
# Don't update stoploss if trailing_only_offset_is_reached is true.
if not (self.trailing_only_offset_is_reached and bound_profit < sl_offset):
# Specific handling for trailing_stop_positive

View File

@ -2469,7 +2469,7 @@ def test_get_exit_rate_exception(default_conf, mocker, is_short):
assert exchange.get_rate(pair, refresh=True, side="exit", is_short=is_short) == 0.13
@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", get_buy_rate_data)
@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", get_entry_rate_data)
@pytest.mark.parametrize("side2", ['bid', 'ask'])
@pytest.mark.parametrize("use_order_book", [True, False])
def test_get_rates_testing_buy(mocker, default_conf, caplog, side, ask, bid,
@ -2477,26 +2477,26 @@ def test_get_rates_testing_buy(mocker, default_conf, caplog, side, ask, bid,
side2, use_order_book, order_book_l2) -> None:
caplog.set_level(logging.DEBUG)
if last_ab is None:
del default_conf['bid_strategy']['ask_last_balance']
del default_conf['entry_pricing']['price_last_balance']
else:
default_conf['bid_strategy']['ask_last_balance'] = last_ab
default_conf['bid_strategy']['price_side'] = side
default_conf['ask_strategy']['price_side'] = side2
default_conf['ask_strategy']['use_order_book'] = use_order_book
default_conf['entry_pricing']['price_last_balance'] = last_ab
default_conf['entry_pricing']['price_side'] = side
default_conf['exit_pricing']['price_side'] = side2
default_conf['exit_pricing']['use_order_book'] = use_order_book
api_mock = MagicMock()
api_mock.fetch_l2_order_book = order_book_l2
api_mock.fetch_ticker = MagicMock(
return_value={'ask': ask, 'last': last, 'bid': bid})
exchange = get_patched_exchange(mocker, default_conf, api_mock)
assert exchange.get_rates('ETH/BTC', refresh=True)[0] == expected
assert exchange.get_rates('ETH/BTC', refresh=True, is_short=False)[0] == expected
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
assert exchange.get_rates('ETH/BTC', refresh=False)[0] == expected
assert exchange.get_rates('ETH/BTC', refresh=False, is_short=False)[0] == expected
assert log_has("Using cached buy rate for ETH/BTC.", caplog)
# Running a 2nd time with Refresh on!
caplog.clear()
assert exchange.get_rates('ETH/BTC', refresh=True)[0] == expected
assert exchange.get_rates('ETH/BTC', refresh=True, is_short=False)[0] == expected
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
api_mock.fetch_l2_order_book.call_count = int(use_order_book)
@ -2511,12 +2511,12 @@ def test_get_rates_testing_sell(default_conf, mocker, caplog, side, bid, ask,
side2, use_order_book, order_book_l2) -> None:
caplog.set_level(logging.DEBUG)
default_conf['ask_strategy']['price_side'] = side
default_conf['exit_pricing']['price_side'] = side
if last_ab is not None:
default_conf['ask_strategy']['bid_last_balance'] = last_ab
default_conf['exit_pricing']['price_last_balance'] = last_ab
default_conf['bid_strategy']['price_side'] = side2
default_conf['bid_strategy']['use_order_book'] = use_order_book
default_conf['entry_pricing']['price_side'] = side2
default_conf['entry_pricing']['use_order_book'] = use_order_book
api_mock = MagicMock()
api_mock.fetch_l2_order_book = order_book_l2
api_mock.fetch_ticker = MagicMock(
@ -2526,12 +2526,12 @@ def test_get_rates_testing_sell(default_conf, mocker, caplog, side, bid, ask,
pair = "ETH/BTC"
# Test regular mode
rate = exchange.get_rates(pair, refresh=True)[1]
rate = exchange.get_rates(pair, refresh=True, is_short=False)[1]
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
assert isinstance(rate, float)
assert rate == expected
# Use caching
rate = exchange.get_rates(pair, refresh=False)[1]
rate = exchange.get_rates(pair, refresh=False, is_short=False)[1]
assert rate == expected
assert log_has("Using cached sell rate for ETH/BTC.", caplog)

View File

@ -297,6 +297,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
# Simulate buy & sell
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.orders[0] = oobj
trade.update_trade(oobj)
oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
@ -438,7 +439,8 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
freqtradebot.enter_positions()
trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'sell')
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.orders[0] = oobj
trade.update_trade(oobj)
# Update the ticker with a market going up
@ -452,20 +454,23 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
trade.is_open = False
freqtradebot.enter_positions()
trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# TODO: updated the first trade again
# trade = Trade.query.first()
# # Simulate fulfilled LIMIT_BUY order for trade
# oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
# trade.orders[0] = oobj
# trade.update_trade(oobj)
# Update the ticker with a market going up
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker_sell_up
)
oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow()
trade.is_open = False
# mocker.patch.multiple(
# 'freqtrade.exchange.Exchange',
# fetch_ticker=ticker_sell_up
# )
# oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
# trade.update_trade(oobj)
# trade.close_date = datetime.utcnow()
# trade.is_open = False
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05)
@ -523,6 +528,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.orders[0] = oobj
trade.update_trade(oobj)
# Update the ticker with a market going up
mocker.patch.multiple(
@ -924,6 +930,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.orders[0] = oobj
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade
@ -960,6 +967,7 @@ def test_enter_tag_performance_handle(default_conf, ticker, limit_buy_order, fee
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.orders[0] = oobj
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade
@ -1034,6 +1042,7 @@ def test_exit_reason_performance_handle(default_conf, ticker, limit_buy_order, f
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.orders[0] = oobj
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade
@ -1108,6 +1117,7 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.orders[0] = oobj
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade

File diff suppressed because it is too large Load Diff

View File

@ -2057,7 +2057,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'profit_ratio': -0.57405275,
'stake_currency': 'ETH',
'enter_tag': 'buy_signal1',
'exit_reason': SellType.STOP_LOSS.value,
'exit_reason': ExitType.STOP_LOSS.value,
'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
'close_date': arrow.utcnow(),
})
@ -2144,7 +2144,7 @@ def test_send_msg_sell_fill_notification(default_conf, mocker, direction,
leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
assert msg_mock.call_args[0][0] == (
'\N{WARNING SIGN} *Binance:* Exited KEY/ETH (#1)\n'
'*Profit:* `-57.41% (loss: -0.05746268 ETH)`\`\n'
'*Profit:* `-57.41% (loss: -0.05746268 ETH)`\n'
f'*Enter Tag:* `{enter_signal}`\n'
'*Exit Reason:* `stop_loss`\n'
f"*Direction:* `{direction}`\n"

View File

@ -845,7 +845,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
assert trade
assert trade.open_order_id is None
assert trade.open_rate == 10
assert trade.stake_amount == round(order['price'] * order['filled'] / leverage, 8)
assert trade.stake_amount == round(order['average'] * order['filled'] / leverage, 8)
# In case of rejected or expired order and partially filled
order['status'] = 'expired'
@ -861,7 +861,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
trade = Trade.query.all()[3]
trade.is_short = is_short
assert trade
assert trade.open_order_id == '555'
assert trade.open_order_id is None
assert trade.open_rate == 0.5
assert trade.stake_amount == round(order['average'] * order['filled'] / leverage, 8)
@ -1075,7 +1075,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
'last': 1.9
}),
create_order=MagicMock(side_effect=[
{'id': enter_order['id']},
enter_order,
exit_order,
]),
get_fee=fee,
@ -1101,20 +1101,20 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
# should do nothing and return false
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = 100
trade.stoploss_order_id = "100"
hanging_stoploss_order = MagicMock(return_value={'status': 'open'})
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', hanging_stoploss_order)
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert trade.stoploss_order_id == 100
assert trade.stoploss_order_id == "100"
# Third case: when stoploss was set but it was canceled for some reason
# should set a stoploss immediately and return False
caplog.clear()
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = 100
trade.stoploss_order_id = "100"
canceled_stoploss_order = MagicMock(return_value={'status': 'canceled'})
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', canceled_stoploss_order)
@ -1134,6 +1134,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = "100"
trade.orders.append(Order(
ft_order_side='stoploss',
order_id='100',
@ -1201,7 +1202,6 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
trade.is_open = True
trade.stoploss_last_update = arrow.utcnow().shift(hours=-1).datetime
trade.stop_loss = 24
trade.amount = limit_buy_order_usdt['amount']
freqtrade.config['trailing_stop'] = True
stoploss = MagicMock(side_effect=InvalidOrderException())
@ -2341,9 +2341,9 @@ def test_close_trade(
trade.is_short = is_short
assert trade
oobj = Order.parse_from_ccxt_object(enter_order, enter_order['symbol'], 'buy')
oobj = Order.parse_from_ccxt_object(enter_order, enter_order['symbol'], trade.enter_side)
trade.update_trade(oobj)
oobj = Order.parse_from_ccxt_object(exit_order, exit_order['symbol'], 'sell')
oobj = Order.parse_from_ccxt_object(exit_order, exit_order['symbol'], trade.exit_side)
trade.update_trade(oobj)
assert trade.is_open is False
@ -2534,9 +2534,9 @@ def test_check_handle_timedout_exit_usercustom(
is_short, open_trade_usdt, caplog
) -> None:
default_conf_usdt["unfilledtimeout"] = {"entry": 1440, "exit": 1440, "exit_timeout_count": 1}
open_trade.open_order_id = limit_sell_order_old['id']
open_trade_usdt.open_order_id = limit_sell_order_old['id']
order = Order.parse_from_ccxt_object(limit_sell_order_old, 'mocked', 'sell')
open_trade.orders[0] = order
open_trade_usdt.orders[0] = order
if is_short:
limit_sell_order_old['side'] = 'buy'
open_trade_usdt.is_short = is_short
@ -3505,11 +3505,11 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(
if is_short:
assert rpc_mock.call_args_list[0][0][0]['type'] == RPCMessageType.SHORT
assert rpc_mock.call_args_list[1][0][0]['type'] == RPCMessageType.SHORT_FILL
assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL
else:
assert rpc_mock.call_args_list[0][0][0]['type'] == RPCMessageType.BUY
assert rpc_mock.call_args_list[1][0][0]['type'] == RPCMessageType.BUY_FILL
assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL
@ -3674,7 +3674,7 @@ def test_sell_profit_only(
'last': bid
}),
create_order=MagicMock(side_effect=[
limit_order_open[eside],
limit_order[eside],
{'id': 1234553382},
]),
get_fee=fee,
@ -3956,7 +3956,7 @@ def test_trailing_stop_loss_positive(
'last': enter_price - (-0.01 if is_short else 0.01),
}),
create_order=MagicMock(side_effect=[
limit_order_open[eside],
limit_order[eside],
{'id': 1234553382},
]),
get_fee=fee,
@ -5420,7 +5420,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
MagicMock(return_value=closed_sell_dca_order_1))
assert freqtrade.execute_trade_exit(trade=trade, limit=8,
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
exit_check=ExitCheckTuple(exit_type=ExitType.PARTIAL_SELL),
sub_trade_amt=15)
# Assert trade is as expected (averaged dca)
@ -5552,7 +5552,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
MagicMock(return_value=closed_sell_dca_order_1))
assert freqtrade.execute_trade_exit(trade=trade, limit=ask,
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
exit_check=ExitCheckTuple(exit_type=ExitType.PARTIAL_SELL),
sub_trade_amt=amount)
trades: List[Trade] = trade.get_open_trades_without_assigned_fees()
assert len(trades) == 1
@ -5598,7 +5598,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
MagicMock(return_value=closed_sell_dca_order_2))
assert freqtrade.execute_trade_exit(trade=trade, limit=ask,
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
exit_check=ExitCheckTuple(exit_type=ExitType.PARTIAL_SELL),
sub_trade_amt=amount)
# Assert trade is as expected (averaged dca)
@ -5694,7 +5694,7 @@ def test_position_adjust3(mocker, default_conf_usdt, fee, data) -> None:
else:
assert freqtrade.execute_trade_exit(
trade=trade, limit=price,
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
exit_check=ExitCheckTuple(exit_type=ExitType.PARTIAL_SELL),
sub_trade_amt=amount)
orders1 = Order.query.all()

View File

@ -478,7 +478,7 @@ def test_update_limit_order(fee, caplog, limit_buy_order_usdt, limit_sell_order_
assert trade.close_profit is None
assert trade.close_date is None
trade.open_order_id = 'mocked_limit_buy_usdt'
trade.open_order_id = enter_order['id']
oobj = Order.parse_from_ccxt_object(enter_order, 'ADA/USDT', enter_side)
trade.update_trade(oobj)
assert trade.open_order_id is None
@ -492,7 +492,7 @@ def test_update_limit_order(fee, caplog, limit_buy_order_usdt, limit_sell_order_
caplog)
caplog.clear()
trade.open_order_id = 'mocked_limit_sell_usdt'
trade.open_order_id = enter_order['id']
time_machine.move_to("2022-03-31 21:45:05 +00:00")
oobj = Order.parse_from_ccxt_object(exit_order, 'ADA/USDT', exit_side)
trade.update_trade(oobj)
@ -2543,7 +2543,6 @@ def test_recalc_trade_from_orders_ignores_bad_orders(fee, is_short):
assert trade.open_trade_value == o1_trade_val
assert trade.nr_of_successful_entries == 2
# Check with 1 order
order_noavg = Order(
ft_order_side=enter_side,