updated requested changes in PR #6545
This commit is contained in:
parent
389ae969fc
commit
bd00f6de17
@ -1181,7 +1181,8 @@ class Exchange:
|
|||||||
buy_rate = None
|
buy_rate = None
|
||||||
sell_rate = None
|
sell_rate = None
|
||||||
if not refresh:
|
if not refresh:
|
||||||
buy_rate, sell_rate = self._buy_rate_cache.get(pair), self._sell_rate_cache.get(pair)
|
buy_rate = self._buy_rate_cache.get(pair)
|
||||||
|
sell_rate = self._sell_rate_cache.get(pair)
|
||||||
if buy_rate:
|
if buy_rate:
|
||||||
logger.debug(f"Using cached buy rate for {pair}.")
|
logger.debug(f"Using cached buy rate for {pair}.")
|
||||||
if sell_rate:
|
if sell_rate:
|
||||||
|
@ -485,7 +485,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
logger.debug("Max adjustment entries is set to unlimited.")
|
logger.debug("Max adjustment entries is set to unlimited.")
|
||||||
self.execute_entry(trade.pair, stake_amount, trade=trade)
|
self.execute_entry(trade.pair, stake_amount, current_entry_rate, trade=trade)
|
||||||
|
|
||||||
if stake_amount is not None and stake_amount < 0.0:
|
if stake_amount is not None and stake_amount < 0.0:
|
||||||
# We should decrease our position
|
# We should decrease our position
|
||||||
@ -631,10 +631,6 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
trade.open_order_id = order_id
|
trade.open_order_id = order_id
|
||||||
|
|
||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
if pos_adjust:
|
|
||||||
trade.recalc_trade_from_orders()
|
|
||||||
else:
|
|
||||||
trade.recalc_open_trade_value()
|
|
||||||
Trade.query.session.add(trade)
|
Trade.query.session.add(trade)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
@ -1142,16 +1138,18 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
trade.sell_reason = None
|
trade.sell_reason = None
|
||||||
cancelled = True
|
cancelled = True
|
||||||
|
self.wallets.update()
|
||||||
else:
|
else:
|
||||||
# TODO: figure out how to handle partially complete sell orders
|
# TODO: figure out how to handle partially complete sell orders
|
||||||
reason = constants.CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
|
reason = constants.CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
|
||||||
cancelled = False
|
cancelled = False
|
||||||
|
|
||||||
self.wallets.update()
|
order_obj = Order.parse_from_ccxt_object(order, trade.pair, 'sell')
|
||||||
|
sub_trade = order_obj.amount != trade.amount
|
||||||
self._notify_exit_cancel(
|
self._notify_exit_cancel(
|
||||||
trade,
|
trade,
|
||||||
order_type=self.strategy.order_types['sell'],
|
order_type=self.strategy.order_types['sell'],
|
||||||
reason=reason
|
reason=reason, sub_trade=sub_trade, order=order_obj
|
||||||
)
|
)
|
||||||
return cancelled
|
return cancelled
|
||||||
|
|
||||||
@ -1189,7 +1187,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
exit_tag: Optional[str] = None,
|
exit_tag: Optional[str] = None,
|
||||||
ordertype: Optional[str] = None,
|
ordertype: Optional[str] = None,
|
||||||
sub_trade_amt: float = None,
|
sub_trade_amt: float = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Executes a trade exit for the given trade and limit
|
Executes a trade exit for the given trade and limit
|
||||||
:param trade: Trade instance
|
:param trade: Trade instance
|
||||||
@ -1279,7 +1277,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
current_rate = self.exchange.get_rate(
|
current_rate = self.exchange.get_rate(
|
||||||
trade.pair, refresh=False, side="sell") if not fill else None
|
trade.pair, refresh=False, side="sell") if not fill else None
|
||||||
|
|
||||||
# second condtion is for mypy only; order will always be passed during sub trade
|
# second condition is for mypy only; order will always be passed during sub trade
|
||||||
if sub_trade and order is not None:
|
if sub_trade and order is not None:
|
||||||
amount = order.safe_filled
|
amount = order.safe_filled
|
||||||
profit_rate = order.safe_price
|
profit_rate = order.safe_price
|
||||||
@ -1327,7 +1325,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
self.rpc.send_msg(msg)
|
self.rpc.send_msg(msg)
|
||||||
|
|
||||||
def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str,
|
def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str,
|
||||||
sub_trade: bool = False) -> None:
|
sub_trade: bool = False, order: Order=None) -> None:
|
||||||
"""
|
"""
|
||||||
Sends rpc notification when a sell cancel occurred.
|
Sends rpc notification when a sell cancel occurred.
|
||||||
"""
|
"""
|
||||||
@ -1350,7 +1348,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
'gain': gain,
|
'gain': gain,
|
||||||
'limit': profit_rate or 0,
|
'limit': profit_rate or 0,
|
||||||
'order_type': order_type,
|
'order_type': order_type,
|
||||||
'amount': trade.amount,
|
'amount': order.safe_amount_after_fee,
|
||||||
'open_rate': trade.open_rate,
|
'open_rate': trade.open_rate,
|
||||||
'current_rate': current_rate,
|
'current_rate': current_rate,
|
||||||
'profit_amount': profit_trade,
|
'profit_amount': profit_trade,
|
||||||
@ -1419,7 +1417,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
trade.update_trade(order_obj)
|
trade.update_trade(order_obj)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
if order['status'] in constants.NON_OPEN_EXCHANGE_STATES:
|
if order.get('status') in constants.NON_OPEN_EXCHANGE_STATES:
|
||||||
# If a buy order was closed, force update on stoploss on exchange
|
# If a buy order was closed, force update on stoploss on exchange
|
||||||
if order.get('side', None) == 'buy':
|
if order.get('side', None) == 'buy':
|
||||||
trade = self.cancel_stoploss_on_exchange(trade)
|
trade = self.cancel_stoploss_on_exchange(trade)
|
||||||
|
@ -383,8 +383,7 @@ class Backtesting:
|
|||||||
|
|
||||||
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
|
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
|
||||||
) -> LocalTrade:
|
) -> LocalTrade:
|
||||||
current_entry_rate = current_exit_rate = row[OPEN_IDX]
|
current_rate = row[OPEN_IDX]
|
||||||
current_rate = current_entry_rate
|
|
||||||
|
|
||||||
current_profit = trade.calc_profit_ratio(current_rate)
|
current_profit = trade.calc_profit_ratio(current_rate)
|
||||||
min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, current_rate, -0.1)
|
min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, current_rate, -0.1)
|
||||||
@ -393,7 +392,7 @@ class Backtesting:
|
|||||||
default_retval=None)(
|
default_retval=None)(
|
||||||
trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=current_rate,
|
trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=current_rate,
|
||||||
current_profit=current_profit, min_stake=min_stake, max_stake=max_stake,
|
current_profit=current_profit, min_stake=min_stake, max_stake=max_stake,
|
||||||
current_entry_rate=current_entry_rate, current_exit_rate=current_exit_rate)
|
current_entry_rate=current_rate, current_exit_rate=current_rate)
|
||||||
|
|
||||||
# Check if we should increase our position
|
# Check if we should increase our position
|
||||||
if stake_amount is not None and stake_amount > 0.0:
|
if stake_amount is not None and stake_amount > 0.0:
|
||||||
@ -403,9 +402,8 @@ class Backtesting:
|
|||||||
return pos_trade
|
return pos_trade
|
||||||
|
|
||||||
if stake_amount is not None and stake_amount < 0.0:
|
if stake_amount is not None and stake_amount < 0.0:
|
||||||
amount = -stake_amount / current_rate
|
amount = abs(stake_amount) / current_rate
|
||||||
if amount > trade.amount:
|
if amount > trade.amount:
|
||||||
logger.info(f"Amount is higher than available. {amount} > {trade.amount}")
|
|
||||||
return trade
|
return trade
|
||||||
pos_trade = self._exit_trade(trade, row, current_rate, amount)
|
pos_trade = self._exit_trade(trade, row, current_rate, amount)
|
||||||
if pos_trade is not None:
|
if pos_trade is not None:
|
||||||
@ -441,29 +439,29 @@ class Backtesting:
|
|||||||
|
|
||||||
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
|
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
|
||||||
try:
|
try:
|
||||||
closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)
|
close_rate = self._get_close_rate(sell_row, trade, sell, trade_dur)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
# call the custom exit price,with default value as previous closerate
|
# call the custom exit price,with default value as previous close_rate
|
||||||
current_profit = trade.calc_profit_ratio(closerate)
|
current_profit = trade.calc_profit_ratio(close_rate)
|
||||||
order_type = self.strategy.order_types['sell']
|
order_type = self.strategy.order_types['sell']
|
||||||
if sell.sell_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL):
|
if sell.sell_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL):
|
||||||
# Custom exit pricing only for sell-signals
|
# Custom exit pricing only for sell-signals
|
||||||
if order_type == 'limit':
|
if order_type == 'limit':
|
||||||
closerate = strategy_safe_wrapper(self.strategy.custom_exit_price,
|
close_rate = strategy_safe_wrapper(self.strategy.custom_exit_price,
|
||||||
default_retval=closerate)(
|
default_retval=close_rate)(
|
||||||
pair=trade.pair, trade=trade,
|
pair=trade.pair, trade=trade,
|
||||||
current_time=sell_candle_time,
|
current_time=sell_candle_time,
|
||||||
proposed_rate=closerate, current_profit=current_profit)
|
proposed_rate=close_rate, current_profit=current_profit)
|
||||||
# We can't place orders lower than current low.
|
# We can't place orders lower than current low.
|
||||||
# freqtrade does not support this in live, and the order would fill immediately
|
# freqtrade does not support this in live, and the order would fill immediately
|
||||||
closerate = max(closerate, sell_row[LOW_IDX])
|
close_rate = max(close_rate, sell_row[LOW_IDX])
|
||||||
# Confirm trade exit:
|
# Confirm trade exit:
|
||||||
time_in_force = self.strategy.order_time_in_force['sell']
|
time_in_force = self.strategy.order_time_in_force['sell']
|
||||||
|
|
||||||
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)(
|
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)(
|
||||||
pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount,
|
pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount,
|
||||||
rate=closerate,
|
rate=close_rate,
|
||||||
time_in_force=time_in_force,
|
time_in_force=time_in_force,
|
||||||
sell_reason=sell.sell_reason,
|
sell_reason=sell.sell_reason,
|
||||||
current_time=sell_candle_time):
|
current_time=sell_candle_time):
|
||||||
@ -480,15 +478,16 @@ class Backtesting:
|
|||||||
):
|
):
|
||||||
trade.sell_reason = sell_row[EXIT_TAG_IDX]
|
trade.sell_reason = sell_row[EXIT_TAG_IDX]
|
||||||
|
|
||||||
return self._exit_trade(trade, sell_row, closerate)
|
return self._exit_trade(trade, sell_row, close_rate)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple,
|
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple,
|
||||||
closerate: float, amount: float = None) -> Optional[LocalTrade]:
|
close_rate: float, amount: float = None) -> Optional[LocalTrade]:
|
||||||
self.order_id_counter += 1
|
self.order_id_counter += 1
|
||||||
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
|
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
|
||||||
order_type = self.strategy.order_types['sell']
|
order_type = self.strategy.order_types['sell']
|
||||||
|
amount = amount or trade.amount
|
||||||
order = Order(
|
order = Order(
|
||||||
id=self.order_id_counter,
|
id=self.order_id_counter,
|
||||||
ft_trade_id=trade.id,
|
ft_trade_id=trade.id,
|
||||||
@ -502,12 +501,12 @@ class Backtesting:
|
|||||||
side="sell",
|
side="sell",
|
||||||
order_type=order_type,
|
order_type=order_type,
|
||||||
status="open",
|
status="open",
|
||||||
price=closerate,
|
price=close_rate,
|
||||||
average=closerate,
|
average=close_rate,
|
||||||
amount=amount or trade.amount,
|
amount=amount,
|
||||||
filled=0,
|
filled=0,
|
||||||
remaining=trade.amount,
|
remaining=amount,
|
||||||
cost=trade.amount * closerate,
|
cost=amount * close_rate,
|
||||||
)
|
)
|
||||||
trade.orders.append(order)
|
trade.orders.append(order)
|
||||||
return trade
|
return trade
|
||||||
|
@ -882,8 +882,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
|
|
||||||
return strategy_safe_wrapper(time_method,
|
return strategy_safe_wrapper(time_method,
|
||||||
default_retval=False)(
|
default_retval=False)(
|
||||||
pair=trade.pair, trade=trade, order=order,
|
pair=trade.pair, trade=trade, order=order,
|
||||||
current_time=current_time)
|
current_time=current_time)
|
||||||
|
|
||||||
def advise_all_indicators(self, data: Dict[str, DataFrame]) -> Dict[str, DataFrame]:
|
def advise_all_indicators(self, data: Dict[str, DataFrame]) -> Dict[str, DataFrame]:
|
||||||
"""
|
"""
|
||||||
|
@ -249,7 +249,7 @@ class Wallets:
|
|||||||
|
|
||||||
if min_stake_amount is not None and min_stake_amount > max_stake_amount:
|
if min_stake_amount is not None and min_stake_amount > max_stake_amount:
|
||||||
if self._log:
|
if self._log:
|
||||||
logger.warning("Minimum stake amount > available balance.")
|
logger.warning(f"Minimum stake amount > available balance.{min_stake_amount} > {max_stake_amount}")
|
||||||
return 0
|
return 0
|
||||||
if min_stake_amount is not None and stake_amount < min_stake_amount:
|
if min_stake_amount is not None and stake_amount < min_stake_amount:
|
||||||
if self._log:
|
if self._log:
|
||||||
|
@ -26,6 +26,54 @@ from tests.conftest import get_mock_coro, get_patched_exchange, log_has, log_has
|
|||||||
# Make sure to always keep one exchange here which is NOT subclassed!!
|
# Make sure to always keep one exchange here which is NOT subclassed!!
|
||||||
EXCHANGES = ['bittrex', 'binance', 'kraken', 'ftx']
|
EXCHANGES = ['bittrex', 'binance', 'kraken', 'ftx']
|
||||||
|
|
||||||
|
get_buy_rate_data = [
|
||||||
|
('ask', 20, 19, 10, 0.0, 20), # Full ask side
|
||||||
|
('ask', 20, 19, 10, 1.0, 10), # Full last side
|
||||||
|
('ask', 20, 19, 10, 0.5, 15), # Between ask and last
|
||||||
|
('ask', 20, 19, 10, 0.7, 13), # Between ask and last
|
||||||
|
('ask', 20, 19, 10, 0.3, 17), # Between ask and last
|
||||||
|
('ask', 5, 6, 10, 1.0, 5), # last bigger than ask
|
||||||
|
('ask', 5, 6, 10, 0.5, 5), # last bigger than ask
|
||||||
|
('ask', 20, 19, 10, None, 20), # ask_last_balance missing
|
||||||
|
('ask', 10, 20, None, 0.5, 10), # last not available - uses ask
|
||||||
|
('ask', 4, 5, None, 0.5, 4), # last not available - uses ask
|
||||||
|
('ask', 4, 5, None, 1, 4), # last not available - uses ask
|
||||||
|
('ask', 4, 5, None, 0, 4), # last not available - uses ask
|
||||||
|
('bid', 21, 20, 10, 0.0, 20), # Full bid side
|
||||||
|
('bid', 21, 20, 10, 1.0, 10), # Full last side
|
||||||
|
('bid', 21, 20, 10, 0.5, 15), # Between bid and last
|
||||||
|
('bid', 21, 20, 10, 0.7, 13), # Between bid and last
|
||||||
|
('bid', 21, 20, 10, 0.3, 17), # Between bid and last
|
||||||
|
('bid', 6, 5, 10, 1.0, 5), # last bigger than bid
|
||||||
|
('bid', 21, 20, 10, None, 20), # ask_last_balance missing
|
||||||
|
('bid', 6, 5, 10, 0.5, 5), # last bigger than bid
|
||||||
|
('bid', 21, 20, None, 0.5, 20), # last not available - uses bid
|
||||||
|
('bid', 6, 5, None, 0.5, 5), # last not available - uses bid
|
||||||
|
('bid', 6, 5, None, 1, 5), # last not available - uses bid
|
||||||
|
('bid', 6, 5, None, 0, 5), # last not available - uses bid
|
||||||
|
]
|
||||||
|
|
||||||
|
get_sell_rate_data = [
|
||||||
|
('bid', 12.0, 11.0, 11.5, 0.0, 11.0), # full bid side
|
||||||
|
('bid', 12.0, 11.0, 11.5, 1.0, 11.5), # full last side
|
||||||
|
('bid', 12.0, 11.0, 11.5, 0.5, 11.25), # between bid and lat
|
||||||
|
('bid', 12.0, 11.2, 10.5, 0.0, 11.2), # Last smaller than bid
|
||||||
|
('bid', 12.0, 11.2, 10.5, 1.0, 11.2), # Last smaller than bid - uses bid
|
||||||
|
('bid', 12.0, 11.2, 10.5, 0.5, 11.2), # Last smaller than bid - uses bid
|
||||||
|
('bid', 0.003, 0.002, 0.005, 0.0, 0.002),
|
||||||
|
('bid', 0.003, 0.002, 0.005, None, 0.002),
|
||||||
|
('ask', 12.0, 11.0, 12.5, 0.0, 12.0), # full ask side
|
||||||
|
('ask', 12.0, 11.0, 12.5, 1.0, 12.5), # full last side
|
||||||
|
('ask', 12.0, 11.0, 12.5, 0.5, 12.25), # between bid and lat
|
||||||
|
('ask', 12.2, 11.2, 10.5, 0.0, 12.2), # Last smaller than ask
|
||||||
|
('ask', 12.0, 11.0, 10.5, 1.0, 12.0), # Last smaller than ask - uses ask
|
||||||
|
('ask', 12.0, 11.2, 10.5, 0.5, 12.0), # Last smaller than ask - uses ask
|
||||||
|
('ask', 10.0, 11.0, 11.0, 0.0, 10.0),
|
||||||
|
('ask', 10.11, 11.2, 11.0, 0.0, 10.11),
|
||||||
|
('ask', 0.001, 0.002, 11.0, 0.0, 0.001),
|
||||||
|
('ask', 0.006, 1.0, 11.0, 0.0, 0.006),
|
||||||
|
('ask', 0.006, 1.0, 11.0, None, 0.006),
|
||||||
|
]
|
||||||
|
|
||||||
def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||||
fun, mock_ccxt_fun, retries=API_RETRY_COUNT + 1, **kwargs):
|
fun, mock_ccxt_fun, retries=API_RETRY_COUNT + 1, **kwargs):
|
||||||
@ -1903,33 +1951,6 @@ def test_fetch_l2_order_book_exception(default_conf, mocker, exchange_name):
|
|||||||
exchange.fetch_l2_order_book(pair='ETH/BTC', limit=50)
|
exchange.fetch_l2_order_book(pair='ETH/BTC', limit=50)
|
||||||
|
|
||||||
|
|
||||||
get_buy_rate_data = [
|
|
||||||
('ask', 20, 19, 10, 0.0, 20), # Full ask side
|
|
||||||
('ask', 20, 19, 10, 1.0, 10), # Full last side
|
|
||||||
('ask', 20, 19, 10, 0.5, 15), # Between ask and last
|
|
||||||
('ask', 20, 19, 10, 0.7, 13), # Between ask and last
|
|
||||||
('ask', 20, 19, 10, 0.3, 17), # Between ask and last
|
|
||||||
('ask', 5, 6, 10, 1.0, 5), # last bigger than ask
|
|
||||||
('ask', 5, 6, 10, 0.5, 5), # last bigger than ask
|
|
||||||
('ask', 20, 19, 10, None, 20), # ask_last_balance missing
|
|
||||||
('ask', 10, 20, None, 0.5, 10), # last not available - uses ask
|
|
||||||
('ask', 4, 5, None, 0.5, 4), # last not available - uses ask
|
|
||||||
('ask', 4, 5, None, 1, 4), # last not available - uses ask
|
|
||||||
('ask', 4, 5, None, 0, 4), # last not available - uses ask
|
|
||||||
('bid', 21, 20, 10, 0.0, 20), # Full bid side
|
|
||||||
('bid', 21, 20, 10, 1.0, 10), # Full last side
|
|
||||||
('bid', 21, 20, 10, 0.5, 15), # Between bid and last
|
|
||||||
('bid', 21, 20, 10, 0.7, 13), # Between bid and last
|
|
||||||
('bid', 21, 20, 10, 0.3, 17), # Between bid and last
|
|
||||||
('bid', 6, 5, 10, 1.0, 5), # last bigger than bid
|
|
||||||
('bid', 21, 20, 10, None, 20), # ask_last_balance missing
|
|
||||||
('bid', 6, 5, 10, 0.5, 5), # last bigger than bid
|
|
||||||
('bid', 21, 20, None, 0.5, 20), # last not available - uses bid
|
|
||||||
('bid', 6, 5, None, 0.5, 5), # last not available - uses bid
|
|
||||||
('bid', 6, 5, None, 1, 5), # last not available - uses bid
|
|
||||||
('bid', 6, 5, None, 0, 5), # last not available - uses bid
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@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_buy_rate_data)
|
||||||
def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
|
def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
|
||||||
@ -1955,29 +1976,6 @@ def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
|
|||||||
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
|
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
|
||||||
|
|
||||||
|
|
||||||
get_sell_rate_data = [
|
|
||||||
('bid', 12.0, 11.0, 11.5, 0.0, 11.0), # full bid side
|
|
||||||
('bid', 12.0, 11.0, 11.5, 1.0, 11.5), # full last side
|
|
||||||
('bid', 12.0, 11.0, 11.5, 0.5, 11.25), # between bid and lat
|
|
||||||
('bid', 12.0, 11.2, 10.5, 0.0, 11.2), # Last smaller than bid
|
|
||||||
('bid', 12.0, 11.2, 10.5, 1.0, 11.2), # Last smaller than bid - uses bid
|
|
||||||
('bid', 12.0, 11.2, 10.5, 0.5, 11.2), # Last smaller than bid - uses bid
|
|
||||||
('bid', 0.003, 0.002, 0.005, 0.0, 0.002),
|
|
||||||
('bid', 0.003, 0.002, 0.005, None, 0.002),
|
|
||||||
('ask', 12.0, 11.0, 12.5, 0.0, 12.0), # full ask side
|
|
||||||
('ask', 12.0, 11.0, 12.5, 1.0, 12.5), # full last side
|
|
||||||
('ask', 12.0, 11.0, 12.5, 0.5, 12.25), # between bid and lat
|
|
||||||
('ask', 12.2, 11.2, 10.5, 0.0, 12.2), # Last smaller than ask
|
|
||||||
('ask', 12.0, 11.0, 10.5, 1.0, 12.0), # Last smaller than ask - uses ask
|
|
||||||
('ask', 12.0, 11.2, 10.5, 0.5, 12.0), # Last smaller than ask - uses ask
|
|
||||||
('ask', 10.0, 11.0, 11.0, 0.0, 10.0),
|
|
||||||
('ask', 10.11, 11.2, 11.0, 0.0, 10.11),
|
|
||||||
('ask', 0.001, 0.002, 11.0, 0.0, 0.001),
|
|
||||||
('ask', 0.006, 1.0, 11.0, 0.0, 0.006),
|
|
||||||
('ask', 0.006, 1.0, 11.0, None, 0.006),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('side,ask,bid,last,last_ab,expected', get_sell_rate_data)
|
@pytest.mark.parametrize('side,ask,bid,last,last_ab,expected', get_sell_rate_data)
|
||||||
def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask,
|
def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask,
|
||||||
last, last_ab, expected) -> None:
|
last, last_ab, expected) -> None:
|
||||||
@ -2108,7 +2106,7 @@ def test_get_rates_testing_buy(mocker, default_conf, caplog, side, ask, bid,
|
|||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.fetch_l2_order_book = order_book_l2
|
api_mock.fetch_l2_order_book = order_book_l2
|
||||||
api_mock.fetch_ticker = MagicMock(
|
api_mock.fetch_ticker = MagicMock(
|
||||||
return_value={'ask': ask, 'last': last, 'bid': bid})
|
return_value={'ask': ask, 'last': last, 'bid': bid})
|
||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
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)[0] == expected
|
||||||
@ -2148,12 +2146,12 @@ def test_get_rates_testing_sell(default_conf, mocker, caplog, side, bid, ask,
|
|||||||
pair = "ETH/BTC"
|
pair = "ETH/BTC"
|
||||||
|
|
||||||
# Test regular mode
|
# Test regular mode
|
||||||
rate = exchange.get_rate(pair, refresh=True, side="sell")
|
rate = exchange.get_rates(pair, refresh=True)[1]
|
||||||
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||||
assert isinstance(rate, float)
|
assert isinstance(rate, float)
|
||||||
assert rate == expected
|
assert rate == expected
|
||||||
# Use caching
|
# Use caching
|
||||||
rate = exchange.get_rate(pair, refresh=False, side="sell")
|
rate = exchange.get_rates(pair, refresh=False)[1]
|
||||||
assert rate == expected
|
assert rate == expected
|
||||||
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||||
|
|
||||||
|
@ -467,9 +467,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
trade.open_rate = oobj.safe_price
|
trade.orders[0] = oobj
|
||||||
trade.amount = oobj.safe_amount_after_fee
|
trade.update_trade(oobj)
|
||||||
trade.recalc_open_trade_value()
|
|
||||||
trade.update_trade(oobjs)
|
trade.update_trade(oobjs)
|
||||||
trade.close_date = datetime.utcnow()
|
trade.close_date = datetime.utcnow()
|
||||||
trade.is_open = False
|
trade.is_open = False
|
||||||
@ -586,9 +585,8 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
trade.open_rate = oobj.safe_price
|
trade.orders[0] = oobj
|
||||||
trade.amount = oobj.safe_amount_after_fee
|
trade.update_trade(oobj)
|
||||||
trade.recalc_open_trade_value()
|
|
||||||
trade.update_trade(oobjs)
|
trade.update_trade(oobjs)
|
||||||
trade.close_date = datetime.utcnow()
|
trade.close_date = datetime.utcnow()
|
||||||
trade.is_open = False
|
trade.is_open = False
|
||||||
@ -708,9 +706,8 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||||||
|
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
trade.open_rate = oobj.safe_price
|
trade.orders[0] = oobj
|
||||||
trade.amount = oobj.safe_amount_after_fee
|
trade.update_trade(oobj)
|
||||||
trade.recalc_open_trade_value()
|
|
||||||
trade.update_trade(oobjs)
|
trade.update_trade(oobjs)
|
||||||
trade.close_date = datetime.utcnow()
|
trade.close_date = datetime.utcnow()
|
||||||
trade.is_open = False
|
trade.is_open = False
|
||||||
|
@ -2075,9 +2075,9 @@ def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, li
|
|||||||
default_conf_usdt["unfilledtimeout"] = {"buy": 1400, "sell": 30}
|
default_conf_usdt["unfilledtimeout"] = {"buy": 1400, "sell": 30}
|
||||||
limit_buy_order_old['id'] = open_trade.open_order_id
|
limit_buy_order_old['id'] = open_trade.open_order_id
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
cancel_order_mock = MagicMock(return_value=limit_buy_order_old)
|
|
||||||
cancel_buy_order = deepcopy(limit_buy_order_old)
|
cancel_buy_order = deepcopy(limit_buy_order_old)
|
||||||
cancel_buy_order['status'] = 'canceled'
|
cancel_buy_order['status'] = 'canceled'
|
||||||
|
cancel_order_mock = MagicMock(return_value=limit_buy_order_old)
|
||||||
cancel_order_wr_mock = MagicMock(return_value=cancel_buy_order)
|
cancel_order_wr_mock = MagicMock(return_value=cancel_buy_order)
|
||||||
|
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -2085,8 +2085,8 @@ def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, li
|
|||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
fetch_ticker=ticker_usdt,
|
fetch_ticker=ticker_usdt,
|
||||||
fetch_order=MagicMock(return_value=limit_buy_order_old),
|
fetch_order=MagicMock(return_value=limit_buy_order_old),
|
||||||
cancel_order_with_result=cancel_order_wr_mock,
|
|
||||||
cancel_order=cancel_order_mock,
|
cancel_order=cancel_order_mock,
|
||||||
|
cancel_order_with_result=cancel_order_wr_mock,
|
||||||
get_fee=fee
|
get_fee=fee
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
@ -2129,7 +2129,9 @@ def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, li
|
|||||||
def test_check_handle_timedout_buy(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
|
def test_check_handle_timedout_buy(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
|
||||||
fee, mocker) -> None:
|
fee, mocker) -> None:
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
limit_buy_order_old['id'] = open_trade.open_order_id
|
open_trade.open_order_id = limit_buy_order_old['id']
|
||||||
|
order = Order.parse_from_ccxt_object(limit_buy_order_old, 'mocked', 'buy')
|
||||||
|
open_trade.orders[0] = order
|
||||||
limit_buy_cancel = deepcopy(limit_buy_order_old)
|
limit_buy_cancel = deepcopy(limit_buy_order_old)
|
||||||
limit_buy_cancel['status'] = 'canceled'
|
limit_buy_cancel['status'] = 'canceled'
|
||||||
cancel_order_mock = MagicMock(return_value=limit_buy_cancel)
|
cancel_order_mock = MagicMock(return_value=limit_buy_cancel)
|
||||||
@ -2214,8 +2216,9 @@ def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt,
|
|||||||
def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, limit_sell_order_old,
|
def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, limit_sell_order_old,
|
||||||
mocker, open_trade, caplog) -> None:
|
mocker, open_trade, caplog) -> None:
|
||||||
default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440, "exit_timeout_count": 1}
|
default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440, "exit_timeout_count": 1}
|
||||||
limit_sell_order_old['id'] = open_trade.open_order_id
|
open_trade.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
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -4877,45 +4880,26 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
|||||||
assert trade.is_open is False
|
assert trade.is_open is False
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('orders, results', [
|
@pytest.mark.parametrize('data', [
|
||||||
(
|
(
|
||||||
(
|
# tuple 1 - side amount, price
|
||||||
# side ampunt, price
|
# tuple 2 - amount, open_rate, stake_amount, cumulative_profit, realized_profit
|
||||||
('buy', 100, 10),
|
(('buy', 100, 10), (100.0, 10.0, 1000.0, 0.0, None)),
|
||||||
('buy', 100, 15),
|
(('buy', 100, 15), (200.0, 12.5, 2500.0, 0.0, None)),
|
||||||
('sell', 50, 12),
|
(('sell', 50, 12), (150.0, 12.5, 1875.0, -28.0625, -28.0625)),
|
||||||
('sell', 100, 20),
|
(('sell', 100, 20), (50.0, 12.5, 625.0, 713.8125, 741.875)),
|
||||||
('sell', 50, 5),
|
(('sell', 50, 5), (50.0, 12.5, 625.0, 713.8125, 336.625)),
|
||||||
),
|
|
||||||
(
|
|
||||||
# amount, open_rate, stake_amount, cumulative_profit, realized_profit
|
|
||||||
(100.0, 10.0, 1000.0, 0.0, None,),
|
|
||||||
(200.0, 12.5, 2500.0, 0.0, None,),
|
|
||||||
(150.0, 12.5, 1875.0, -28.0625, -28.0625,),
|
|
||||||
(50.0, 12.5, 625.0, 713.8125, 741.875,),
|
|
||||||
(50.0, 12.5, 625.0, 713.8125, 336.625,),
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
(
|
(('buy', 100, 3), (100.0, 3.0, 300.0, 0.0, None)),
|
||||||
('buy', 100, 3),
|
(('buy', 100, 7), (200.0, 5.0, 1000.0, 0.0, None)),
|
||||||
('buy', 100, 7),
|
(('sell', 100, 11), (100.0, 5.0, 500.0, 596.0, 596.0)),
|
||||||
('sell', 100, 11),
|
(('buy', 150, 15), (250.0, 11.0, 2750.0, 596.0, 596.0)),
|
||||||
('buy', 150, 15),
|
(('sell', 100, 19), (150.0, 11.0, 1650.0, 1388.5, 792.5)),
|
||||||
('sell', 100, 19),
|
(('sell', 150, 23), (150.0, 11.0, 1650.0, 1388.5, 3175.75)),
|
||||||
('sell', 150, 23),
|
)
|
||||||
),
|
|
||||||
(
|
|
||||||
(100.0, 3.0, 300.0, 0.0, None,),
|
|
||||||
(200.0, 5.0, 1000.0, 0.0, None,),
|
|
||||||
(100.0, 5.0, 500.0, 596.0, 596.0,),
|
|
||||||
(250.0, 11.0, 2750.0, 596.0, 596.0,),
|
|
||||||
(150.0, 11.0, 1650.0, 1388.5, 792.5,),
|
|
||||||
(150.0, 11.0, 1650.0, 1388.5, 3175.75,),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
])
|
])
|
||||||
def test_position_adjust3(mocker, default_conf_usdt, fee, orders, results) -> None:
|
def test_position_adjust3(mocker, default_conf_usdt, fee, data) -> None:
|
||||||
default_conf_usdt.update({
|
default_conf_usdt.update({
|
||||||
"position_adjustment_enable": True,
|
"position_adjustment_enable": True,
|
||||||
"dry_run": False,
|
"dry_run": False,
|
||||||
@ -4928,7 +4912,7 @@ def test_position_adjust3(mocker, default_conf_usdt, fee, orders, results) -> No
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
trade = None
|
trade = None
|
||||||
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
|
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
|
||||||
for idx, (order, result) in enumerate(zip(orders, results)):
|
for idx, (order, result) in enumerate(data):
|
||||||
amount = order[1]
|
amount = order[1]
|
||||||
price = order[2]
|
price = order[2]
|
||||||
price_mock = MagicMock(return_value=price)
|
price_mock = MagicMock(return_value=price)
|
||||||
|
@ -1215,7 +1215,7 @@ def test_update_order_from_ccxt(caplog):
|
|||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
'price': 1234.5,
|
'price': 1234.5,
|
||||||
'amount': 20.0,
|
'amount': 20.0,
|
||||||
'filled': 9,
|
'filled': 9,
|
||||||
'remaining': 11,
|
'remaining': 11,
|
||||||
'status': 'open',
|
'status': 'open',
|
||||||
@ -1641,7 +1641,7 @@ def test_recalc_trade_from_orders_ignores_bad_orders(fee):
|
|||||||
assert trade.open_trade_value == 2 * o1_trade_val
|
assert trade.open_trade_value == 2 * o1_trade_val
|
||||||
assert trade.nr_of_successful_buys == 2
|
assert trade.nr_of_successful_buys == 2
|
||||||
|
|
||||||
# Just to make sure sell orders are ignored, let's calculate one more time.
|
# Just to make sure non partial sell orders are ignored, let's calculate one more time.
|
||||||
sell1 = Order(
|
sell1 = Order(
|
||||||
ft_order_side='sell',
|
ft_order_side='sell',
|
||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
|
Loading…
Reference in New Issue
Block a user