diff --git a/freqtrade/constants.py b/freqtrade/constants.py index c6b8f0e62..ee104325b 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -39,6 +39,8 @@ DEFAULT_DATAFRAME_COLUMNS = ['date', 'open', 'high', 'low', 'close', 'volume'] # Don't modify sequence of DEFAULT_TRADES_COLUMNS # it has wide consequences for stored trades files DEFAULT_TRADES_COLUMNS = ['timestamp', 'id', 'type', 'side', 'price', 'amount', 'cost'] +TRADING_MODES = ['spot', 'margin', 'futures'] +COLLATERAL_TYPES = ['cross', 'isolated'] LAST_BT_RESULT_FN = '.last_result.json' FTHYPT_FILEVERSION = 'fthypt_fileversion' @@ -146,6 +148,8 @@ CONF_SCHEMA = { 'sell_profit_offset': {'type': 'number'}, 'ignore_roi_if_buy_signal': {'type': 'boolean'}, 'ignore_buying_expired_candle_after': {'type': 'number'}, + 'trading_mode': {'type': 'string', 'enum': TRADING_MODES}, + 'collateral_type': {'type': 'string', 'enum': COLLATERAL_TYPES}, 'bot_name': {'type': 'string'}, 'unfilledtimeout': { 'type': 'object', @@ -193,7 +197,7 @@ CONF_SCHEMA = { 'required': ['price_side'] }, 'custom_price_max_distance_ratio': { - 'type': 'number', 'minimum': 0.0 + 'type': 'number', 'minimum': 0.0 }, 'order_types': { 'type': 'object', diff --git a/freqtrade/enums/rpcmessagetype.py b/freqtrade/enums/rpcmessagetype.py index 4e3f693e5..663b37b83 100644 --- a/freqtrade/enums/rpcmessagetype.py +++ b/freqtrade/enums/rpcmessagetype.py @@ -5,15 +5,21 @@ class RPCMessageType(Enum): STATUS = 'status' WARNING = 'warning' STARTUP = 'startup' + BUY = 'buy' BUY_FILL = 'buy_fill' BUY_CANCEL = 'buy_cancel' + SELL = 'sell' SELL_FILL = 'sell_fill' SELL_CANCEL = 'sell_cancel' PROTECTION_TRIGGER = 'protection_trigger' PROTECTION_TRIGGER_GLOBAL = 'protection_trigger_global' + SHORT = 'short' + SHORT_FILL = 'short_fill' + SHORT_CANCEL = 'short_cancel' + def __repr__(self): return self.value diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 10bf0a753..f711bc258 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -804,8 +804,14 @@ class Exchange: rate_for_order = self.price_to_precision(pair, rate) if needs_price else None self._lev_prep(pair, leverage) - order = self._api.create_order(pair, ordertype, side, - amount, rate_for_order, params) + order = self._api.create_order( + pair, + ordertype, + side, + amount, + rate_for_order, + params + ) self._log_exchange_response('create_order', order) return order diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ddb4b148f..bb7e06e8a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -7,7 +7,7 @@ import traceback from datetime import datetime, time, timezone from math import isclose from threading import Lock -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple import arrow from schedule import Scheduler @@ -17,7 +17,8 @@ from freqtrade.configuration import validate_config_consistency from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge -from freqtrade.enums import RPCMessageType, SellType, State, TradingMode +from freqtrade.enums import (Collateral, RPCMessageType, SellType, SignalDirection, State, + TradingMode) from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError, InvalidOrderException, PricingError) from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds @@ -101,14 +102,19 @@ class FreqtradeBot(LoggingMixin): initial_state = self.config.get('initial_state') self.state = State[initial_state.upper()] if initial_state else State.STOPPED - # Protect sell-logic from forcesell and vice versa + # Protect exit-logic from forcesell and vice versa self._exit_lock = Lock() LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe)) + self.trading_mode: TradingMode = TradingMode.SPOT + self.collateral_type: Optional[Collateral] = None + if 'trading_mode' in self.config: self.trading_mode = TradingMode(self.config['trading_mode']) - else: - self.trading_mode = TradingMode.SPOT + + if 'collateral_type' in self.config: + self.collateral_type = Collateral(self.config['collateral_type']) + self._schedule = Scheduler() if self.trading_mode == TradingMode.FUTURES: @@ -194,7 +200,7 @@ class FreqtradeBot(LoggingMixin): # Protect from collisions with forceexit. # Without this, freqtrade my try to recreate stoploss_on_exchange orders - # while selling is in process, since telegram messages arrive in an different thread. + # while exiting is in process, since telegram messages arrive in an different thread. with self._exit_lock: trades = Trade.get_open_trades() # First process current opened trades (positions) @@ -305,21 +311,26 @@ class FreqtradeBot(LoggingMixin): trades: List[Trade] = Trade.get_closed_trades_without_assigned_fees() for trade in trades: - - if not trade.is_open and not trade.fee_updated('sell'): + if not trade.is_open and not trade.fee_updated(trade.exit_side): # Get sell fee - order = trade.select_order('sell', False) + order = trade.select_order(trade.exit_side, False) if order: - logger.info(f"Updating sell-fee on trade {trade} for order {order.order_id}.") + logger.info( + f"Updating {trade.exit_side}-fee on trade {trade}" + f"for order {order.order_id}." + ) self.update_trade_state(trade, order.order_id, stoploss_order=order.ft_order_side == 'stoploss') trades: List[Trade] = Trade.get_open_trades_without_assigned_fees() for trade in trades: - if trade.is_open and not trade.fee_updated('buy'): - order = trade.select_order('buy', False) + if trade.is_open and not trade.fee_updated(trade.enter_side): + order = trade.select_order(trade.enter_side, False) if order: - logger.info(f"Updating buy-fee on trade {trade} for order {order.order_id}.") + logger.info( + f"Updating {trade.enter_side}-fee on trade {trade}" + f"for order {order.order_id}." + ) self.update_trade_state(trade, order.order_id) def handle_insufficient_funds(self, trade: Trade): @@ -327,8 +338,8 @@ class FreqtradeBot(LoggingMixin): Determine if we ever opened a exiting order for this trade. If not, try update entering fees - otherwise "refind" the open order we obviously lost. """ - sell_order = trade.select_order('sell', None) - if sell_order: + exit_order = trade.select_order(trade.exit_side, None) + if exit_order: self.refind_lost_order(trade) else: self.reupdate_enter_order_fees(trade) @@ -338,10 +349,11 @@ class FreqtradeBot(LoggingMixin): Get buy order from database, and try to reupdate. Handles trades where the initial fee-update did not work. """ - logger.info(f"Trying to reupdate buy fees for {trade}") - order = trade.select_order('buy', False) + logger.info(f"Trying to reupdate {trade.enter_side} fees for {trade}") + order = trade.select_order(trade.enter_side, False) if order: - logger.info(f"Updating buy-fee on trade {trade} for order {order.order_id}.") + logger.info( + f"Updating {trade.enter_side}-fee on trade {trade} for order {order.order_id}.") self.update_trade_state(trade, order.order_id) def refind_lost_order(self, trade): @@ -357,7 +369,7 @@ class FreqtradeBot(LoggingMixin): if not order.ft_is_open: logger.debug(f"Order {order} is no longer open.") continue - if order.ft_order_side == 'buy': + if order.ft_order_side == trade.enter_side: # Skip buy side - this is handled by reupdate_enter_order_fees continue try: @@ -367,7 +379,7 @@ class FreqtradeBot(LoggingMixin): if fo and fo['status'] == 'open': # Assume this as the open stoploss order trade.stoploss_order_id = order.order_id - elif order.ft_order_side == 'sell': + elif order.ft_order_side == trade.exit_side: if fo and fo['status'] == 'open': # Assume this as the open order trade.open_order_id = order.order_id @@ -456,7 +468,9 @@ class FreqtradeBot(LoggingMixin): # running get_signal on historical data fetched (signal, enter_tag) = self.strategy.get_entry_signal( - pair, self.strategy.timeframe, analyzed_df + pair, + self.strategy.timeframe, + analyzed_df ) if signal: @@ -465,19 +479,31 @@ class FreqtradeBot(LoggingMixin): bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {}) if ((bid_check_dom.get('enabled', False)) and (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): - # TODO-lev: Does the below need to be adjusted for shorts? - if self._check_depth_of_market_buy(pair, bid_check_dom): - # TODO-lev: pass in "enter" as side. - - return self.execute_entry(pair, stake_amount, enter_tag=enter_tag) + if self._check_depth_of_market(pair, bid_check_dom, side=signal): + return self.execute_entry( + pair, + stake_amount, + enter_tag=enter_tag, + is_short=(signal == SignalDirection.SHORT) + ) else: return False - return self.execute_entry(pair, stake_amount, enter_tag=enter_tag) + return self.execute_entry( + pair, + stake_amount, + enter_tag=enter_tag, + is_short=(signal == SignalDirection.SHORT) + ) else: return False - def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool: + def _check_depth_of_market( + self, + pair: str, + conf: Dict, + side: SignalDirection + ) -> bool: """ Checks depth of market before executing a buy """ @@ -487,9 +513,17 @@ class FreqtradeBot(LoggingMixin): order_book_data_frame = order_book_to_dataframe(order_book['bids'], order_book['asks']) order_book_bids = order_book_data_frame['b_size'].sum() order_book_asks = order_book_data_frame['a_size'].sum() - bids_ask_delta = order_book_bids / order_book_asks + + enter_side = order_book_bids if side == SignalDirection.LONG else order_book_asks + exit_side = order_book_asks if side == SignalDirection.LONG else order_book_bids + bids_ask_delta = enter_side / exit_side + + bids = f"Bids: {order_book_bids}" + asks = f"Asks: {order_book_asks}" + delta = f"Delta: {bids_ask_delta}" + logger.info( - f"Bids: {order_book_bids}, Asks: {order_book_asks}, Delta: {bids_ask_delta}, " + f"{bids}, {asks}, {delta}, Direction: {side.value}" f"Bid Price: {order_book['bids'][0][0]}, Ask Price: {order_book['asks'][0][0]}, " f"Immediate Bid Quantity: {order_book['bids'][0][1]}, " f"Immediate Ask Quantity: {order_book['asks'][0][1]}." @@ -501,21 +535,65 @@ class FreqtradeBot(LoggingMixin): logger.info(f"Bids to asks delta for {pair} does not satisfy condition.") return False - def execute_entry(self, pair: str, stake_amount: float, price: Optional[float] = None, - forcebuy: bool = False, enter_tag: Optional[str] = None) -> bool: + def leverage_prep( + self, + pair: str, + open_rate: float, + amount: float, + leverage: float, + is_short: bool + ) -> Tuple[float, Optional[float]]: + + interest_rate = 0.0 + isolated_liq = None + + # TODO-lev: Uncomment once liq and interest merged in + # if TradingMode == TradingMode.MARGIN: + # interest_rate = self.exchange.get_interest_rate( + # pair=pair, + # open_rate=open_rate, + # is_short=is_short + # ) + + # if self.collateral_type == Collateral.ISOLATED: + + # isolated_liq = liquidation_price( + # exchange_name=self.exchange.name, + # trading_mode=self.trading_mode, + # open_rate=open_rate, + # amount=amount, + # leverage=leverage, + # is_short=is_short + # ) + + return interest_rate, isolated_liq + + def execute_entry( + self, + pair: str, + stake_amount: float, + price: Optional[float] = None, + forcebuy: bool = False, + leverage: float = 1.0, + is_short: bool = False, + enter_tag: Optional[str] = None + ) -> bool: """ Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY :param stake_amount: amount of stake-currency for the pair + :param leverage: amount of leverage applied to this trade :return: True if a buy order is created, false if it fails. """ time_in_force = self.strategy.order_time_in_force['buy'] + [side, name] = ['sell', 'Short'] if is_short else ['buy', 'Long'] + if price: enter_limit_requested = price else: # Calculate price - proposed_enter_rate = self.exchange.get_rate(pair, refresh=True, side="buy") + proposed_enter_rate = self.exchange.get_rate(pair, refresh=True, side=side) custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, default_retval=proposed_enter_rate)( pair=pair, current_time=datetime.now(timezone.utc), @@ -524,10 +602,14 @@ class FreqtradeBot(LoggingMixin): enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate) if not enter_limit_requested: - raise PricingError('Could not determine buy price.') + raise PricingError(f'Could not determine {side} price.') - min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, enter_limit_requested, - self.strategy.stoploss) + min_stake_amount = self.exchange.get_min_pair_stake_amount( + pair, + enter_limit_requested, + self.strategy.stoploss, + leverage=leverage + ) if not self.edge: max_stake_amount = self.wallets.get_available_stake_amount() @@ -543,10 +625,12 @@ class FreqtradeBot(LoggingMixin): if not stake_amount: return False - logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: " - f"{stake_amount} ...") + logger.info( + f"{name} signal found: about create a new trade for {pair} with stake_amount: " + f"{stake_amount} ..." + ) - amount = stake_amount / enter_limit_requested + amount = (stake_amount / enter_limit_requested) * leverage order_type = self.strategy.order_types['buy'] if forcebuy: # Forcebuy can define a different ordertype @@ -558,15 +642,21 @@ class FreqtradeBot(LoggingMixin): if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)( pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested, time_in_force=time_in_force, current_time=datetime.now(timezone.utc), - side='long' + side='short' if is_short else 'long' ): logger.info(f"User requested abortion of buying {pair}") return False amount = self.exchange.amount_to_precision(pair, amount) - order = self.exchange.create_order(pair=pair, ordertype=order_type, side="buy", - amount=amount, rate=enter_limit_requested, - time_in_force=time_in_force) - order_obj = Order.parse_from_ccxt_object(order, pair, 'buy') + order = self.exchange.create_order( + pair=pair, + ordertype=order_type, + side=side, + amount=amount, + rate=enter_limit_requested, + time_in_force=time_in_force, + leverage=leverage + ) + order_obj = Order.parse_from_ccxt_object(order, pair, side) order_id = order['id'] order_status = order.get('status', None) @@ -579,17 +669,17 @@ class FreqtradeBot(LoggingMixin): # return false if the order is not filled if float(order['filled']) == 0: - logger.warning('Buy %s order with time in force %s for %s is %s by %s.' + logger.warning('%s %s order with time in force %s for %s is %s by %s.' ' zero amount is fulfilled.', - order_tif, order_type, pair, order_status, self.exchange.name) + name, order_tif, order_type, pair, order_status, self.exchange.name) return False else: # the order is partially fulfilled # in case of IOC orders we can check immediately # if the order is fulfilled fully or partially - logger.warning('Buy %s order with time in force %s for %s is %s by %s.' + logger.warning('%s %s order with time in force %s for %s is %s by %s.' ' %s amount fulfilled out of %s (%s remaining which is canceled).', - order_tif, order_type, pair, order_status, self.exchange.name, + name, order_tif, order_type, pair, order_status, self.exchange.name, order['filled'], order['amount'], order['remaining'] ) stake_amount = order['cost'] @@ -602,6 +692,14 @@ class FreqtradeBot(LoggingMixin): amount = safe_value_fallback(order, 'filled', 'amount') enter_limit_filled_price = safe_value_fallback(order, 'average', 'price') + interest_rate, isolated_liq = self.leverage_prep( + leverage=leverage, + pair=pair, + amount=amount, + open_rate=enter_limit_filled_price, + is_short=is_short + ) + # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker') open_date = datetime.now(timezone.utc) @@ -627,6 +725,10 @@ class FreqtradeBot(LoggingMixin): # TODO-lev: compatibility layer for buy_tag (!) buy_tag=enter_tag, timeframe=timeframe_to_minutes(self.config['timeframe']), + leverage=leverage, + is_short=is_short, + interest_rate=interest_rate, + isolated_liq=isolated_liq, trading_mode=self.trading_mode, funding_fees=funding_fees ) @@ -652,7 +754,7 @@ class FreqtradeBot(LoggingMixin): """ msg = { 'trade_id': trade.id, - 'type': RPCMessageType.BUY, + 'type': RPCMessageType.SHORT if trade.is_short else RPCMessageType.BUY, 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, @@ -673,11 +775,11 @@ class FreqtradeBot(LoggingMixin): """ Sends rpc notification when a entry order cancel occurred. """ - current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="buy") - + current_rate = self.exchange.get_rate(trade.pair, refresh=False, side=trade.enter_side) + msg_type = RPCMessageType.SHORT_CANCEL if trade.is_short else RPCMessageType.BUY_CANCEL msg = { 'trade_id': trade.id, - 'type': RPCMessageType.BUY_CANCEL, + 'type': msg_type, 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, @@ -696,9 +798,10 @@ class FreqtradeBot(LoggingMixin): self.rpc.send_msg(msg) def _notify_enter_fill(self, trade: Trade) -> None: + msg_type = RPCMessageType.SHORT_FILL if trade.is_short else RPCMessageType.BUY_FILL msg = { 'trade_id': trade.id, - 'type': RPCMessageType.BUY_FILL, + 'type': msg_type, 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, @@ -752,6 +855,7 @@ class FreqtradeBot(LoggingMixin): logger.debug('Handling %s ...', trade) (enter, exit_) = (False, False) + exit_signal_type = "exit_short" if trade.is_short else "exit_long" # TODO-lev: change to use_exit_signal, ignore_roi_if_enter_signal if (self.config.get('use_sell_signal', True) or @@ -762,15 +866,16 @@ class FreqtradeBot(LoggingMixin): (enter, exit_) = self.strategy.get_exit_signal( trade.pair, self.strategy.timeframe, - analyzed_df, is_short=trade.is_short + analyzed_df, + is_short=trade.is_short ) - # TODO-lev: side should depend on trade side. - exit_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell") + logger.debug('checking exit') + exit_rate = self.exchange.get_rate(trade.pair, refresh=True, side=trade.exit_side) if self._check_and_execute_exit(trade, exit_rate, enter, exit_): return True - logger.debug('Found no sell signal for %s.', trade) + logger.debug(f'Found no {exit_signal_type} signal for %s.', trade) return False def create_stoploss_order(self, trade: Trade, stop_price: float) -> bool: @@ -855,7 +960,10 @@ class FreqtradeBot(LoggingMixin): # If enter order is fulfilled but there is no stoploss, we add a stoploss on exchange if not stoploss_order: stoploss = self.edge.stoploss(pair=trade.pair) if self.edge else self.strategy.stoploss - stop_price = trade.open_rate * (1 + stoploss) + if trade.is_short: + stop_price = trade.open_rate * (1 - stoploss) + else: + stop_price = trade.open_rate * (1 + stoploss) if self.create_stoploss_order(trade=trade, stop_price=stop_price): trade.stoploss_last_update = datetime.utcnow() @@ -880,11 +988,11 @@ class FreqtradeBot(LoggingMixin): # if trailing stoploss is enabled we check if stoploss value has changed # in which case we cancel stoploss order and put another one with new # value immediately - self.handle_trailing_stoploss_on_exchange(trade, stoploss_order, side=trade.exit_side) + self.handle_trailing_stoploss_on_exchange(trade, stoploss_order) return False - def handle_trailing_stoploss_on_exchange(self, trade: Trade, order: dict, side: str) -> None: + def handle_trailing_stoploss_on_exchange(self, trade: Trade, order: dict) -> None: """ Check to see if stoploss on exchange should be updated in case of trailing stoploss on exchange @@ -892,7 +1000,7 @@ class FreqtradeBot(LoggingMixin): :param order: Current on exchange stoploss order :return: None """ - if self.exchange.stoploss_adjust(trade.stop_loss, order, side): + if self.exchange.stoploss_adjust(trade.stop_loss, order, side=trade.exit_side): # we check if the update is necessary update_beat = self.strategy.order_types.get('stoploss_on_exchange_interval', 60) if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() >= update_beat: @@ -918,7 +1026,11 @@ class FreqtradeBot(LoggingMixin): Check and execute trade exit """ should_exit: SellCheckTuple = self.strategy.should_exit( - trade, exit_rate, datetime.now(timezone.utc), enter=enter, exit_=exit_, + trade, + exit_rate, + datetime.now(timezone.utc), + enter=enter, + exit_=exit_, force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 ) @@ -959,24 +1071,23 @@ class FreqtradeBot(LoggingMixin): continue fully_cancelled = self.update_trade_state(trade, trade.open_order_id, order) + is_entering = order['side'] == trade.enter_side + not_closed = order['status'] == 'open' or fully_cancelled + side = trade.enter_side if is_entering else trade.exit_side + timed_out = self._check_timed_out(side, order) + time_method = 'check_sell_timeout' if order['side'] == 'sell' else 'check_buy_timeout' - if (order['side'] == 'buy' and (order['status'] == 'open' or fully_cancelled) and ( - fully_cancelled - or self._check_timed_out('buy', order) - or strategy_safe_wrapper(self.strategy.check_buy_timeout, - default_retval=False)(pair=trade.pair, - trade=trade, - order=order))): - self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT']) - - elif (order['side'] == 'sell' and (order['status'] == 'open' or fully_cancelled) and ( - fully_cancelled - or self._check_timed_out('sell', order) - or strategy_safe_wrapper(self.strategy.check_sell_timeout, - default_retval=False)(pair=trade.pair, - trade=trade, - order=order))): - self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT']) + if not_closed and (fully_cancelled or timed_out or ( + strategy_safe_wrapper(getattr(self.strategy, time_method), default_retval=False)( + pair=trade.pair, + trade=trade, + order=order + ) + )): + if is_entering: + self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT']) + else: + self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT']) def cancel_all_open_orders(self) -> None: """ @@ -991,10 +1102,10 @@ class FreqtradeBot(LoggingMixin): logger.info('Cannot query order for %s due to %s', trade, traceback.format_exc()) continue - if order['side'] == 'buy': + if order['side'] == trade.enter_side: self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) - elif order['side'] == 'sell': + elif order['side'] == trade.exit_side: self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) Trade.commit() @@ -1016,7 +1127,7 @@ class FreqtradeBot(LoggingMixin): if filled_val > 0 and filled_stake < minstake: logger.warning( f"Order {trade.open_order_id} for {trade.pair} not cancelled, " - f"as the filled amount of {filled_val} would result in an unsellable trade.") + f"as the filled amount of {filled_val} would result in an unexitable trade.") return False corder = self.exchange.cancel_order_with_result(trade.open_order_id, trade.pair, trade.amount) @@ -1031,12 +1142,16 @@ class FreqtradeBot(LoggingMixin): corder = order reason = constants.CANCEL_REASON['CANCELLED_ON_EXCHANGE'] - logger.info('Buy order %s for %s.', reason, trade) + side = trade.enter_side.capitalize() + logger.info('%s order %s for %s.', side, reason, trade) # Using filled to determine the filled amount filled_amount = safe_value_fallback2(corder, order, 'filled', 'filled') if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC): - logger.info('Buy order fully cancelled. Removing %s from database.', trade) + logger.info( + '%s order fully cancelled. Removing %s from database.', + side, trade + ) # if trade is not partially completed, just delete the trade trade.delete() was_trade_fully_canceled = True @@ -1054,11 +1169,11 @@ class FreqtradeBot(LoggingMixin): self.update_trade_state(trade, trade.open_order_id, corder) trade.open_order_id = None - logger.info('Partial buy order timeout for %s.', trade) + logger.info('Partial %s order timeout for %s.', trade.enter_side, trade) reason += f", {constants.CANCEL_REASON['PARTIALLY_FILLED']}" self.wallets.update() - self._notify_enter_cancel(trade, order_type=self.strategy.order_types['buy'], + self._notify_enter_cancel(trade, order_type=self.strategy.order_types[trade.enter_side], reason=reason) return was_trade_fully_canceled @@ -1076,12 +1191,13 @@ class FreqtradeBot(LoggingMixin): trade.amount) trade.update_order(co) except InvalidOrderException: - logger.exception(f"Could not cancel sell order {trade.open_order_id}") + logger.exception( + f"Could not cancel {trade.exit_side} order {trade.open_order_id}") return 'error cancelling order' - logger.info('Sell order %s for %s.', reason, trade) + logger.info('%s order %s for %s.', trade.exit_side.capitalize(), reason, trade) else: reason = constants.CANCEL_REASON['CANCELLED_ON_EXCHANGE'] - logger.info('Sell order %s for %s.', reason, trade) + logger.info('%s order %s for %s.', trade.exit_side.capitalize(), reason, trade) trade.update_order(order) trade.close_rate = None @@ -1098,7 +1214,7 @@ class FreqtradeBot(LoggingMixin): self.wallets.update() self._notify_exit_cancel( trade, - order_type=self.strategy.order_types['sell'], + order_type=self.strategy.order_types[trade.exit_side], reason=reason ) return reason @@ -1129,7 +1245,12 @@ class FreqtradeBot(LoggingMixin): raise DependencyException( f"Not enough amount to exit trade. Trade-amount: {amount}, Wallet: {wallet_amount}") - def execute_trade_exit(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool: + def execute_trade_exit( + self, + trade: Trade, + limit: float, + sell_reason: SellCheckTuple, # TODO-lev update to exit_reason + ) -> bool: """ Executes a trade exit for the given trade and limit :param trade: Trade instance @@ -1137,13 +1258,13 @@ class FreqtradeBot(LoggingMixin): :param sell_reason: Reason the sell was triggered :return: True if it succeeds (supported) False (not supported) """ - sell_type = 'sell' # TODO-lev: Update to exit + exit_type = 'sell' # TODO-lev: Update to exit if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): - sell_type = 'stoploss' + exit_type = 'stoploss' # if stoploss is on exchange and we are on dry_run mode, # we consider the sell price stop price - if self.config['dry_run'] and sell_type == 'stoploss' \ + if self.config['dry_run'] and exit_type == 'stoploss' \ and self.strategy.order_types['stoploss_on_exchange']: limit = trade.stop_loss @@ -1167,7 +1288,7 @@ class FreqtradeBot(LoggingMixin): except InvalidOrderException: logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}") - order_type = self.strategy.order_types[sell_type] + order_type = self.strategy.order_types[exit_type] if sell_reason.sell_type == SellType.EMERGENCY_SELL: # Emergency sells (default to market!) order_type = self.strategy.order_types.get("emergencysell", "market") @@ -1177,7 +1298,7 @@ class FreqtradeBot(LoggingMixin): order_type = self.strategy.order_types.get("forcesell", order_type) amount = self._safe_exit_amount(trade.pair, trade.amount) - time_in_force = self.strategy.order_time_in_force['sell'] + time_in_force = self.strategy.order_time_in_force['sell'] # TODO-lev update to exit if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit, @@ -1191,7 +1312,7 @@ class FreqtradeBot(LoggingMixin): order = self.exchange.create_order( pair=trade.pair, ordertype=order_type, - side="sell", + side=trade.exit_side, amount=amount, rate=limit, time_in_force=time_in_force @@ -1202,7 +1323,7 @@ class FreqtradeBot(LoggingMixin): self.handle_insufficient_funds(trade) return False - order_obj = Order.parse_from_ccxt_object(order, trade.pair, 'sell') + order_obj = Order.parse_from_ccxt_object(order, trade.pair, trade.exit_side) trade.orders.append(order_obj) trade.open_order_id = order['id'] @@ -1230,7 +1351,7 @@ class FreqtradeBot(LoggingMixin): profit_trade = trade.calc_profit(rate=profit_rate) # Use cached rates here - it was updated seconds ago. current_rate = self.exchange.get_rate( - trade.pair, refresh=False, side="sell") if not fill else None + trade.pair, refresh=False, side=trade.exit_side) if not fill else None profit_ratio = trade.calc_profit_ratio(profit_rate) gain = "profit" if profit_ratio > 0 else "loss" @@ -1275,7 +1396,7 @@ class FreqtradeBot(LoggingMixin): profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested profit_trade = trade.calc_profit(rate=profit_rate) - current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="sell") + current_rate = self.exchange.get_rate(trade.pair, refresh=False, side=trade.exit_side) profit_ratio = trade.calc_profit_ratio(profit_rate) gain = "profit" if profit_ratio > 0 else "loss" @@ -1390,7 +1511,7 @@ class FreqtradeBot(LoggingMixin): self.wallets.update() if fee_abs != 0 and self.wallets.get_free(trade_base_currency) >= amount: # Eat into dust if we own more than base currency - # TODO-lev: won't be in "base"(quote) currency for shorts + # TODO-lev: won't be in base currency for shorts logger.info(f"Fee amount for {trade} was in base currency - " f"Eating Fee {fee_abs} into dust.") elif fee_abs != 0: diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index bbb390e75..5496628f4 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -506,7 +506,6 @@ class LocalTrade(): lower_stop = new_loss < self.stop_loss # stop losses only walk up, never down!, - # TODO-lev # ? But adding more to a leveraged trade would create a lower liquidation price, # ? decreasing the minimum stoploss if (higher_stop and not self.is_short) or (lower_stop and self.is_short): diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index a22a0b6b8..94541218c 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -840,28 +840,32 @@ class IStrategy(ABC, HyperStrategyMixin): else: logger.warning("CustomStoploss function did not return valid stoploss") - if self.trailing_stop and trade.stop_loss < (low or current_rate): + sl_lower_long = (trade.stop_loss < (low or current_rate) and not trade.is_short) + sl_higher_short = (trade.stop_loss > (high or current_rate) and trade.is_short) + if self.trailing_stop and (sl_lower_long or sl_higher_short): # trailing stoploss handling sl_offset = self.trailing_stop_positive_offset # Make sure current_profit is calculated using high for backtesting. - # TODO-lev: Check this function - high / low usage must be inversed for short trades! - high_profit = current_profit if not high else trade.calc_profit_ratio(high) + bound = low if trade.is_short else high + bound_profit = current_profit if not bound else trade.calc_profit_ratio(bound) # Don't update stoploss if trailing_only_offset_is_reached is true. - if not (self.trailing_only_offset_is_reached and high_profit < sl_offset): + if not (self.trailing_only_offset_is_reached and bound_profit < sl_offset): # Specific handling for trailing_stop_positive - if self.trailing_stop_positive is not None and high_profit > sl_offset: + if self.trailing_stop_positive is not None and bound_profit > sl_offset: stop_loss_value = self.trailing_stop_positive logger.debug(f"{trade.pair} - Using positive stoploss: {stop_loss_value} " f"offset: {sl_offset:.4g} profit: {current_profit:.4f}%") - trade.adjust_stop_loss(high or current_rate, stop_loss_value) + trade.adjust_stop_loss(bound or current_rate, stop_loss_value) + sl_higher_short = (trade.stop_loss >= (low or current_rate) and not trade.is_short) + sl_lower_long = ((trade.stop_loss <= (high or current_rate) and trade.is_short)) # evaluate if the stoploss was hit if stoploss is not on exchange # in Dry-Run, this handles stoploss logic as well, as the logic will not be different to # regular stoploss handling. - if ((trade.stop_loss >= (low or current_rate)) and + if ((sl_higher_short or sl_lower_long) and (not self.order_types.get('stoploss_on_exchange') or self.config['dry_run'])): sell_type = SellType.STOP_LOSS @@ -870,12 +874,18 @@ class IStrategy(ABC, HyperStrategyMixin): if trade.initial_stop_loss != trade.stop_loss: sell_type = SellType.TRAILING_STOP_LOSS logger.debug( - f"{trade.pair} - HIT STOP: current price at {(low or current_rate):.6f}, " + f"{trade.pair} - HIT STOP: current price at " + f"{((high if trade.is_short else low) or current_rate):.6f}, " f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at {trade.initial_stop_loss:.6f}, " f"trade opened at {trade.open_rate:.6f}") + new_stoploss = ( + trade.stop_loss + trade.initial_stop_loss + if trade.is_short else + trade.stop_loss - trade.initial_stop_loss + ) logger.debug(f"{trade.pair} - Trailing stop saved " - f"{trade.stop_loss - trade.initial_stop_loss:.6f}") + f"{new_stoploss:.6f}") return SellCheckTuple(sell_type=sell_type) diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index 80fa7cdae..13df9c2a8 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -58,6 +58,8 @@ class SampleStrategy(IStrategy): # Hyperoptable parameters buy_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True) sell_rsi = IntParameter(low=50, high=100, default=70, space='sell', optimize=True, load=True) + short_rsi = IntParameter(low=51, high=100, default=70, space='sell', optimize=True, load=True) + exit_short_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True) # Optimal timeframe for the strategy. timeframe = '5m' @@ -354,6 +356,16 @@ class SampleStrategy(IStrategy): ), 'enter_long'] = 1 + dataframe.loc[ + ( + # Signal: RSI crosses above 70 + (qtpylib.crossed_above(dataframe['rsi'], self.short_rsi.value)) & + (dataframe['tema'] > dataframe['bb_middleband']) & # Guard: tema above BB middle + (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'enter_short'] = 1 + return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -371,5 +383,18 @@ class SampleStrategy(IStrategy): (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling (dataframe['volume'] > 0) # Make sure Volume is not 0 ), + 'exit_long'] = 1 + + dataframe.loc[ + ( + # Signal: RSI crosses above 30 + (qtpylib.crossed_above(dataframe['rsi'], self.exit_short_rsi.value)) & + # Guard: tema below BB middle + (dataframe['tema'] <= dataframe['bb_middleband']) & + (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard: tema is raising + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'exit_short'] = 1 + return dataframe diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 4e07e299c..d8218c82a 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -903,7 +903,7 @@ def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results, tmpdir): mocker.patch( 'freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist', return_value=True - ) + ) def fake_iterator(*args, **kwargs): yield from [saved_hyperopt_results] @@ -1309,9 +1309,10 @@ def test_start_list_data(testdatadir, capsys): @pytest.mark.usefixtures("init_persistence") +# TODO-lev: Short trades? def test_show_trades(mocker, fee, capsys, caplog): mocker.patch("freqtrade.persistence.init_db") - create_mock_trades(fee) + create_mock_trades(fee, False) args = [ "show-trades", "--db-url", diff --git a/tests/conftest.py b/tests/conftest.py index 7eec8d171..2c6297d57 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -209,8 +209,14 @@ def get_patched_worker(mocker, config) -> Worker: return Worker(args=None, config=config) -def patch_get_signal(freqtrade: FreqtradeBot, enter_long=True, exit_long=False, - enter_short=False, exit_short=False, enter_tag: Optional[str] = None) -> None: +def patch_get_signal( + freqtrade: FreqtradeBot, + enter_long=True, + exit_long=False, + enter_short=False, + exit_short=False, + enter_tag: Optional[str] = None +) -> None: """ :param mocker: mocker to patch IStrategy class :param value: which value IStrategy.get_signal() must return @@ -241,7 +247,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, enter_long=True, exit_long=False, freqtrade.exchange.refresh_latest_ohlcv = lambda p: None -def create_mock_trades(fee, use_db: bool = True): +def create_mock_trades(fee, is_short: bool, use_db: bool = True): """ Create some fake trades ... """ @@ -252,22 +258,22 @@ def create_mock_trades(fee, use_db: bool = True): LocalTrade.add_bt_trade(trade) # Simulate dry_run entries - trade = mock_trade_1(fee) + trade = mock_trade_1(fee, is_short) add_trade(trade) - trade = mock_trade_2(fee) + trade = mock_trade_2(fee, is_short) add_trade(trade) - trade = mock_trade_3(fee) + trade = mock_trade_3(fee, is_short) add_trade(trade) - trade = mock_trade_4(fee) + trade = mock_trade_4(fee, is_short) add_trade(trade) - trade = mock_trade_5(fee) + trade = mock_trade_5(fee, is_short) add_trade(trade) - trade = mock_trade_6(fee) + trade = mock_trade_6(fee, is_short) add_trade(trade) if use_db: @@ -286,22 +292,22 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True): LocalTrade.add_bt_trade(trade) # Simulate dry_run entries - trade = mock_trade_1(fee) + trade = mock_trade_1(fee, False) add_trade(trade) - trade = mock_trade_2(fee) + trade = mock_trade_2(fee, False) add_trade(trade) - trade = mock_trade_3(fee) + trade = mock_trade_3(fee, False) add_trade(trade) - trade = mock_trade_4(fee) + trade = mock_trade_4(fee, False) add_trade(trade) - trade = mock_trade_5(fee) + trade = mock_trade_5(fee, False) add_trade(trade) - trade = mock_trade_6(fee) + trade = mock_trade_6(fee, False) add_trade(trade) trade = short_trade(fee) @@ -324,7 +330,7 @@ def create_mock_trades_usdt(fee, use_db: bool = True): else: LocalTrade.add_bt_trade(trade) - # Simulate dry_run entries + # Simulate dry_run entries trade = mock_trade_usdt_1(fee) add_trade(trade) @@ -2297,6 +2303,7 @@ def limit_sell_order_usdt_open(): 'timestamp': arrow.utcnow().int_timestamp, 'price': 2.20, 'amount': 30.0, + 'cost': 66.0, 'filled': 0.0, 'remaining': 30.0, 'status': 'open' @@ -2342,3 +2349,27 @@ def market_sell_order_usdt(): 'remaining': 0.0, 'status': 'closed' } + + +@pytest.fixture(scope='function') +def limit_order(limit_buy_order_usdt, limit_sell_order_usdt): + return { + 'buy': limit_buy_order_usdt, + 'sell': limit_sell_order_usdt + } + + +@pytest.fixture(scope='function') +def market_order(market_buy_order_usdt, market_sell_order_usdt): + return { + 'buy': market_buy_order_usdt, + 'sell': market_sell_order_usdt + } + + +@pytest.fixture(scope='function') +def limit_order_open(limit_buy_order_usdt_open, limit_sell_order_usdt_open): + return { + 'buy': limit_buy_order_usdt_open, + 'sell': limit_sell_order_usdt_open + } diff --git a/tests/conftest_trades.py b/tests/conftest_trades.py index cf3c970f6..fe88064a3 100644 --- a/tests/conftest_trades.py +++ b/tests/conftest_trades.py @@ -6,12 +6,24 @@ from freqtrade.persistence.models import Order, Trade MOCK_TRADE_COUNT = 6 -def mock_order_1(): +def enter_side(is_short: bool): + return "sell" if is_short else "buy" + + +def exit_side(is_short: bool): + return "buy" if is_short else "sell" + + +def direc(is_short: bool): + return "short" if is_short else "long" + + +def mock_order_1(is_short: bool): return { - 'id': '1234', + 'id': f'1234_{direc(is_short)}', 'symbol': 'ETH/BTC', 'status': 'closed', - 'side': 'buy', + 'side': enter_side(is_short), 'type': 'limit', 'price': 0.123, 'amount': 123.0, @@ -20,7 +32,7 @@ def mock_order_1(): } -def mock_trade_1(fee): +def mock_trade_1(fee, is_short: bool): trade = Trade( pair='ETH/BTC', stake_amount=0.001, @@ -32,21 +44,22 @@ def mock_trade_1(fee): open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=17), open_rate=0.123, exchange='binance', - open_order_id='dry_run_buy_12345', + open_order_id=f'dry_run_buy_{direc(is_short)}_12345', strategy='StrategyTestV3', timeframe=5, + is_short=is_short ) - o = Order.parse_from_ccxt_object(mock_order_1(), 'ETH/BTC', 'buy') + o = Order.parse_from_ccxt_object(mock_order_1(is_short), 'ETH/BTC', enter_side(is_short)) trade.orders.append(o) return trade -def mock_order_2(): +def mock_order_2(is_short: bool): return { - 'id': '1235', + 'id': f'1235_{direc(is_short)}', 'symbol': 'ETC/BTC', 'status': 'closed', - 'side': 'buy', + 'side': enter_side(is_short), 'type': 'limit', 'price': 0.123, 'amount': 123.0, @@ -55,12 +68,12 @@ def mock_order_2(): } -def mock_order_2_sell(): +def mock_order_2_sell(is_short: bool): return { - 'id': '12366', + 'id': f'12366_{direc(is_short)}', 'symbol': 'ETC/BTC', 'status': 'closed', - 'side': 'sell', + 'side': exit_side(is_short), 'type': 'limit', 'price': 0.128, 'amount': 123.0, @@ -69,7 +82,7 @@ def mock_order_2_sell(): } -def mock_trade_2(fee): +def mock_trade_2(fee, is_short: bool): """ Closed trade... """ @@ -82,30 +95,31 @@ def mock_trade_2(fee): fee_close=fee.return_value, open_rate=0.123, close_rate=0.128, - close_profit=0.005, - close_profit_abs=0.000584127, + close_profit=-0.005 if is_short else 0.005, + close_profit_abs=-0.005584127 if is_short else 0.000584127, exchange='binance', is_open=False, - open_order_id='dry_run_sell_12345', + open_order_id=f'dry_run_sell_{direc(is_short)}_12345', strategy='StrategyTestV3', timeframe=5, sell_reason='sell_signal', open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), + is_short=is_short ) - o = Order.parse_from_ccxt_object(mock_order_2(), 'ETC/BTC', 'buy') + o = Order.parse_from_ccxt_object(mock_order_2(is_short), 'ETC/BTC', enter_side(is_short)) trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_2_sell(), 'ETC/BTC', 'sell') + o = Order.parse_from_ccxt_object(mock_order_2_sell(is_short), 'ETC/BTC', exit_side(is_short)) trade.orders.append(o) return trade -def mock_order_3(): +def mock_order_3(is_short: bool): return { - 'id': '41231a12a', + 'id': f'41231a12a_{direc(is_short)}', 'symbol': 'XRP/BTC', 'status': 'closed', - 'side': 'buy', + 'side': enter_side(is_short), 'type': 'limit', 'price': 0.05, 'amount': 123.0, @@ -114,12 +128,12 @@ def mock_order_3(): } -def mock_order_3_sell(): +def mock_order_3_sell(is_short: bool): return { - 'id': '41231a666a', + 'id': f'41231a666a_{direc(is_short)}', 'symbol': 'XRP/BTC', 'status': 'closed', - 'side': 'sell', + 'side': exit_side(is_short), 'type': 'stop_loss_limit', 'price': 0.06, 'average': 0.06, @@ -129,7 +143,7 @@ def mock_order_3_sell(): } -def mock_trade_3(fee): +def mock_trade_3(fee, is_short: bool): """ Closed trade """ @@ -142,8 +156,8 @@ def mock_trade_3(fee): fee_close=fee.return_value, open_rate=0.05, close_rate=0.06, - close_profit=0.01, - close_profit_abs=0.000155, + close_profit=-0.01 if is_short else 0.01, + close_profit_abs=-0.001155 if is_short else 0.000155, exchange='binance', is_open=False, strategy='StrategyTestV3', @@ -151,20 +165,21 @@ def mock_trade_3(fee): sell_reason='roi', open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), close_date=datetime.now(tz=timezone.utc), + is_short=is_short ) - o = Order.parse_from_ccxt_object(mock_order_3(), 'XRP/BTC', 'buy') + o = Order.parse_from_ccxt_object(mock_order_3(is_short), 'XRP/BTC', enter_side(is_short)) trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_3_sell(), 'XRP/BTC', 'sell') + o = Order.parse_from_ccxt_object(mock_order_3_sell(is_short), 'XRP/BTC', exit_side(is_short)) trade.orders.append(o) return trade -def mock_order_4(): +def mock_order_4(is_short: bool): return { - 'id': 'prod_buy_12345', + 'id': f'prod_buy_{direc(is_short)}_12345', 'symbol': 'ETC/BTC', 'status': 'open', - 'side': 'buy', + 'side': enter_side(is_short), 'type': 'limit', 'price': 0.123, 'amount': 123.0, @@ -173,7 +188,7 @@ def mock_order_4(): } -def mock_trade_4(fee): +def mock_trade_4(fee, is_short: bool): """ Simulate prod entry """ @@ -188,21 +203,22 @@ def mock_trade_4(fee): is_open=True, open_rate=0.123, exchange='binance', - open_order_id='prod_buy_12345', + open_order_id=f'prod_buy_{direc(is_short)}_12345', strategy='StrategyTestV3', timeframe=5, + is_short=is_short ) - o = Order.parse_from_ccxt_object(mock_order_4(), 'ETC/BTC', 'buy') + o = Order.parse_from_ccxt_object(mock_order_4(is_short), 'ETC/BTC', enter_side(is_short)) trade.orders.append(o) return trade -def mock_order_5(): +def mock_order_5(is_short: bool): return { - 'id': 'prod_buy_3455', + 'id': f'prod_buy_{direc(is_short)}_3455', 'symbol': 'XRP/BTC', 'status': 'closed', - 'side': 'buy', + 'side': enter_side(is_short), 'type': 'limit', 'price': 0.123, 'amount': 123.0, @@ -211,12 +227,12 @@ def mock_order_5(): } -def mock_order_5_stoploss(): +def mock_order_5_stoploss(is_short: bool): return { - 'id': 'prod_stoploss_3455', + 'id': f'prod_stoploss_{direc(is_short)}_3455', 'symbol': 'XRP/BTC', 'status': 'open', - 'side': 'sell', + 'side': exit_side(is_short), 'type': 'stop_loss_limit', 'price': 0.123, 'amount': 123.0, @@ -225,7 +241,7 @@ def mock_order_5_stoploss(): } -def mock_trade_5(fee): +def mock_trade_5(fee, is_short: bool): """ Simulate prod entry with stoploss """ @@ -241,22 +257,23 @@ def mock_trade_5(fee): open_rate=0.123, exchange='binance', strategy='SampleStrategy', - stoploss_order_id='prod_stoploss_3455', + stoploss_order_id=f'prod_stoploss_{direc(is_short)}_3455', timeframe=5, + is_short=is_short ) - o = Order.parse_from_ccxt_object(mock_order_5(), 'XRP/BTC', 'buy') + o = Order.parse_from_ccxt_object(mock_order_5(is_short), 'XRP/BTC', enter_side(is_short)) trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_5_stoploss(), 'XRP/BTC', 'stoploss') + o = Order.parse_from_ccxt_object(mock_order_5_stoploss(is_short), 'XRP/BTC', 'stoploss') trade.orders.append(o) return trade -def mock_order_6(): +def mock_order_6(is_short: bool): return { - 'id': 'prod_buy_6', + 'id': f'prod_buy_{direc(is_short)}_6', 'symbol': 'LTC/BTC', 'status': 'closed', - 'side': 'buy', + 'side': enter_side(is_short), 'type': 'limit', 'price': 0.15, 'amount': 2.0, @@ -265,23 +282,23 @@ def mock_order_6(): } -def mock_order_6_sell(): +def mock_order_6_sell(is_short: bool): return { - 'id': 'prod_sell_6', + 'id': f'prod_sell_{direc(is_short)}_6', 'symbol': 'LTC/BTC', 'status': 'open', - 'side': 'sell', + 'side': exit_side(is_short), 'type': 'limit', - 'price': 0.20, + 'price': 0.15 if is_short else 0.20, 'amount': 2.0, 'filled': 0.0, 'remaining': 2.0, } -def mock_trade_6(fee): +def mock_trade_6(fee, is_short: bool): """ - Simulate prod entry with open sell order + Simulate prod entry with open exit order """ trade = Trade( pair='LTC/BTC', @@ -295,12 +312,12 @@ def mock_trade_6(fee): open_rate=0.15, exchange='binance', strategy='SampleStrategy', - open_order_id="prod_sell_6", + open_order_id=f"prod_sell_{direc(is_short)}_6", timeframe=5, ) - o = Order.parse_from_ccxt_object(mock_order_6(), 'LTC/BTC', 'buy') + o = Order.parse_from_ccxt_object(mock_order_6(is_short), 'LTC/BTC', enter_side(is_short)) trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_6_sell(), 'LTC/BTC', 'sell') + o = Order.parse_from_ccxt_object(mock_order_6_sell(is_short), 'LTC/BTC', exit_side(is_short)) trade.orders.append(o) return trade diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index e7b8c5b2f..94cea62eb 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -111,9 +111,10 @@ def test_load_backtest_data_multi(testdatadir): @pytest.mark.usefixtures("init_persistence") -def test_load_trades_from_db(default_conf, fee, mocker): +@pytest.mark.parametrize('is_short', [False, True]) +def test_load_trades_from_db(default_conf, fee, is_short, mocker): - create_mock_trades(fee) + create_mock_trades(fee, is_short) # remove init so it does not init again init_mock = mocker.patch('freqtrade.data.btanalysis.init_db', MagicMock()) diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py index f0033b2d7..641d2f263 100644 --- a/tests/exchange/test_kraken.py +++ b/tests/exchange/test_kraken.py @@ -164,6 +164,8 @@ def test_get_balances_prod(default_conf, mocker): ccxt_exceptionhandlers(mocker, default_conf, api_mock, "kraken", "get_balances", "fetch_balance") +# TODO-lev: All these stoploss tests with shorts + @pytest.mark.parametrize('ordertype', ['market', 'limit']) @pytest.mark.parametrize('side,adjustedprice', [ diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index c6246dccb..93eebde82 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -679,7 +679,7 @@ def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee, caplog) -> None assert pm.whitelist == ['ETH/BTC', 'TKN/BTC', 'XRP/BTC'] with time_machine.travel("2021-09-01 05:00:00 +00:00") as t: - create_mock_trades(fee) + create_mock_trades(fee, False) pm.refresh_pairlist() assert pm.whitelist == ['XRP/BTC'] assert log_has_re(r'Removing pair .* since .* is below .*', caplog) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 2f8fb9b85..0d349abf3 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -289,7 +289,8 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, rpc._rpc_daily_profit(0, stake_currency, fiat_display_currency) -def test_rpc_trade_history(mocker, default_conf, markets, fee): +@pytest.mark.parametrize('is_short', [True, False]) +def test_rpc_trade_history(mocker, default_conf, markets, fee, is_short): mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -297,7 +298,7 @@ def test_rpc_trade_history(mocker, default_conf, markets, fee): ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - create_mock_trades(fee) + create_mock_trades(fee, is_short) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() trades = rpc._rpc_trade_history(2) @@ -314,7 +315,8 @@ def test_rpc_trade_history(mocker, default_conf, markets, fee): assert trades['trades'][0]['pair'] == 'XRP/BTC' -def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog): +@pytest.mark.parametrize('is_short', [True, False]) +def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short): mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) stoploss_mock = MagicMock() cancel_mock = MagicMock() @@ -327,7 +329,7 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog): freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot.strategy.order_types['stoploss_on_exchange'] = True - create_mock_trades(fee) + create_mock_trades(fee, is_short) rpc = RPC(freqtradebot) with pytest.raises(RPCException, match='invalid argument'): rpc._rpc_delete('200') diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 18a84a19e..c9ffb4f34 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -458,7 +458,8 @@ def test_api_balance(botclient, mocker, rpc_balance, tickers): assert 'starting_capital_ratio' in response -def test_api_count(botclient, mocker, ticker, fee, markets): +@pytest.mark.parametrize('is_short', [True, False]) +def test_api_count(botclient, mocker, ticker, fee, markets, is_short): ftbot, client = botclient patch_get_signal(ftbot) mocker.patch.multiple( @@ -475,7 +476,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets): assert rc.json()["max"] == 1 # Create some test data - create_mock_trades(fee) + create_mock_trades(fee, is_short) rc = client_get(client, f"{BASE_URI}/count") assert_response(rc) assert rc.json()["current"] == 4 @@ -556,7 +557,8 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): assert rc.json()['data'][0]['date'] == str(datetime.utcnow().date()) -def test_api_trades(botclient, mocker, fee, markets): +@pytest.mark.parametrize('is_short', [True, False]) +def test_api_trades(botclient, mocker, fee, markets, is_short): ftbot, client = botclient patch_get_signal(ftbot) mocker.patch.multiple( @@ -569,7 +571,7 @@ def test_api_trades(botclient, mocker, fee, markets): assert rc.json()['trades_count'] == 0 assert rc.json()['total_trades'] == 0 - create_mock_trades(fee) + create_mock_trades(fee, is_short) Trade.query.session.flush() rc = client_get(client, f"{BASE_URI}/trades") @@ -584,6 +586,7 @@ def test_api_trades(botclient, mocker, fee, markets): assert rc.json()['total_trades'] == 2 +# TODO-lev: @pytest.mark.parametrize('is_short', [True, False]) def test_api_trade_single(botclient, mocker, fee, ticker, markets): ftbot, client = botclient patch_get_signal(ftbot) @@ -596,7 +599,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): assert_response(rc, 404) assert rc.json()['detail'] == 'Trade not found.' - create_mock_trades(fee) + create_mock_trades(fee, False) Trade.query.session.flush() rc = client_get(client, f"{BASE_URI}/trade/3") @@ -604,6 +607,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): assert rc.json()['trade_id'] == 3 +# TODO-lev: @pytest.mark.parametrize('is_short', [True, False]) def test_api_delete_trade(botclient, mocker, fee, markets): ftbot, client = botclient patch_get_signal(ftbot) @@ -619,7 +623,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): # Error - trade won't exist yet. assert_response(rc, 502) - create_mock_trades(fee) + create_mock_trades(fee, False) ftbot.strategy.order_types['stoploss_on_exchange'] = True trades = Trade.query.all() @@ -695,6 +699,7 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") +# TODO-lev: @pytest.mark.parametrize('is_short', [True, False]) def test_api_profit(botclient, mocker, ticker, fee, markets): ftbot, client = botclient patch_get_signal(ftbot) @@ -710,7 +715,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): assert_response(rc, 200) assert rc.json()['trade_count'] == 0 - create_mock_trades(fee) + create_mock_trades(fee, False) # Simulate fulfilled LIMIT_BUY order for trade rc = client_get(client, f"{BASE_URI}/profit") @@ -746,7 +751,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") -def test_api_stats(botclient, mocker, ticker, fee, markets,): +# TODO-lev: @pytest.mark.parametrize('is_short', [True, False]) +def test_api_stats(botclient, mocker, ticker, fee, markets): ftbot, client = botclient patch_get_signal(ftbot) mocker.patch.multiple( @@ -762,7 +768,7 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,): assert 'durations' in rc.json() assert 'sell_reasons' in rc.json() - create_mock_trades(fee) + create_mock_trades(fee, False) rc = client_get(client, f"{BASE_URI}/stats") assert_response(rc, 200) @@ -820,6 +826,10 @@ def test_api_performance(botclient, fee): {'count': 1, 'pair': 'XRP/ETH', 'profit': -5.57, 'profit_abs': -0.1150375}] +# TODO-lev: @pytest.mark.parametrize('is_short,side', [ +# (True, "short"), +# (False, "long") +# ]) def test_api_status(botclient, mocker, ticker, fee, markets): ftbot, client = botclient patch_get_signal(ftbot) @@ -835,7 +845,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): rc = client_get(client, f"{BASE_URI}/status") assert_response(rc, 200) assert rc.json() == [] - create_mock_trades(fee) + create_mock_trades(fee, False) rc = client_get(client, f"{BASE_URI}/status") assert_response(rc) @@ -888,7 +898,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'is_open': True, 'max_rate': ANY, 'min_rate': ANY, - 'open_order_id': 'dry_run_buy_12345', + 'open_order_id': 'dry_run_buy_long_12345', 'open_rate_requested': ANY, 'open_trade_value': 15.1668225, 'sell_reason': None, diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 791797ceb..c81fda101 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -33,6 +33,7 @@ class DummyCls(Telegram): """ Dummy class for testing the Telegram @authorized_only decorator """ + def __init__(self, rpc: RPC, config) -> None: super().__init__(rpc, config) self.state = {'called': False} @@ -479,8 +480,9 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, assert '*Best Performing:* `ETH/BTC: 6.20%`' in msg_mock.call_args_list[-1][0][0] +@pytest.mark.parametrize('is_short', [True, False]) def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, mocker) -> None: + limit_buy_order, limit_sell_order, mocker, is_short) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -496,7 +498,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, msg_mock.reset_mock() # Create some test data - create_mock_trades(fee) + create_mock_trades(fee, is_short) telegram._stats(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -997,9 +999,9 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: msg = ('
  current    max    total stake\n---------  -----  -------------\n'
            '        1      {}          {}
').format( - default_conf['max_open_trades'], - default_conf['stake_amount'] - ) + default_conf['max_open_trades'], + default_conf['stake_amount'] + ) assert msg in msg_mock.call_args_list[0][0][0] @@ -1159,6 +1161,7 @@ def test_edge_enabled(edge_conf, update, mocker) -> None: assert 'Winrate' not in msg_mock.call_args_list[0][0][0] +# TODO-lev: @pytest.mark.parametrize('is_short', [True, False]) def test_telegram_trades(mocker, update, default_conf, fee): telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) @@ -1177,7 +1180,7 @@ def test_telegram_trades(mocker, update, default_conf, fee): assert "
" not in msg_mock.call_args_list[0][0][0]
     msg_mock.reset_mock()
 
-    create_mock_trades(fee)
+    create_mock_trades(fee, False)
 
     context = MagicMock()
     context.args = [5]
@@ -1191,6 +1194,7 @@ def test_telegram_trades(mocker, update, default_conf, fee):
                 msg_mock.call_args_list[0][0][0]))
 
 
+# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
 def test_telegram_delete_trade(mocker, update, default_conf, fee):
 
     telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
@@ -1201,7 +1205,7 @@ def test_telegram_delete_trade(mocker, update, default_conf, fee):
     assert "Trade-id not set." in msg_mock.call_args_list[0][0][0]
 
     msg_mock.reset_mock()
-    create_mock_trades(fee)
+    create_mock_trades(fee, False)
 
     context = MagicMock()
     context.args = [1]
diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py
index a9334c616..e735ab181 100644
--- a/tests/strategy/test_interface.py
+++ b/tests/strategy/test_interface.py
@@ -47,8 +47,8 @@ def test_returns_latest_signal(ohlcv_history):
     mocked_history.loc[1, 'exit_long'] = 0
     mocked_history.loc[1, 'enter_long'] = 1
 
-    assert _STRATEGY.get_entry_signal('ETH/BTC', '5m', mocked_history
-                                      ) == (SignalDirection.LONG, None)
+    assert _STRATEGY.get_entry_signal(
+        'ETH/BTC', '5m', mocked_history) == (SignalDirection.LONG, None)
     assert _STRATEGY.get_exit_signal('ETH/BTC', '5m', mocked_history) == (True, False)
     assert _STRATEGY.get_exit_signal('ETH/BTC', '5m', mocked_history, True) == (False, False)
     mocked_history.loc[1, 'exit_long'] = 0
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index b77c42d15..6d784d9d1 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -11,7 +11,7 @@ import arrow
 import pytest
 
 from freqtrade.constants import CANCEL_REASON, MATH_CLOSE_PREC, UNLIMITED_STAKE_AMOUNT
-from freqtrade.enums import RPCMessageType, RunMode, SellType, State, TradingMode
+from freqtrade.enums import RPCMessageType, RunMode, SellType, SignalDirection, State
 from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
                                   InvalidOrderException, OperationalException, PricingError,
                                   TemporaryError)
@@ -23,9 +23,9 @@ from freqtrade.worker import Worker
 from tests.conftest import (create_mock_trades, get_patched_freqtradebot, get_patched_worker,
                             log_has, log_has_re, patch_edge, patch_exchange, patch_get_signal,
                             patch_wallet, patch_whitelist)
-from tests.conftest_trades import (MOCK_TRADE_COUNT, mock_order_1, mock_order_2, mock_order_2_sell,
-                                   mock_order_3, mock_order_3_sell, mock_order_4,
-                                   mock_order_5_stoploss, mock_order_6_sell)
+from tests.conftest_trades import (MOCK_TRADE_COUNT, enter_side, exit_side, mock_order_1,
+                                   mock_order_2, mock_order_2_sell, mock_order_3, mock_order_3_sell,
+                                   mock_order_4, mock_order_5_stoploss, mock_order_6_sell)
 
 
 def patch_RPCManager(mocker) -> MagicMock:
@@ -41,6 +41,7 @@ def patch_RPCManager(mocker) -> MagicMock:
 
 # Unit tests
 
+
 def test_freqtradebot_state(mocker, default_conf_usdt, markets) -> None:
     mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets))
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
@@ -195,12 +196,10 @@ def test_edge_overrides_stake_amount(mocker, edge_conf) -> None:
 
 
 @pytest.mark.parametrize('buy_price_mult,ignore_strat_sl', [
-    # Override stoploss
-    (0.79, False),
-    # Override strategy stoploss
-    (0.85, True)
+    (0.79, False),   # Override stoploss
+    (0.85, True),    # Override strategy stoploss
 ])
-def test_edge_overrides_stoploss(limit_buy_order_usdt, fee, caplog, mocker,
+def test_edge_overrides_stoploss(limit_order, fee, caplog, mocker,
                                  buy_price_mult, ignore_strat_sl, edge_conf) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
@@ -210,14 +209,14 @@ def test_edge_overrides_stoploss(limit_buy_order_usdt, fee, caplog, mocker,
     # Strategy stoploss is -0.1 but Edge imposes a stoploss at -0.2
     # Thus, if price falls 21%, stoploss should be triggered
     #
-    # mocking the ticker_usdt: price is falling ...
-    buy_price = limit_buy_order_usdt['price']
+    # mocking the ticker: price is falling ...
+    enter_price = limit_order['buy']['price']
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=MagicMock(return_value={
-            'bid': buy_price * buy_price_mult,
-            'ask': buy_price * buy_price_mult,
-            'last': buy_price * buy_price_mult,
+            'bid': enter_price * buy_price_mult,
+            'ask': enter_price * buy_price_mult,
+            'last': enter_price * buy_price_mult,
         }),
         get_fee=fee,
     )
@@ -230,7 +229,8 @@ def test_edge_overrides_stoploss(limit_buy_order_usdt, fee, caplog, mocker,
     freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
     freqtrade.enter_positions()
     trade = Trade.query.first()
-    trade.update(limit_buy_order_usdt)
+    caplog.clear()
+    trade.update(limit_order['buy'])
     #############################################
 
     # stoploss shoud be hit
@@ -271,7 +271,12 @@ def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) -
     assert Trade.total_open_trades_stakes() == 120.0
 
 
-def test_create_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee, mocker) -> None:
+@pytest.mark.parametrize("is_short,open_rate", [
+    (False, 2.0),
+    (True, 2.2)
+])
+def test_create_trade(default_conf_usdt, ticker_usdt, limit_order,
+                      fee, mocker, is_short, open_rate) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -284,10 +289,11 @@ def test_create_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee,
     # Save state of current whitelist
     whitelist = deepcopy(default_conf_usdt['exchange']['pair_whitelist'])
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.create_trade('ETH/USDT')
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade is not None
     assert trade.stake_amount == 60.0
     assert trade.is_open
@@ -295,9 +301,9 @@ def test_create_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee,
     assert trade.exchange == 'binance'
 
     # Simulate fulfilled LIMIT_BUY order for trade
-    trade.update(limit_buy_order_usdt)
+    trade.update(limit_order[enter_side(is_short)])
 
-    assert trade.open_rate == 2.0
+    assert trade.open_rate == open_rate
     assert trade.amount == 30.0
 
     assert whitelist == default_conf_usdt['exchange']['pair_whitelist']
@@ -319,6 +325,7 @@ def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, fee, mocke
         freqtrade.create_trade('ETH/USDT')
 
 
+@pytest.mark.parametrize("is_short", [False, True])
 @pytest.mark.parametrize('stake_amount,create,amount_enough,max_open_trades', [
     (5.0, True, True, 99),
     (0.00005, True, False, 99),
@@ -326,27 +333,27 @@ def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, fee, mocke
     (UNLIMITED_STAKE_AMOUNT, False, True, 0),
 ])
 def test_create_trade_minimal_amount(
-    default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, mocker,
-    stake_amount, create, amount_enough, max_open_trades, caplog
+    default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker,
+    stake_amount, create, amount_enough, max_open_trades, caplog, is_short
 ) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
-    buy_mock = MagicMock(return_value=limit_buy_order_usdt_open)
+    enter_mock = MagicMock(return_value=limit_order_open[enter_side(is_short)])
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
-        create_order=buy_mock,
+        create_order=enter_mock,
         get_fee=fee,
     )
     default_conf_usdt['max_open_trades'] = max_open_trades
     freqtrade = FreqtradeBot(default_conf_usdt)
     freqtrade.config['stake_amount'] = stake_amount
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     if create:
         assert freqtrade.create_trade('ETH/USDT')
         if amount_enough:
-            rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount']
+            rate, amount = enter_mock.call_args[1]['rate'], enter_mock.call_args[1]['amount']
             assert rate * amount <= default_conf_usdt['stake_amount']
         else:
             assert log_has_re(
@@ -416,7 +423,8 @@ def test_enter_positions_global_pairlock(default_conf_usdt, ticker_usdt, limit_b
     assert log_has_re(message, caplog)
 
 
-def test_handle_protections(mocker, default_conf_usdt, fee):
+@pytest.mark.parametrize('is_short', [False, True])
+def test_handle_protections(mocker, default_conf_usdt, fee, is_short):
     default_conf_usdt['protections'] = [
         {"method": "CooldownPeriod", "stop_duration": 60},
         {
@@ -431,7 +439,7 @@ def test_handle_protections(mocker, default_conf_usdt, fee):
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
     freqtrade.protections._protection_handlers[1].global_stop = MagicMock(
         return_value=(True, arrow.utcnow().shift(hours=1).datetime, "asdf"))
-    create_mock_trades(fee)
+    create_mock_trades(fee, is_short)
     freqtrade.handle_protections('ETC/BTC')
     send_msg_mock = freqtrade.rpc.send_msg
     assert send_msg_mock.call_count == 2
@@ -450,7 +458,8 @@ def test_create_trade_no_signal(default_conf_usdt, fee, mocker) -> None:
     )
     default_conf_usdt['stake_amount'] = 10
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_long=False)
+    # patch_get_signal(freqtrade, enter_long=False)
+    patch_get_signal(freqtrade, enter_long=False, exit_long=False)
 
     Trade.query = MagicMock()
     Trade.query.filter = MagicMock()
@@ -516,19 +525,24 @@ def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker,
     assert len(trades) == 4
 
 
-def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_buy_order_usdt,
-                                limit_buy_order_usdt_open, fee, mocker, caplog) -> None:
+@pytest.mark.parametrize('is_short, open_rate', [
+    (False, 2.0),
+    (True, 2.02)
+])
+def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_order, limit_order_open,
+                                is_short, open_rate, fee, mocker, caplog
+                                ) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
-        fetch_order=MagicMock(return_value=limit_buy_order_usdt),
+        create_order=MagicMock(return_value=limit_order_open[enter_side(is_short)]),
+        fetch_order=MagicMock(return_value=limit_order[enter_side(is_short)]),
         get_fee=fee,
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     trades = Trade.query.filter(Trade.is_open.is_(True)).all()
     assert not trades
@@ -543,11 +557,12 @@ def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_buy_order_
     assert trade.is_open
     assert trade.open_date is not None
     assert trade.exchange == 'binance'
-    assert trade.open_rate == 2.0
-    assert trade.amount == 30.0
+    assert trade.open_rate == open_rate  # TODO-lev: I think? That's what the ticker ask price is
+    assert isclose(trade.amount, 60 / open_rate)
 
     assert log_has(
-        'Buy signal found: about create a new trade for ETH/USDT with stake_amount: 60.0 ...',
+        f'{"Short" if is_short else "Long"} signal found: about create a new trade for ETH/USDT '
+        'with stake_amount: 60.0 ...',
         caplog
     )
 
@@ -695,148 +710,160 @@ def test_process_informative_pairs_added(default_conf_usdt, ticker_usdt, mocker)
     assert ("ETH/USDT", default_conf_usdt["timeframe"]) in refresh_mock.call_args[0][0]
 
 
-def test_execute_entry(mocker, default_conf_usdt, fee, limit_buy_order_usdt,
-                       limit_buy_order_usdt_open) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
+                       limit_order_open, is_short) -> None:
+
+    open_order = limit_order_open[enter_side(is_short)]
+    order = limit_order[enter_side(is_short)]
+
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     freqtrade = FreqtradeBot(default_conf_usdt)
     freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False)
     stake_amount = 2
     bid = 0.11
-    buy_rate_mock = MagicMock(return_value=bid)
-    buy_mm = MagicMock(return_value=limit_buy_order_usdt_open)
+    enter_rate_mock = MagicMock(return_value=bid)
+    enter_mm = MagicMock(return_value=open_order)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        get_rate=buy_rate_mock,
+        get_rate=enter_rate_mock,
         fetch_ticker=MagicMock(return_value={
             'bid': 1.9,
             'ask': 2.2,
             'last': 1.9
         }),
-        create_order=buy_mm,
+        create_order=enter_mm,
         get_min_pair_stake_amount=MagicMock(return_value=1),
         get_fee=fee,
     )
     pair = 'ETH/USDT'
 
-    assert not freqtrade.execute_entry(pair, stake_amount)
-    assert buy_rate_mock.call_count == 1
-    assert buy_mm.call_count == 0
+    assert not freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
+    assert enter_rate_mock.call_count == 1
+    assert enter_mm.call_count == 0
     assert freqtrade.strategy.confirm_trade_entry.call_count == 1
-    buy_rate_mock.reset_mock()
+    enter_rate_mock.reset_mock()
 
-    limit_buy_order_usdt_open['id'] = '22'
+    open_order['id'] = '22'
     freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
     assert freqtrade.execute_entry(pair, stake_amount)
-    assert buy_rate_mock.call_count == 1
-    assert buy_mm.call_count == 1
-    call_args = buy_mm.call_args_list[0][1]
+    assert enter_rate_mock.call_count == 1
+    assert enter_mm.call_count == 1
+    call_args = enter_mm.call_args_list[0][1]
     assert call_args['pair'] == pair
     assert call_args['rate'] == bid
     assert call_args['amount'] == round(stake_amount / bid, 8)
-    buy_rate_mock.reset_mock()
+    enter_rate_mock.reset_mock()
 
     # Should create an open trade with an open order id
     # As the order is not fulfilled yet
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
     assert trade.is_open is True
     assert trade.open_order_id == '22'
 
     # Test calling with price
-    limit_buy_order_usdt_open['id'] = '33'
+    open_order['id'] = '33'
     fix_price = 0.06
-    assert freqtrade.execute_entry(pair, stake_amount, fix_price)
+    assert freqtrade.execute_entry(pair, stake_amount, fix_price, is_short=is_short)
     # Make sure get_rate wasn't called again
-    assert buy_rate_mock.call_count == 0
+    assert enter_rate_mock.call_count == 0
 
-    assert buy_mm.call_count == 2
-    call_args = buy_mm.call_args_list[1][1]
+    assert enter_mm.call_count == 2
+    call_args = enter_mm.call_args_list[1][1]
     assert call_args['pair'] == pair
     assert call_args['rate'] == fix_price
     assert call_args['amount'] == round(stake_amount / fix_price, 8)
 
     # In case of closed order
-    limit_buy_order_usdt['status'] = 'closed'
-    limit_buy_order_usdt['price'] = 10
-    limit_buy_order_usdt['cost'] = 100
-    limit_buy_order_usdt['id'] = '444'
+    order['status'] = 'closed'
+    order['price'] = 10
+    order['cost'] = 100
+    order['id'] = '444'
 
     mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=limit_buy_order_usdt))
-    assert freqtrade.execute_entry(pair, stake_amount)
+                 MagicMock(return_value=order))
+    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
     trade = Trade.query.all()[2]
+    trade.is_short = is_short
     assert trade
     assert trade.open_order_id is None
     assert trade.open_rate == 10
     assert trade.stake_amount == 100
 
     # In case of rejected or expired order and partially filled
-    limit_buy_order_usdt['status'] = 'expired'
-    limit_buy_order_usdt['amount'] = 30.0
-    limit_buy_order_usdt['filled'] = 20.0
-    limit_buy_order_usdt['remaining'] = 10.00
-    limit_buy_order_usdt['price'] = 0.5
-    limit_buy_order_usdt['cost'] = 15.0
-    limit_buy_order_usdt['id'] = '555'
+    order['status'] = 'expired'
+    order['amount'] = 30.0
+    order['filled'] = 20.0
+    order['remaining'] = 10.00
+    order['price'] = 0.5
+    order['cost'] = 15.0
+    order['id'] = '555'
     mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=limit_buy_order_usdt))
+                 MagicMock(return_value=order))
     assert freqtrade.execute_entry(pair, stake_amount)
     trade = Trade.query.all()[3]
+    trade.is_short = is_short
     assert trade
     assert trade.open_order_id == '555'
     assert trade.open_rate == 0.5
     assert trade.stake_amount == 15.0
 
     # Test with custom stake
-    limit_buy_order_usdt['status'] = 'open'
-    limit_buy_order_usdt['id'] = '556'
+    order['status'] = 'open'
+    order['id'] = '556'
 
     freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0
-    assert freqtrade.execute_entry(pair, stake_amount)
+    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
     trade = Trade.query.all()[4]
+    trade.is_short = is_short
     assert trade
     assert trade.stake_amount == 150
 
     # Exception case
-    limit_buy_order_usdt['id'] = '557'
+    order['id'] = '557'
     freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
-    assert freqtrade.execute_entry(pair, stake_amount)
+    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
     trade = Trade.query.all()[5]
+    trade.is_short = is_short
     assert trade
     assert trade.stake_amount == 2.0
 
     # In case of the order is rejected and not filled at all
-    limit_buy_order_usdt['status'] = 'rejected'
-    limit_buy_order_usdt['amount'] = 30.0
-    limit_buy_order_usdt['filled'] = 0.0
-    limit_buy_order_usdt['remaining'] = 30.0
-    limit_buy_order_usdt['price'] = 0.5
-    limit_buy_order_usdt['cost'] = 0.0
-    limit_buy_order_usdt['id'] = '66'
+    order['status'] = 'rejected'
+    order['amount'] = 30.0
+    order['filled'] = 0.0
+    order['remaining'] = 30.0
+    order['price'] = 0.5
+    order['cost'] = 0.0
+    order['id'] = '66'
     mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=limit_buy_order_usdt))
+                 MagicMock(return_value=order))
     assert not freqtrade.execute_entry(pair, stake_amount)
 
     # Fail to get price...
     mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0))
 
-    with pytest.raises(PricingError, match="Could not determine buy price."):
-        freqtrade.execute_entry(pair, stake_amount)
+    with pytest.raises(PricingError, match=f"Could not determine {enter_side(is_short)} price."):
+        freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
 
     # In case of custom entry price
     mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.50)
-    limit_buy_order_usdt['status'] = 'open'
-    limit_buy_order_usdt['id'] = '5566'
+    order['status'] = 'open'
+    order['id'] = '5566'
     freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508
-    assert freqtrade.execute_entry(pair, stake_amount)
+    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
     trade = Trade.query.all()[6]
+    trade.is_short = is_short
     assert trade
     assert trade.open_rate_requested == 0.508
 
     # In case of custom entry price set to None
-    limit_buy_order_usdt['status'] = 'open'
-    limit_buy_order_usdt['id'] = '5567'
+
+    order['status'] = 'open'
+    order['id'] = '5567'
     freqtrade.strategy.custom_entry_price = lambda **kwargs: None
 
     mocker.patch.multiple(
@@ -844,22 +871,25 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_buy_order_usdt,
         get_rate=MagicMock(return_value=10),
     )
 
-    assert freqtrade.execute_entry(pair, stake_amount)
+    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
     trade = Trade.query.all()[7]
+    trade.is_short = is_short
     assert trade
     assert trade.open_rate_requested == 10
 
     # In case of custom entry price not float type
-    limit_buy_order_usdt['status'] = 'open'
-    limit_buy_order_usdt['id'] = '5568'
+    order['status'] = 'open'
+    order['id'] = '5568'
     freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price"
-    assert freqtrade.execute_entry(pair, stake_amount)
+    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
     trade = Trade.query.all()[8]
+    trade.is_short = is_short
     assert trade
     assert trade.open_rate_requested == 10
 
 
-def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_buy_order_usdt) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_order, is_short) -> None:
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
@@ -868,7 +898,7 @@ def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_buy_o
             'ask': 2.2,
             'last': 1.9
         }),
-        create_order=MagicMock(return_value=limit_buy_order_usdt),
+        create_order=MagicMock(return_value=limit_order[enter_side(is_short)]),
         get_rate=MagicMock(return_value=0.11),
         get_min_pair_stake_amount=MagicMock(return_value=1),
         get_fee=fee,
@@ -877,13 +907,14 @@ def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_buy_o
     pair = 'ETH/USDT'
 
     freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=ValueError)
+    # TODO-lev: KeyError happens on short, why?
     assert freqtrade.execute_entry(pair, stake_amount)
 
-    limit_buy_order_usdt['id'] = '222'
+    limit_order[enter_side(is_short)]['id'] = '222'
     freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=Exception)
     assert freqtrade.execute_entry(pair, stake_amount)
 
-    limit_buy_order_usdt['id'] = '2223'
+    limit_order[enter_side(is_short)]['id'] = '2223'
     freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
     assert freqtrade.execute_entry(pair, stake_amount)
 
@@ -891,14 +922,16 @@ def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_buy_o
     assert not freqtrade.execute_entry(pair, stake_amount)
 
 
-def test_add_stoploss_on_exchange(mocker, default_conf_usdt, limit_buy_order_usdt) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_add_stoploss_on_exchange(mocker, default_conf_usdt, limit_order, is_short) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
+    order = limit_order[enter_side(is_short)]
     mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt)
+    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
     mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
     mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
-                 return_value=limit_buy_order_usdt['amount'])
+                 return_value=order['amount'])
 
     stoploss = MagicMock(return_value={'id': 13434334})
     mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
@@ -907,6 +940,7 @@ def test_add_stoploss_on_exchange(mocker, default_conf_usdt, limit_buy_order_usd
     freqtrade.strategy.order_types['stoploss_on_exchange'] = True
 
     trade = MagicMock()
+    trade.is_short = is_short
     trade.open_order_id = None
     trade.stoploss_order_id = None
     trade.is_open = True
@@ -918,9 +952,12 @@ def test_add_stoploss_on_exchange(mocker, default_conf_usdt, limit_buy_order_usd
     assert trade.is_open is True
 
 
-def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog,
-                                     limit_buy_order_usdt, limit_sell_order_usdt) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_short,
+                                     limit_order) -> None:
     stoploss = MagicMock(return_value={'id': 13434334})
+    enter_order = limit_order[enter_side(is_short)]
+    exit_order = limit_order[exit_side(is_short)]
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -931,8 +968,8 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog,
             'last': 1.9
         }),
         create_order=MagicMock(side_effect=[
-            {'id': limit_buy_order_usdt['id']},
-            {'id': limit_sell_order_usdt['id']},
+            {'id': enter_order['id']},
+            {'id': exit_order['id']},
         ]),
         get_fee=fee,
     )
@@ -941,15 +978,17 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog,
         stoploss=stoploss
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     # First case: when stoploss is not yet set but the order is open
     # should get the stoploss order id immediately
     # and should return false as no trade actually happened
     trade = MagicMock()
+    trade.is_short = is_short
     trade.is_open = True
     trade.open_order_id = None
     trade.stoploss_order_id = None
+    trade.is_short = is_short
 
     assert freqtrade.handle_stoploss_on_exchange(trade) is False
     assert stoploss.call_count == 1
@@ -988,6 +1027,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog,
     caplog.clear()
     freqtrade.enter_positions()
     trade = Trade.query.first()
+    trade.is_short = is_short
     trade.is_open = True
     trade.open_order_id = None
     trade.stoploss_order_id = 100
@@ -999,7 +1039,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog,
         'type': 'stop_loss_limit',
         'price': 3,
         'average': 2,
-        'amount': limit_buy_order_usdt['amount'],
+        'amount': enter_order['amount'],
     })
     mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order', stoploss_order_hit)
     assert freqtrade.handle_stoploss_on_exchange(trade) is True
@@ -1038,9 +1078,12 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog,
     assert stoploss.call_count == 0
 
 
-def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog,
-                                         limit_buy_order_usdt, limit_sell_order_usdt) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog, is_short,
+                                         limit_order) -> None:
     # Sixth case: stoploss order was cancelled but couldn't create new one
+    enter_order = limit_order[enter_side(is_short)]
+    exit_order = limit_order[exit_side(is_short)]
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -1051,8 +1094,8 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog,
             'last': 1.9
         }),
         create_order=MagicMock(side_effect=[
-            {'id': limit_buy_order_usdt['id']},
-            {'id': limit_sell_order_usdt['id']},
+            {'id': enter_order['id']},
+            {'id': exit_order['id']},
         ]),
         get_fee=fee,
     )
@@ -1062,10 +1105,11 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog,
         stoploss=MagicMock(side_effect=ExchangeError()),
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     freqtrade.enter_positions()
     trade = Trade.query.first()
+    trade.is_short = is_short
     trade.is_open = True
     trade.open_order_id = None
     trade.stoploss_order_id = 100
@@ -1077,13 +1121,17 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog,
     assert trade.is_open is True
 
 
-def test_create_stoploss_order_invalid_order(mocker, default_conf_usdt, caplog, fee,
-                                             limit_buy_order_usdt_open, limit_sell_order_usdt):
+@pytest.mark.parametrize("is_short", [False, True])
+def test_create_stoploss_order_invalid_order(
+    mocker, default_conf_usdt, caplog, fee, is_short, limit_order, limit_order_open
+):
+    open_order = limit_order_open[enter_side(is_short)]
+    order = limit_order[exit_side(is_short)]
     rpc_mock = patch_RPCManager(mocker)
     patch_exchange(mocker)
     create_order_mock = MagicMock(side_effect=[
-        limit_buy_order_usdt_open,
-        {'id': limit_sell_order_usdt['id']}
+        open_order,
+        {'id': order['id']}
     ])
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
@@ -1101,11 +1149,12 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf_usdt, caplog,
         stoploss=MagicMock(side_effect=InvalidOrderException()),
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.order_types['stoploss_on_exchange'] = True
 
     freqtrade.enter_positions()
     trade = Trade.query.first()
+    trade.is_short = is_short
     caplog.clear()
     freqtrade.create_stoploss_order(trade, 200)
     assert trade.stoploss_order_id is None
@@ -1125,9 +1174,12 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf_usdt, caplog,
     assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market'
 
 
-def test_create_stoploss_order_insufficient_funds(mocker, default_conf_usdt, caplog, fee,
-                                                  limit_buy_order_usdt_open, limit_sell_order_usdt):
-    sell_mock = MagicMock(return_value={'id': limit_sell_order_usdt['id']})
+@pytest.mark.parametrize("is_short", [False, True])
+def test_create_stoploss_order_insufficient_funds(
+    mocker, default_conf_usdt, caplog, fee, limit_order_open,
+    limit_order, is_short
+):
+    exit_order = limit_order[exit_side(is_short)]['id']
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
 
     mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds')
@@ -1139,8 +1191,8 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf_usdt, cap
             'last': 1.9
         }),
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
-            sell_mock,
+            limit_order[enter_side(is_short)],
+            exit_order,
         ]),
         get_fee=fee,
         fetch_order=MagicMock(return_value={'status': 'canceled'}),
@@ -1149,11 +1201,12 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf_usdt, cap
         'freqtrade.exchange.Binance',
         stoploss=MagicMock(side_effect=InsufficientFundsError()),
     )
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.order_types['stoploss_on_exchange'] = True
 
     freqtrade.enter_positions()
     trade = Trade.query.first()
+    trade.is_short = is_short
     caplog.clear()
     freqtrade.create_stoploss_order(trade, 200)
     # stoploss_orderid was empty before
@@ -1168,11 +1221,18 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf_usdt, cap
     assert mock_insuf.call_count == 1
 
 
+@pytest.mark.parametrize("is_short,bid,ask,stop_price,amt,hang_price", [
+    (False, [4.38, 4.16], [4.4, 4.17], ['2.0805', 4.4 * 0.95], 27.39726027, 3),
+    (True, [1.09, 1.21], [1.1, 1.22], ['2.321', 1.09 * 1.05], 27.27272727, 1.5),
+])
 @pytest.mark.usefixtures("init_persistence")
-def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee,
-                                              limit_buy_order_usdt, limit_sell_order_usdt) -> None:
+def test_handle_stoploss_on_exchange_trailing(
+    mocker, default_conf_usdt, fee, is_short, bid, ask, limit_order, stop_price, amt, hang_price
+) -> None:
     # TODO-lev: test for short
     # When trailing stoploss is set
+    enter_order = limit_order[enter_side(is_short)]
+    exit_order = limit_order[exit_side(is_short)]
     stoploss = MagicMock(return_value={'id': 13434334})
     patch_RPCManager(mocker)
     mocker.patch.multiple(
@@ -1180,11 +1240,11 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee,
         fetch_ticker=MagicMock(return_value={
             'bid': 2.19,
             'ask': 2.2,
-            'last': 2.19
+            'last': 2.19,
         }),
         create_order=MagicMock(side_effect=[
-            {'id': limit_buy_order_usdt['id']},
-            {'id': limit_sell_order_usdt['id']},
+            {'id': enter_order['id']},
+            {'id': exit_order['id']},
         ]),
         get_fee=fee,
     )
@@ -1206,15 +1266,16 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee,
     freqtrade.strategy.order_types['stoploss_on_exchange'] = True
 
     # setting stoploss
-    freqtrade.strategy.stoploss = -0.05
+    freqtrade.strategy.stoploss = 0.05 if is_short else -0.05
 
     # setting stoploss_on_exchange_interval to 60 seconds
     freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
 
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     freqtrade.enter_positions()
     trade = Trade.query.first()
+    trade.is_short = is_short
     trade.is_open = True
     trade.open_order_id = None
     trade.stoploss_order_id = 100
@@ -1223,10 +1284,10 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee,
         'id': 100,
         'status': 'open',
         'type': 'stop_loss_limit',
-        'price': 3,
+        'price': hang_price,
         'average': 2,
         'info': {
-            'stopPrice': '2.0805'
+            'stopPrice': stop_price[0]
         }
     })
 
@@ -1240,9 +1301,9 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee,
     mocker.patch(
         'freqtrade.exchange.Exchange.fetch_ticker',
         MagicMock(return_value={
-            'bid': 4.38,
-            'ask': 4.4,
-            'last': 4.38
+            'bid': bid[0],
+            'ask': ask[0],
+            'last': bid[0],
         })
     )
 
@@ -1258,7 +1319,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee,
     stoploss_order_mock.assert_not_called()
 
     assert freqtrade.handle_trade(trade) is False
-    assert trade.stop_loss == 4.4 * 0.95
+    assert trade.stop_loss == stop_price[1]
 
     # setting stoploss_on_exchange_interval to 0 seconds
     freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
@@ -1267,11 +1328,11 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee,
 
     cancel_order_mock.assert_called_once_with(100, 'ETH/USDT')
     stoploss_order_mock.assert_called_once_with(
-        amount=27.39726027,
+        amount=amt,
         pair='ETH/USDT',
         order_types=freqtrade.strategy.order_types,
-        stop_price=4.4 * 0.95,
-        side="sell",
+        stop_price=stop_price[1],
+        side=exit_side(is_short),
         leverage=1.0
     )
 
@@ -1279,18 +1340,20 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee,
     mocker.patch(
         'freqtrade.exchange.Exchange.fetch_ticker',
         MagicMock(return_value={
-            'bid': 4.16,
-            'ask': 4.17,
-            'last': 4.16
+            'bid': bid[1],
+            'ask': ask[1],
+            'last': bid[1],
         })
     )
     assert freqtrade.handle_trade(trade) is True
 
 
+@pytest.mark.parametrize("is_short", [False, True])
 def test_handle_stoploss_on_exchange_trailing_error(
-        mocker, default_conf_usdt, fee, caplog, limit_buy_order_usdt, limit_sell_order_usdt
+    mocker, default_conf_usdt, fee, caplog, limit_order, is_short
 ) -> None:
-    # TODO-lev: test for short
+    enter_order = limit_order[enter_side(is_short)]
+    exit_order = limit_order[exit_side(is_short)]
     # When trailing stoploss is set
     stoploss = MagicMock(return_value={'id': 13434334})
     patch_exchange(mocker)
@@ -1303,8 +1366,8 @@ def test_handle_stoploss_on_exchange_trailing_error(
             'last': 1.9
         }),
         create_order=MagicMock(side_effect=[
-            {'id': limit_buy_order_usdt['id']},
-            {'id': limit_sell_order_usdt['id']},
+            {'id': enter_order['id']},
+            {'id': exit_order['id']},
         ]),
         get_fee=fee,
     )
@@ -1322,18 +1385,20 @@ def test_handle_stoploss_on_exchange_trailing_error(
     freqtrade.strategy.order_types['stoploss_on_exchange'] = True
 
     # setting stoploss
-    freqtrade.strategy.stoploss = -0.05
+    freqtrade.strategy.stoploss = 0.05 if is_short else -0.05
 
     # setting stoploss_on_exchange_interval to 60 seconds
     freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.enter_positions()
     trade = Trade.query.first()
+    trade.is_short = is_short
     trade.is_open = True
     trade.open_order_id = None
     trade.stoploss_order_id = "abcd"
     trade.stop_loss = 0.2
     trade.stoploss_last_update = arrow.utcnow().shift(minutes=-601).datetime.replace(tzinfo=None)
+    trade.is_short = is_short
 
     stoploss_order_hanging = {
         'id': "abcd",
@@ -1349,7 +1414,7 @@ def test_handle_stoploss_on_exchange_trailing_error(
                  side_effect=InvalidOrderException())
     mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order',
                  return_value=stoploss_order_hanging)
-    freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging, side="sell")
+    freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
     assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/USDT.*", caplog)
 
     # Still try to create order
@@ -1359,14 +1424,17 @@ def test_handle_stoploss_on_exchange_trailing_error(
     caplog.clear()
     cancel_mock = mocker.patch("freqtrade.exchange.Binance.cancel_stoploss_order", MagicMock())
     mocker.patch("freqtrade.exchange.Binance.stoploss", side_effect=ExchangeError())
-    freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging, side="sell")
+    freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
     assert cancel_mock.call_count == 1
     assert log_has_re(r"Could not create trailing stoploss order for pair ETH/USDT\..*", caplog)
 
 
-@pytest.mark.usefixtures("init_persistence")
+@pytest.mark.parametrize("is_short", [False, True])
 def test_handle_stoploss_on_exchange_custom_stop(
-        mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_sell_order_usdt) -> None:
+    mocker, default_conf_usdt, fee, is_short, limit_order
+) -> None:
+    enter_order = limit_order[enter_side(is_short)]
+    exit_order = limit_order[exit_side(is_short)]
     # When trailing stoploss is set
     # TODO-lev: test for short
     stoploss = MagicMock(return_value={'id': 13434334})
@@ -1379,8 +1447,8 @@ def test_handle_stoploss_on_exchange_custom_stop(
             'last': 1.9
         }),
         create_order=MagicMock(side_effect=[
-            {'id': limit_buy_order_usdt['id']},
-            {'id': limit_sell_order_usdt['id']},
+            {'id': enter_order['id']},
+            {'id': exit_order['id']},
         ]),
         get_fee=fee,
     )
@@ -1407,10 +1475,11 @@ def test_handle_stoploss_on_exchange_custom_stop(
     # setting stoploss_on_exchange_interval to 60 seconds
     freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
 
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     freqtrade.enter_positions()
     trade = Trade.query.first()
+    trade.is_short = is_short
     trade.is_open = True
     trade.open_order_id = None
     trade.stoploss_order_id = 100
@@ -1435,9 +1504,9 @@ def test_handle_stoploss_on_exchange_custom_stop(
     mocker.patch(
         'freqtrade.exchange.Exchange.fetch_ticker',
         MagicMock(return_value={
-            'bid': 4.38,
-            'ask': 4.4,
-            'last': 4.38
+            'bid': 4.38 if not is_short else 1.9 / 2,
+            'ask': 4.4 if not is_short else 2.2 / 2,
+            'last': 4.38 if not is_short else 1.9 / 2,
         })
     )
 
@@ -1453,8 +1522,8 @@ def test_handle_stoploss_on_exchange_custom_stop(
     stoploss_order_mock.assert_not_called()
 
     assert freqtrade.handle_trade(trade) is False
-    assert trade.stop_loss == 4.4 * 0.96
-    assert trade.stop_loss_pct == -0.04
+    assert trade.stop_loss == 4.4 * 0.96 if not is_short else 1.1
+    assert trade.stop_loss_pct == -0.04 if not is_short else 0.04
 
     # setting stoploss_on_exchange_interval to 0 seconds
     freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
@@ -1462,12 +1531,13 @@ def test_handle_stoploss_on_exchange_custom_stop(
     assert freqtrade.handle_stoploss_on_exchange(trade) is False
 
     cancel_order_mock.assert_called_once_with(100, 'ETH/USDT')
+    # Long uses modified ask - offset, short modified bid + offset
     stoploss_order_mock.assert_called_once_with(
-        amount=31.57894736,
+        amount=trade.amount,
         pair='ETH/USDT',
         order_types=freqtrade.strategy.order_types,
-        stop_price=4.4 * 0.96,
-        side="sell",
+        stop_price=4.4 * 0.96 if not is_short else 0.95 * 1.04,
+        side=exit_side(is_short),
         leverage=1.0
     )
 
@@ -1483,9 +1553,12 @@ def test_handle_stoploss_on_exchange_custom_stop(
     assert freqtrade.handle_trade(trade) is True
 
 
-def test_tsl_on_exchange_compatible_with_edge(
-        mocker, edge_conf, fee, limit_buy_order_usdt, limit_sell_order_usdt) -> None:
-    # TODO-lev: test for short
+def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
+                                              limit_order) -> None:
+
+    enter_order = limit_order['buy']
+    exit_order = limit_order['sell']
+
     # When trailing stoploss is set
     stoploss = MagicMock(return_value={'id': 13434334})
     patch_RPCManager(mocker)
@@ -1502,8 +1575,8 @@ def test_tsl_on_exchange_compatible_with_edge(
             'last': 2.19
         }),
         create_order=MagicMock(side_effect=[
-            {'id': limit_buy_order_usdt['id']},
-            {'id': limit_sell_order_usdt['id']},
+            {'id': enter_order['id']},
+            {'id': exit_order['id']},
         ]),
         get_fee=fee,
         stoploss=stoploss,
@@ -1594,7 +1667,7 @@ def test_tsl_on_exchange_compatible_with_edge(
         pair='NEO/BTC',
         order_types=freqtrade.strategy.order_types,
         stop_price=4.4 * 0.99,
-        side="sell",
+        side='sell',
         leverage=1.0
     )
 
@@ -1622,11 +1695,47 @@ def test_enter_positions(mocker, default_conf_usdt, return_value, side_effect,
     assert mock_ct.call_count == len(default_conf_usdt['exchange']['pair_whitelist'])
 
 
-def test_exit_positions_exception(mocker, default_conf_usdt, limit_buy_order_usdt, caplog) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_exit_positions(
+    mocker, default_conf_usdt, limit_order, is_short, caplog
+) -> None:
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt)
+
+    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
+    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
+                 return_value=limit_order[enter_side(is_short)])
+    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
+    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
+                 return_value=limit_order[enter_side(is_short)]['amount'])
 
     trade = MagicMock()
+    trade.is_short = is_short
+    trade.open_order_id = '123'
+    trade.open_fee = 0.001
+    trades = [trade]
+    n = freqtrade.exit_positions(trades)
+    assert n == 0
+    # Test amount not modified by fee-logic
+    assert not log_has(
+        'Applying fee to amount for Trade {} from 30.0 to 90.81'.format(trade), caplog
+    )
+
+    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
+    # test amount modified by fee-logic
+    n = freqtrade.exit_positions(trades)
+    assert n == 0
+
+
+@pytest.mark.parametrize("is_short", [False, True])
+def test_exit_positions_exception(
+    mocker, default_conf_usdt, limit_order, caplog, is_short
+) -> None:
+    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
+    order = limit_order[enter_side(is_short)]
+    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
+
+    trade = MagicMock()
+    trade.is_short = is_short
     trade.open_order_id = None
     trade.open_fee = 0.001
     trade.pair = 'ETH/USDT'
@@ -1637,19 +1746,24 @@ def test_exit_positions_exception(mocker, default_conf_usdt, limit_buy_order_usd
         'freqtrade.freqtradebot.FreqtradeBot.handle_trade',
         side_effect=DependencyException()
     )
+    caplog.clear()
     n = freqtrade.exit_positions(trades)
     assert n == 0
     assert log_has('Unable to exit trade ETH/USDT: ', caplog)
 
 
-def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, caplog) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_update_trade_state(
+    mocker, default_conf_usdt, limit_order, is_short, caplog
+) -> None:
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
+    order = limit_order[enter_side(is_short)]
 
     mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt)
+    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
     mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
     mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
-                 return_value=limit_buy_order_usdt['amount'])
+                 return_value=order['amount'])
 
     trade = Trade(
         open_order_id=123,
@@ -1659,6 +1773,7 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, cap
         open_date=arrow.utcnow().datetime,
         amount=11,
         exchange="binance",
+        is_short=is_short
     )
     assert not freqtrade.update_trade_state(trade, None)
     assert log_has_re(r'Orderid for trade .* is empty.', caplog)
@@ -1669,7 +1784,7 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, cap
     assert not log_has_re(r'Applying fee to .*', caplog)
     caplog.clear()
     assert trade.open_order_id is None
-    assert trade.amount == limit_buy_order_usdt['amount']
+    assert trade.amount == order['amount']
 
     trade.open_order_id = '123'
     mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
@@ -1687,12 +1802,16 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, cap
     assert log_has_re('Found open order for.*', caplog)
 
 
+@pytest.mark.parametrize("is_short", [False, True])
 @pytest.mark.parametrize('initial_amount,has_rounding_fee', [
     (30.0 + 1e-14, True),
     (8.0, False)
 ])
-def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, limit_buy_order_usdt,
-                                          fee, mocker, initial_amount, has_rounding_fee, caplog):
+def test_update_trade_state_withorderdict(
+    default_conf_usdt, trades_for_order, limit_order, fee, mocker, initial_amount,
+    has_rounding_fee, is_short, caplog
+):
+    order = limit_order[enter_side(is_short)]
     trades_for_order[0]['amount'] = initial_amount
     mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
     # fetch_order should not be called!!
@@ -1711,22 +1830,26 @@ def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, l
         fee_close=fee.return_value,
         open_order_id="123456",
         is_open=True,
+        is_short=is_short
     )
-    freqtrade.update_trade_state(trade, '123456', limit_buy_order_usdt)
+    freqtrade.update_trade_state(trade, '123456', order)
     assert trade.amount != amount
-    assert trade.amount == limit_buy_order_usdt['amount']
+    assert trade.amount == order['amount']
     if has_rounding_fee:
         assert log_has_re(r'Applying fee on amount for .*', caplog)
 
 
-def test_update_trade_state_exception(mocker, default_conf_usdt,
-                                      limit_buy_order_usdt, caplog) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_update_trade_state_exception(mocker, default_conf_usdt, is_short, limit_order,
+                                      caplog) -> None:
+    order = limit_order[enter_side(is_short)]
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt)
+    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
 
     trade = MagicMock()
     trade.open_order_id = '123'
     trade.open_fee = 0.001
+    trade.is_short = is_short
 
     # Test raise of OperationalException exception
     mocker.patch(
@@ -1753,8 +1876,12 @@ def test_update_trade_state_orderexception(mocker, default_conf_usdt, caplog) ->
     assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog)
 
 
-def test_update_trade_state_sell(default_conf_usdt, trades_for_order, limit_sell_order_usdt_open,
-                                 limit_sell_order_usdt, mocker):
+@pytest.mark.parametrize("is_short", [False, True])
+def test_update_trade_state_sell(
+    default_conf_usdt, trades_for_order, limit_order_open, limit_order, is_short, mocker,
+):
+    open_order = limit_order_open[exit_side(is_short)]
+    l_order = limit_order[exit_side(is_short)]
     mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
     # fetch_order should not be called!!
     mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
@@ -1762,8 +1889,8 @@ def test_update_trade_state_sell(default_conf_usdt, trades_for_order, limit_sell
     mocker.patch('freqtrade.wallets.Wallets.update', wallet_mock)
 
     patch_exchange(mocker)
-    amount = limit_sell_order_usdt["amount"]
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
+    amount = l_order["amount"]
     wallet_mock.reset_mock()
     trade = Trade(
         pair='LTC/ETH',
@@ -1775,12 +1902,14 @@ def test_update_trade_state_sell(default_conf_usdt, trades_for_order, limit_sell
         open_date=arrow.utcnow().datetime,
         open_order_id="123456",
         is_open=True,
+        interest_rate=0.0005,
+        is_short=is_short
     )
-    order = Order.parse_from_ccxt_object(limit_sell_order_usdt_open, 'LTC/ETH', 'sell')
+    order = Order.parse_from_ccxt_object(open_order, 'LTC/ETH', (enter_side(is_short)))
     trade.orders.append(order)
     assert order.status == 'open'
-    freqtrade.update_trade_state(trade, trade.open_order_id, limit_sell_order_usdt)
-    assert trade.amount == limit_sell_order_usdt['amount']
+    freqtrade.update_trade_state(trade, trade.open_order_id, l_order)
+    assert trade.amount == l_order['amount']
     # Wallet needs to be updated after closing a limit-sell order to reenable buying
     assert wallet_mock.call_count == 1
     assert not trade.is_open
@@ -1788,78 +1917,96 @@ def test_update_trade_state_sell(default_conf_usdt, trades_for_order, limit_sell
     assert order.status == 'closed'
 
 
-def test_handle_trade(default_conf_usdt, limit_buy_order_usdt, limit_sell_order_usdt_open,
-                      limit_sell_order_usdt, fee, mocker) -> None:
+@pytest.mark.parametrize('is_short,close_profit', [
+    (False, 0.09451372),
+    (True, 0.08635224),
+])
+def test_handle_trade(
+    default_conf_usdt, limit_order_open, limit_order, fee, mocker, is_short, close_profit
+) -> None:
+    open_order = limit_order_open[exit_side(is_short)]
+    enter_order = limit_order[enter_side(is_short)]
+    exit_order = limit_order[exit_side(is_short)]
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
+            'bid': 2.19,
             'ask': 2.2,
-            'last': 1.9
+            'last': 2.19
         }),
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt,
-            limit_sell_order_usdt_open,
+            enter_order,
+            open_order,
         ]),
         get_fee=fee,
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
 
     time.sleep(0.01)  # Race condition fix
-    trade.update(limit_buy_order_usdt)
+    trade.update(enter_order)
     assert trade.is_open is True
     freqtrade.wallets.update()
 
-    patch_get_signal(freqtrade, enter_long=False, exit_long=True)
+    patch_get_signal(freqtrade, enter_long=False, exit_short=is_short, exit_long=not is_short)
     assert freqtrade.handle_trade(trade) is True
-    assert trade.open_order_id == limit_sell_order_usdt['id']
+    assert trade.open_order_id == exit_order['id']
 
-    # Simulate fulfilled LIMIT_SELL order for trade
-    trade.update(limit_sell_order_usdt)
+    # Simulate fulfilled LIMIT order for trade
+    trade.update(exit_order)
 
-    assert trade.close_rate == 2.2
-    assert trade.close_profit == 0.09451372
+    assert trade.close_rate == 2.0 if is_short else 2.2
+    assert trade.close_profit == close_profit
     assert trade.calc_profit() == 5.685
     assert trade.close_date is not None
 
 
-def test_handle_overlapping_signals(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open,
-                                    fee, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_handle_overlapping_signals(
+    default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, is_short
+) -> None:
+    open_order = limit_order_open[exit_side(is_short)]
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
+            open_order,
             {'id': 1234553382},
         ]),
         get_fee=fee,
     )
 
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_long=True, exit_long=True)
+    if is_short:
+        patch_get_signal(freqtrade, enter_long=False, enter_short=True, exit_short=True)
+    else:
+        patch_get_signal(freqtrade, enter_long=True, exit_long=True)
     freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
 
     freqtrade.enter_positions()
 
     # Buy and Sell triggering, so doing nothing ...
     trades = Trade.query.all()
+
     nb_trades = len(trades)
     assert nb_trades == 0
 
     # Buy is triggering, so buying ...
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.enter_positions()
     trades = Trade.query.all()
+    for trade in trades:
+        trade.is_short = is_short
     nb_trades = len(trades)
     assert nb_trades == 1
     assert trades[0].is_open is True
@@ -1868,26 +2015,42 @@ def test_handle_overlapping_signals(default_conf_usdt, ticker_usdt, limit_buy_or
     patch_get_signal(freqtrade, enter_long=False)
     assert freqtrade.handle_trade(trades[0]) is False
     trades = Trade.query.all()
+    for trade in trades:
+        trade.is_short = is_short
     nb_trades = len(trades)
     assert nb_trades == 1
     assert trades[0].is_open is True
 
     # Buy and Sell are triggering, so doing nothing ...
-    patch_get_signal(freqtrade, enter_long=True, exit_long=True)
+    if is_short:
+        patch_get_signal(freqtrade, enter_long=False, enter_short=True, exit_short=True)
+    else:
+        patch_get_signal(freqtrade, enter_long=True, exit_long=True)
     assert freqtrade.handle_trade(trades[0]) is False
     trades = Trade.query.all()
+    for trade in trades:
+        trade.is_short = is_short
     nb_trades = len(trades)
     assert nb_trades == 1
     assert trades[0].is_open is True
 
     # Sell is triggering, guess what : we are Selling!
-    patch_get_signal(freqtrade, enter_long=False, exit_long=True)
+    if is_short:
+        patch_get_signal(freqtrade, enter_long=False, exit_short=True)
+    else:
+        patch_get_signal(freqtrade, enter_long=False, exit_long=True)
     trades = Trade.query.all()
+    for trade in trades:
+        trade.is_short = is_short
     assert freqtrade.handle_trade(trades[0]) is True
 
 
-def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open,
-                          fee, mocker, caplog) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, caplog,
+                          is_short) -> None:
+
+    open_order = limit_order_open[enter_side(is_short)]
+
     caplog.set_level(logging.DEBUG)
 
     patch_RPCManager(mocker)
@@ -1895,19 +2058,20 @@ def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_o
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
+            open_order,
             {'id': 1234553382},
         ]),
         get_fee=fee,
     )
 
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
 
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     trade.is_open = True
 
     # FIX: sniffing logs, suggest handle_trade should not execute_trade_exit
@@ -1915,14 +2079,22 @@ def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_o
     #      we might just want to check if we are in a sell condition without
     #      executing
     # if ROI is reached we must sell
+    # TODO-lev: Change the next line for shorts
+    caplog.clear()
     patch_get_signal(freqtrade, enter_long=False, exit_long=True)
     assert freqtrade.handle_trade(trade)
     assert log_has("ETH/USDT - Required profit reached. sell_type=SellType.ROI",
                    caplog)
 
 
-def test_handle_trade_use_sell_signal(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open,
-                                      limit_sell_order_usdt_open, fee, mocker, caplog) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_handle_trade_use_sell_signal(
+    default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, caplog, is_short
+) -> None:
+
+    enter_open_order = limit_order_open[exit_side(is_short)]
+    exit_open_order = limit_order_open[enter_side(is_short)]
+
     # use_sell_signal is True buy default
     caplog.set_level(logging.DEBUG)
     patch_RPCManager(mocker)
@@ -1930,50 +2102,61 @@ def test_handle_trade_use_sell_signal(default_conf_usdt, ticker_usdt, limit_buy_
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
-            limit_sell_order_usdt_open,
+            enter_open_order,
+            exit_open_order,
         ]),
         get_fee=fee,
     )
 
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     trade.is_open = True
 
     patch_get_signal(freqtrade, enter_long=False, exit_long=False)
     assert not freqtrade.handle_trade(trade)
 
-    patch_get_signal(freqtrade, enter_long=False, exit_long=True)
+    if is_short:
+        patch_get_signal(freqtrade, enter_long=False, exit_short=True)
+    else:
+        patch_get_signal(freqtrade, enter_long=False, exit_long=True)
     assert freqtrade.handle_trade(trade)
     assert log_has("ETH/USDT - Sell signal received. sell_type=SellType.SELL_SIGNAL",
                    caplog)
 
 
-def test_close_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt,
-                     limit_buy_order_usdt_open, limit_sell_order_usdt, fee, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_close_trade(
+    default_conf_usdt, ticker_usdt, limit_order_open,
+    limit_order, fee, mocker, is_short
+) -> None:
+    open_order = limit_order_open[exit_side(is_short)]
+    enter_order = limit_order[exit_side(is_short)]
+    exit_order = limit_order[enter_side(is_short)]
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
+        create_order=MagicMock(return_value=open_order),
         get_fee=fee,
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     # Create trade and sell it
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
 
-    trade.update(limit_buy_order_usdt)
-    trade.update(limit_sell_order_usdt)
+    trade.update(enter_order)
+    trade.update(exit_order)
     assert trade.is_open is False
 
     with pytest.raises(DependencyException, match=r'.*closed trade.*'):
@@ -1993,27 +2176,33 @@ def test_bot_loop_start_called_once(mocker, default_conf_usdt, caplog):
     assert ftbot.strategy.analyze.call_count == 1
 
 
-def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, limit_buy_order_old,
-                                              open_trade, fee, mocker) -> None:
-    default_conf_usdt["unfilledtimeout"] = {"buy": 1400, "sell": 30}
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_timedout_buy_usercustom(
+    default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
+    limit_sell_order_old, fee, mocker, is_short
+) -> None:
+
+    old_order = limit_sell_order_old if is_short else limit_buy_order_old
+    default_conf_usdt["unfilledtimeout"] = {"buy": 30,
+                                            "sell": 1400} if is_short else {"buy": 1400, "sell": 30}
 
     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['status'] = 'canceled'
-    cancel_order_wr_mock = MagicMock(return_value=cancel_buy_order)
+    cancel_order_mock = MagicMock(return_value=old_order)
+    cancel_enter_order = deepcopy(old_order)
+    cancel_enter_order['status'] = 'canceled'
+    cancel_order_wr_mock = MagicMock(return_value=cancel_enter_order)
 
     patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_buy_order_old),
+        fetch_order=MagicMock(return_value=old_order),
         cancel_order_with_result=cancel_order_wr_mock,
         cancel_order=cancel_order_mock,
         get_fee=fee
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-
+    open_trade.is_short = is_short
     Trade.query.session.add(open_trade)
 
     # Ensure default is to return empty (so not mocked yet)
@@ -2021,24 +2210,34 @@ def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, li
     assert cancel_order_mock.call_count == 0
 
     # Return false - trade remains open
-    freqtrade.strategy.check_buy_timeout = MagicMock(return_value=False)
+    if is_short:
+        freqtrade.strategy.check_sell_timeout = MagicMock(return_value=False)
+    else:
+        freqtrade.strategy.check_buy_timeout = MagicMock(return_value=False)
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 0
     trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
     nb_trades = len(trades)
     assert nb_trades == 1
-    assert freqtrade.strategy.check_buy_timeout.call_count == 1
+    if is_short:
+        assert freqtrade.strategy.check_sell_timeout.call_count == 1
+        # Raise Keyerror ... (no impact on trade)
+        freqtrade.strategy.check_sell_timeout = MagicMock(side_effect=KeyError)
+    else:
+        assert freqtrade.strategy.check_buy_timeout.call_count == 1
+        freqtrade.strategy.check_buy_timeout = MagicMock(side_effect=KeyError)
 
-    # Raise Keyerror ... (no impact on trade)
-    freqtrade.strategy.check_buy_timeout = MagicMock(side_effect=KeyError)
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 0
     trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
     nb_trades = len(trades)
     assert nb_trades == 1
-    assert freqtrade.strategy.check_buy_timeout.call_count == 1
-
-    freqtrade.strategy.check_buy_timeout = MagicMock(return_value=True)
+    if is_short:
+        assert freqtrade.strategy.check_sell_timeout.call_count == 1
+        freqtrade.strategy.check_sell_timeout = MagicMock(return_value=True)
+    else:
+        assert freqtrade.strategy.check_buy_timeout.call_count == 1
+        freqtrade.strategy.check_buy_timeout = MagicMock(return_value=True)
     # Trade should be closed since the function returns true
     freqtrade.check_handle_timedout()
     assert cancel_order_wr_mock.call_count == 1
@@ -2046,28 +2245,39 @@ def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, li
     trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
     nb_trades = len(trades)
     assert nb_trades == 0
-    assert freqtrade.strategy.check_buy_timeout.call_count == 1
+    if is_short:
+        assert freqtrade.strategy.check_sell_timeout.call_count == 1
+    else:
+        assert freqtrade.strategy.check_buy_timeout.call_count == 1
 
 
-def test_check_handle_timedout_buy(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
-                                   fee, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_timedout_buy(
+    default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
+    limit_sell_order_old, fee, mocker, is_short
+) -> None:
+    old_order = limit_sell_order_old if is_short else limit_buy_order_old
     rpc_mock = patch_RPCManager(mocker)
-    limit_buy_cancel = deepcopy(limit_buy_order_old)
+    limit_buy_cancel = deepcopy(old_order)
     limit_buy_cancel['status'] = 'canceled'
     cancel_order_mock = MagicMock(return_value=limit_buy_cancel)
     patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_buy_order_old),
+        fetch_order=MagicMock(return_value=old_order),
         cancel_order_with_result=cancel_order_mock,
         get_fee=fee
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
 
+    open_trade.is_short = is_short
     Trade.query.session.add(open_trade)
 
-    freqtrade.strategy.check_buy_timeout = MagicMock(return_value=False)
+    if is_short:
+        freqtrade.strategy.check_sell_timeout = MagicMock(return_value=False)
+    else:
+        freqtrade.strategy.check_buy_timeout = MagicMock(return_value=False)
     # check it does cancel buy orders over the time limit
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 1
@@ -2076,25 +2286,32 @@ def test_check_handle_timedout_buy(default_conf_usdt, ticker_usdt, limit_buy_ord
     nb_trades = len(trades)
     assert nb_trades == 0
     # Custom user buy-timeout is never called
-    assert freqtrade.strategy.check_buy_timeout.call_count == 0
+    if is_short:
+        assert freqtrade.strategy.check_sell_timeout.call_count == 0
+    else:
+        assert freqtrade.strategy.check_buy_timeout.call_count == 0
 
 
-def test_check_handle_cancelled_buy(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
-                                    fee, mocker, caplog) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_cancelled_buy(
+    default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
+    limit_sell_order_old, fee, mocker, caplog, is_short
+) -> None:
     """ Handle Buy order cancelled on exchange"""
+    old_order = limit_sell_order_old if is_short else limit_buy_order_old
     rpc_mock = patch_RPCManager(mocker)
     cancel_order_mock = MagicMock()
     patch_exchange(mocker)
-    limit_buy_order_old.update({"status": "canceled", 'filled': 0.0})
+    old_order.update({"status": "canceled", 'filled': 0.0})
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_buy_order_old),
+        fetch_order=MagicMock(return_value=old_order),
         cancel_order=cancel_order_mock,
         get_fee=fee
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-
+    open_trade.is_short = is_short
     Trade.query.session.add(open_trade)
 
     # check it does cancel buy orders over the time limit
@@ -2104,11 +2321,15 @@ def test_check_handle_cancelled_buy(default_conf_usdt, ticker_usdt, limit_buy_or
     trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
     nb_trades = len(trades)
     assert nb_trades == 0
-    assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog)
+    assert log_has_re(
+        f"{'Sell' if is_short else 'Buy'} order cancelled on exchange for Trade.*", caplog)
 
 
-def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt,
-                                             open_trade, fee, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_timedout_buy_exception(
+    default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
+    is_short, fee, mocker
+) -> None:
     rpc_mock = patch_RPCManager(mocker)
     cancel_order_mock = MagicMock()
     patch_exchange(mocker)
@@ -2133,8 +2354,11 @@ def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt,
     assert nb_trades == 1
 
 
-def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, limit_sell_order_old,
-                                               mocker, open_trade) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_timedout_sell_usercustom(
+    default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker,
+    is_short, open_trade_usdt
+) -> None:
     default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440}
     rpc_mock = patch_RPCManager(mocker)
     cancel_order_mock = MagicMock()
@@ -2147,12 +2371,12 @@ def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, l
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
 
-    open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime
-    open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime
-    open_trade.close_profit_abs = 0.001
-    open_trade.is_open = False
+    open_trade_usdt.open_date = arrow.utcnow().shift(hours=-5).datetime
+    open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
+    open_trade_usdt.close_profit_abs = 0.001
+    open_trade_usdt.is_open = False
 
-    Trade.query.session.add(open_trade)
+    Trade.query.session.add(open_trade_usdt)
     # Ensure default is false
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 0
@@ -2162,7 +2386,7 @@ def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, l
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 0
     assert rpc_mock.call_count == 0
-    assert open_trade.is_open is False
+    assert open_trade_usdt.is_open is False
     assert freqtrade.strategy.check_sell_timeout.call_count == 1
 
     freqtrade.strategy.check_sell_timeout = MagicMock(side_effect=KeyError)
@@ -2170,7 +2394,7 @@ def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, l
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 0
     assert rpc_mock.call_count == 0
-    assert open_trade.is_open is False
+    assert open_trade_usdt.is_open is False
     assert freqtrade.strategy.check_sell_timeout.call_count == 1
 
     # Return True - sells!
@@ -2178,12 +2402,15 @@ def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, l
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 1
     assert rpc_mock.call_count == 1
-    assert open_trade.is_open is True
+    assert open_trade_usdt.is_open is True
     assert freqtrade.strategy.check_sell_timeout.call_count == 1
 
 
-def test_check_handle_timedout_sell(default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker,
-                                    open_trade) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_timedout_sell(
+    default_conf_usdt, ticker_usdt, limit_sell_order_old,
+    mocker, is_short, open_trade_usdt
+) -> None:
     rpc_mock = patch_RPCManager(mocker)
     cancel_order_mock = MagicMock()
     patch_exchange(mocker)
@@ -2195,25 +2422,28 @@ def test_check_handle_timedout_sell(default_conf_usdt, ticker_usdt, limit_sell_o
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
 
-    open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime
-    open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime
-    open_trade.close_profit_abs = 0.001
-    open_trade.is_open = False
+    open_trade_usdt.open_date = arrow.utcnow().shift(hours=-5).datetime
+    open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
+    open_trade_usdt.close_profit_abs = 0.001
+    open_trade_usdt.is_open = False
 
-    Trade.query.session.add(open_trade)
+    Trade.query.session.add(open_trade_usdt)
 
     freqtrade.strategy.check_sell_timeout = MagicMock(return_value=False)
     # check it does cancel sell orders over the time limit
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 1
     assert rpc_mock.call_count == 1
-    assert open_trade.is_open is True
+    assert open_trade_usdt.is_open is True
     # Custom user sell-timeout is never called
     assert freqtrade.strategy.check_sell_timeout.call_count == 0
 
 
-def test_check_handle_cancelled_sell(default_conf_usdt, ticker_usdt, limit_sell_order_old,
-                                     open_trade, mocker, caplog) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_cancelled_sell(
+    default_conf_usdt, ticker_usdt, limit_sell_order_old, open_trade_usdt,
+    is_short, mocker, caplog
+) -> None:
     """ Handle sell order cancelled on exchange"""
     rpc_mock = patch_RPCManager(mocker)
     cancel_order_mock = MagicMock()
@@ -2227,22 +2457,25 @@ def test_check_handle_cancelled_sell(default_conf_usdt, ticker_usdt, limit_sell_
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
 
-    open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime
-    open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime
-    open_trade.is_open = False
+    open_trade_usdt.open_date = arrow.utcnow().shift(hours=-5).datetime
+    open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
+    open_trade_usdt.is_open = False
 
-    Trade.query.session.add(open_trade)
+    Trade.query.session.add(open_trade_usdt)
 
     # check it does cancel sell orders over the time limit
     freqtrade.check_handle_timedout()
     assert cancel_order_mock.call_count == 0
     assert rpc_mock.call_count == 1
-    assert open_trade.is_open is True
+    assert open_trade_usdt.is_open is True
     assert log_has_re("Sell order cancelled on exchange for Trade.*", caplog)
 
 
-def test_check_handle_timedout_partial(default_conf_usdt, ticker_usdt, limit_buy_order_old_partial,
-                                       open_trade, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_timedout_partial(
+    default_conf_usdt, ticker_usdt, limit_buy_order_old_partial, is_short,
+    open_trade, mocker
+) -> None:
     rpc_mock = patch_RPCManager(mocker)
     limit_buy_canceled = deepcopy(limit_buy_order_old_partial)
     limit_buy_canceled['status'] = 'canceled'
@@ -2270,9 +2503,12 @@ def test_check_handle_timedout_partial(default_conf_usdt, ticker_usdt, limit_buy
     assert trades[0].stake_amount == open_trade.open_rate * trades[0].amount
 
 
-def test_check_handle_timedout_partial_fee(default_conf_usdt, ticker_usdt, open_trade, caplog, fee,
-                                           limit_buy_order_old_partial, trades_for_order,
-                                           limit_buy_order_old_partial_canceled, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_timedout_partial_fee(
+    default_conf_usdt, ticker_usdt, open_trade, caplog, fee, is_short,
+    limit_buy_order_old_partial, trades_for_order,
+    limit_buy_order_old_partial_canceled, mocker
+) -> None:
     rpc_mock = patch_RPCManager(mocker)
     cancel_order_mock = MagicMock(return_value=limit_buy_order_old_partial_canceled)
     mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=0))
@@ -2309,9 +2545,12 @@ def test_check_handle_timedout_partial_fee(default_conf_usdt, ticker_usdt, open_
     assert pytest.approx(trades[0].fee_open) == 0.001
 
 
-def test_check_handle_timedout_partial_except(default_conf_usdt, ticker_usdt, open_trade, caplog,
-                                              fee, limit_buy_order_old_partial, trades_for_order,
-                                              limit_buy_order_old_partial_canceled, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_handle_timedout_partial_except(
+    default_conf_usdt, ticker_usdt, open_trade, caplog, fee, is_short,
+    limit_buy_order_old_partial, trades_for_order,
+    limit_buy_order_old_partial_canceled, mocker
+) -> None:
     rpc_mock = patch_RPCManager(mocker)
     cancel_order_mock = MagicMock(return_value=limit_buy_order_old_partial_canceled)
     patch_exchange(mocker)
@@ -2370,6 +2609,7 @@ def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_tr
 
     Trade.query.session.add(open_trade_usdt)
 
+    caplog.clear()
     freqtrade.check_handle_timedout()
     assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ADA/USDT, amount=30.00000000, "
                       r"is_short=False, leverage=1.0, "
@@ -2379,10 +2619,13 @@ def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_tr
                       caplog)
 
 
-def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_buy_order_usdt) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order,
+                             is_short) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
-    cancel_buy_order = deepcopy(limit_buy_order_usdt)
+    l_order = limit_order[enter_side(is_short)]
+    cancel_buy_order = deepcopy(limit_order[enter_side(is_short)])
     cancel_buy_order['status'] = 'canceled'
     del cancel_buy_order['filled']
 
@@ -2395,36 +2638,39 @@ def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_buy_order_
     trade = MagicMock()
     trade.pair = 'LTC/USDT'
     trade.open_rate = 200
-    limit_buy_order_usdt['filled'] = 0.0
-    limit_buy_order_usdt['status'] = 'open'
+    trade.is_short = False
+    trade.enter_side = "buy"
+    l_order['filled'] = 0.0
+    l_order['status'] = 'open'
     reason = CANCEL_REASON['TIMEOUT']
-    assert freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason)
+    assert freqtrade.handle_cancel_enter(trade, l_order, reason)
     assert cancel_order_mock.call_count == 1
 
     cancel_order_mock.reset_mock()
     caplog.clear()
-    limit_buy_order_usdt['filled'] = 0.01
-    assert not freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason)
+    l_order['filled'] = 0.01
+    assert not freqtrade.handle_cancel_enter(trade, l_order, reason)
     assert cancel_order_mock.call_count == 0
-    assert log_has_re("Order .* for .* not cancelled, as the filled amount.* unsellable.*", caplog)
+    assert log_has_re("Order .* for .* not cancelled, as the filled amount.* unexitable.*", caplog)
 
     caplog.clear()
     cancel_order_mock.reset_mock()
-    limit_buy_order_usdt['filled'] = 2
-    assert not freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason)
+    l_order['filled'] = 2
+    assert not freqtrade.handle_cancel_enter(trade, l_order, reason)
     assert cancel_order_mock.call_count == 1
 
     # Order remained open for some reason (cancel failed)
     cancel_buy_order['status'] = 'open'
     cancel_order_mock = MagicMock(return_value=cancel_buy_order)
     mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock)
-    assert not freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason)
+    assert not freqtrade.handle_cancel_enter(trade, l_order, reason)
     assert log_has_re(r"Order .* for .* not cancelled.", caplog)
 
 
-@pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
-                         indirect=['limit_buy_order_canceled_empty'])
-def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt,
+@ pytest.mark.parametrize("is_short", [False, True])
+@ pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
+                          indirect=['limit_buy_order_canceled_empty'])
+def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, is_short,
                                        limit_buy_order_canceled_empty) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
@@ -2437,22 +2683,25 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt,
     reason = CANCEL_REASON['TIMEOUT']
     trade = MagicMock()
     trade.pair = 'LTC/ETH'
+    trade.enter_side = "buy"
     assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason)
     assert cancel_order_mock.call_count == 0
     assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog)
     assert nofiy_mock.call_count == 1
 
 
-@pytest.mark.parametrize('cancelorder', [
+@ pytest.mark.parametrize("is_short", [False, True])
+@ pytest.mark.parametrize('cancelorder', [
     {},
     {'remaining': None},
     'String Return value',
     123
 ])
-def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_buy_order_usdt,
+def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_order, is_short,
                                           cancelorder) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
+    l_order = limit_order[enter_side(is_short)]
     cancel_order_mock = MagicMock(return_value=cancelorder)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
@@ -2464,16 +2713,18 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_buy_o
 
     trade = MagicMock()
     trade.pair = 'LTC/USDT'
+    trade.enter_side = "buy"
     trade.open_rate = 200
-    limit_buy_order_usdt['filled'] = 0.0
-    limit_buy_order_usdt['status'] = 'open'
+    trade.enter_side = "buy"
+    l_order['filled'] = 0.0
+    l_order['status'] = 'open'
     reason = CANCEL_REASON['TIMEOUT']
-    assert freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason)
+    assert freqtrade.handle_cancel_enter(trade, l_order, reason)
     assert cancel_order_mock.call_count == 1
 
     cancel_order_mock.reset_mock()
-    limit_buy_order_usdt['filled'] = 1.0
-    assert not freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason)
+    l_order['filled'] = 1.0
+    assert not freqtrade.handle_cancel_enter(trade, l_order, reason)
     assert cancel_order_mock.call_count == 1
 
 
@@ -2538,8 +2789,12 @@ def test_handle_cancel_exit_cancel_exception(mocker, default_conf_usdt) -> None:
     assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order'
 
 
-def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker
-                               ) -> None:
+@ pytest.mark.parametrize("is_short, open_rate, amt", [
+    (False, 2.0, 30.0),
+    (True, 2.02, 29.70297029),
+])
+def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker,
+                               ticker_usdt_sell_down, is_short, open_rate, amt) -> None:
     rpc_mock = patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -2550,7 +2805,7 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
     )
     patch_whitelist(mocker, default_conf_usdt)
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=False)
 
     # Create some test data
@@ -2558,25 +2813,31 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
     rpc_mock.reset_mock()
 
     trade = Trade.query.first()
+    assert trade.is_short == is_short
     assert trade
     assert freqtrade.strategy.confirm_trade_exit.call_count == 0
 
     # Increase the price and sell it
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_up
+        fetch_ticker=ticker_usdt_sell_down if is_short else ticker_usdt_sell_up
     )
     # Prevented sell ...
-    freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'],
-                                 sell_reason=SellCheckTuple(sell_type=SellType.ROI))
+    freqtrade.execute_trade_exit(
+        trade=trade,
+        limit=(ticker_usdt_sell_down()['ask'] if is_short else ticker_usdt_sell_up()['bid']),
+        sell_reason=SellCheckTuple(sell_type=SellType.ROI)
+    )
     assert rpc_mock.call_count == 0
     assert freqtrade.strategy.confirm_trade_exit.call_count == 1
 
     # Repatch with true
     freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
-
-    freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'],
-                                 sell_reason=SellCheckTuple(sell_type=SellType.ROI))
+    freqtrade.execute_trade_exit(
+        trade=trade,
+        limit=(ticker_usdt_sell_down()['ask'] if is_short else ticker_usdt_sell_up()['bid']),
+        sell_reason=SellCheckTuple(sell_type=SellType.ROI)
+    )
     assert freqtrade.strategy.confirm_trade_exit.call_count == 1
 
     assert rpc_mock.call_count == 1
@@ -2587,13 +2848,13 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
         'exchange': 'Binance',
         'pair': 'ETH/USDT',
         'gain': 'profit',
-        'limit': 2.2,
-        'amount': 30.0,
+        'limit': 2.0 if is_short else 2.2,
+        'amount': amt,
         'order_type': 'limit',
-        'open_rate': 2.0,
-        'current_rate': 2.3,
-        'profit_amount': 5.685,
-        'profit_ratio': 0.09451372,
+        'open_rate': open_rate,
+        'current_rate': 2.01 if is_short else 2.3,
+        'profit_amount': 0.29554455 if is_short else 5.685,
+        'profit_ratio': 0.00493809 if is_short else 0.09451372,
         'stake_currency': 'USDT',
         'fiat_currency': 'USD',
         'sell_reason': SellType.ROI.value,
@@ -2603,8 +2864,9 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
     } == last_msg
 
 
+@ pytest.mark.parametrize("is_short", [False, True])
 def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down,
-                                 mocker) -> None:
+                                 ticker_usdt_sell_up, mocker, is_short) -> None:
     rpc_mock = patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -2615,22 +2877,23 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd
     )
     patch_whitelist(mocker, default_conf_usdt)
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     # Create some test data
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
 
     # Decrease the price and sell it
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_down
+        fetch_ticker=ticker_usdt_sell_up if is_short else ticker_usdt_sell_down
     )
-
-    freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_down()['bid'],
-                                 sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
+    freqtrade.execute_trade_exit(
+        trade=trade, limit=(ticker_usdt_sell_up if is_short else ticker_usdt_sell_down)()['bid'],
+        sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
 
     assert rpc_mock.call_count == 2
     last_msg = rpc_mock.call_args_list[-1][0][0]
@@ -2640,13 +2903,13 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd
         'exchange': 'Binance',
         'pair': 'ETH/USDT',
         'gain': 'loss',
-        'limit': 2.01,
-        'amount': 30.0,
+        'limit': 2.2 if is_short else 2.01,
+        'amount': 29.70297029 if is_short else 30.0,
         'order_type': 'limit',
-        'open_rate': 2.0,
-        'current_rate': 2.0,
-        'profit_amount': -0.00075,
-        'profit_ratio': -1.247e-05,
+        'open_rate': 2.02 if is_short else 2.0,
+        'current_rate': 2.2 if is_short else 2.0,
+        'profit_amount': -5.65990099 if is_short else -0.00075,
+        'profit_ratio': -0.0945681 if is_short else -1.247e-05,
         'stake_currency': 'USDT',
         'fiat_currency': 'USD',
         'sell_reason': SellType.STOP_LOSS.value,
@@ -2656,8 +2919,14 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd
     } == last_msg
 
 
-def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fee,
-                                              ticker_usdt_sell_up, mocker) -> None:
+@pytest.mark.parametrize(
+    "is_short,amount,open_rate,current_rate,limit,profit_amount,profit_ratio,profit_or_loss", [
+        (False, 30, 2.0, 2.3, 2.25, 7.18125, 0.11938903, 'profit'),
+        (True, 29.70297029, 2.02, 2.2, 2.25, -7.14876237, -0.11944465, 'loss'),  # TODO-lev
+    ])
+def test_execute_trade_exit_custom_exit_price(
+        default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, is_short, amount, open_rate,
+        current_rate, limit, profit_amount, profit_ratio, profit_or_loss, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -2670,7 +2939,7 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe
     config['custom_price_max_distance_ratio'] = 0.1
     patch_whitelist(mocker, config)
     freqtrade = FreqtradeBot(config)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=False)
 
     # Create some test data
@@ -2678,6 +2947,7 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe
     rpc_mock.reset_mock()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
     assert freqtrade.strategy.confirm_trade_exit.call_count == 0
 
@@ -2691,7 +2961,7 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe
 
     # Set a custom exit price
     freqtrade.strategy.custom_exit_price = lambda **kwargs: 2.25
-
+    # TODO-lev: side="buy"
     freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'],
                                  sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL))
 
@@ -2706,14 +2976,14 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe
         'type': RPCMessageType.SELL,
         'exchange': 'Binance',
         'pair': 'ETH/USDT',
-        'gain': 'profit',
-        'limit': 2.25,
-        'amount': 30.0,
+        'gain': profit_or_loss,
+        'limit': limit,
+        'amount': amount,
         'order_type': 'limit',
-        'open_rate': 2.0,
-        'current_rate': 2.3,
-        'profit_amount': 7.18125,
-        'profit_ratio': 0.11938903,
+        'open_rate': open_rate,
+        'current_rate': current_rate,
+        'profit_amount': profit_amount,
+        'profit_ratio': profit_ratio,
         'stake_currency': 'USDT',
         'fiat_currency': 'USD',
         'sell_reason': SellType.SELL_SIGNAL.value,
@@ -2723,8 +2993,10 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe
     } == last_msg
 
 
+@ pytest.mark.parametrize("is_short", [False, True])
 def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
-        default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, mocker) -> None:
+        default_conf_usdt, ticker_usdt, fee, is_short, ticker_usdt_sell_down,
+        ticker_usdt_sell_up, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -2735,27 +3007,29 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
     )
     patch_whitelist(mocker, default_conf_usdt)
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     # Create some test data
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    assert trade.is_short == is_short
     assert trade
 
     # Decrease the price and sell it
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_down
+        fetch_ticker=ticker_usdt_sell_up if is_short else ticker_usdt_sell_down
     )
 
     default_conf_usdt['dry_run'] = True
     freqtrade.strategy.order_types['stoploss_on_exchange'] = True
     # Setting trade stoploss to 0.01
 
-    trade.stop_loss = 2.0 * 0.99
-    freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_down()['bid'],
-                                 sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
+    trade.stop_loss = 2.0 * 1.01 if is_short else 2.0 * 0.99
+    freqtrade.execute_trade_exit(
+        trade=trade, limit=(ticker_usdt_sell_up if is_short else ticker_usdt_sell_down())['bid'],
+        sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
 
     assert rpc_mock.call_count == 2
     last_msg = rpc_mock.call_args_list[-1][0][0]
@@ -2766,13 +3040,13 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
         'exchange': 'Binance',
         'pair': 'ETH/USDT',
         'gain': 'loss',
-        'limit': 1.98,
-        'amount': 30.0,
+        'limit': 2.02 if is_short else 1.98,
+        'amount': 29.70297029 if is_short else 30.0,
         'order_type': 'limit',
-        'open_rate': 2.0,
-        'current_rate': 2.0,
-        'profit_amount': -0.8985,
-        'profit_ratio': -0.01493766,
+        'open_rate': 2.02 if is_short else 2.0,
+        'current_rate': 2.2 if is_short else 2.0,
+        'profit_amount': -0.3 if is_short else -0.8985,
+        'profit_ratio': -0.00501253 if is_short else -0.01493766,
         'stake_currency': 'USDT',
         'fiat_currency': 'USD',
         'sell_reason': SellType.STOP_LOSS.value,
@@ -2810,14 +3084,16 @@ def test_execute_trade_exit_sloe_cancel_exception(
     freqtrade.config['dry_run'] = False
     trade.stoploss_order_id = "abcd"
 
+    # TODO-lev: side="buy"
     freqtrade.execute_trade_exit(trade=trade, limit=1234,
                                  sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
     assert create_order_mock.call_count == 2
     assert log_has('Could not cancel stoploss order abcd', caplog)
 
 
-def test_execute_trade_exit_with_stoploss_on_exchange(default_conf_usdt, ticker_usdt, fee,
-                                                      ticker_usdt_sell_up, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_execute_trade_exit_with_stoploss_on_exchange(
+        default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, is_short, mocker) -> None:
 
     default_conf_usdt['exchange']['name'] = 'binance'
     rpc_mock = patch_RPCManager(mocker)
@@ -2843,12 +3119,13 @@ def test_execute_trade_exit_with_stoploss_on_exchange(default_conf_usdt, ticker_
 
     freqtrade = FreqtradeBot(default_conf_usdt)
     freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     # Create some test data
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
     trades = [trade]
 
@@ -2861,10 +3138,12 @@ def test_execute_trade_exit_with_stoploss_on_exchange(default_conf_usdt, ticker_
         fetch_ticker=ticker_usdt_sell_up
     )
 
+    # TODO-lev: side="buy"
     freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'],
                                  sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
     assert cancel_order.call_count == 1
     assert rpc_mock.call_count == 3
@@ -2942,8 +3221,33 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf_usdt
     assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL
 
 
-def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee,
-                                         ticker_usdt_sell_up, mocker) -> None:
+@pytest.mark.parametrize(
+    "is_short,amount,open_rate,current_rate,limit,profit_amount,profit_ratio,profit_or_loss", [
+        (False, 30, 2.0, 2.3, 2.2, 5.685, 0.09451372, 'profit'),
+        # TODO-lev: Should the current rate be 2.2 for shorts?
+        (True, 29.70297029, 2.02, 2.2, 2.3, -8.63762376, -0.1443212, 'loss'),
+    ])
+def test_execute_trade_exit_market_order(
+    default_conf_usdt, ticker_usdt, fee, is_short, current_rate, amount, open_rate,
+    limit, profit_amount, profit_ratio, profit_or_loss, ticker_usdt_sell_up, mocker
+) -> None:
+    """
+    amount
+        long: 60 / 2.0 = 30
+        short: 60 / 2.02 = 29.70297029
+    open_value
+        long: (30 * 2.0) + (30 * 2.0 * 0.0025) = 60.15
+        short: (29.702970297029704 * 2.02) - (29.702970297029704 * 2.02 * 0.0025) = 59.85
+    close_value
+        long: (30 * 2.2) - (30 * 2.2 * 0.0025) = 65.835
+        short: (29.702970297029704 * 2.3) + (29.702970297029704 * 2.3 * 0.0025) = 68.48762376237624
+    profit
+        long: 65.835 - 60.15 = 5.684999999999995
+        short: 59.85 - 68.48762376237624 = -8.637623762376244
+    profit_ratio
+        long: (65.835/60.15) - 1 = 0.0945137157107232
+        short: 1 - (68.48762376237624/59.85) = -0.1443211990371971
+    """
     rpc_mock = patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -2954,12 +3258,13 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee,
     )
     patch_whitelist(mocker, default_conf_usdt)
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     # Create some test data
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
 
     # Increase the price and sell it
@@ -2969,11 +3274,15 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee,
     )
     freqtrade.config['order_types']['sell'] = 'market'
 
-    freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'],
-                                 sell_reason=SellCheckTuple(sell_type=SellType.ROI))
+    # TODO-lev: side="buy"
+    freqtrade.execute_trade_exit(
+        trade=trade,
+        limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
+        sell_reason=SellCheckTuple(sell_type=SellType.ROI)
+    )
 
     assert not trade.is_open
-    assert trade.close_profit == 0.09451372
+    assert trade.close_profit == profit_ratio
 
     assert rpc_mock.call_count == 3
     last_msg = rpc_mock.call_args_list[-1][0][0]
@@ -2982,14 +3291,14 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee,
         'trade_id': 1,
         'exchange': 'Binance',
         'pair': 'ETH/USDT',
-        'gain': 'profit',
-        'limit': 2.2,
-        'amount': 30.0,
+        'gain': profit_or_loss,
+        'limit': limit,
+        'amount': round(amount, 9),
         'order_type': 'market',
-        'open_rate': 2.0,
-        'current_rate': 2.3,
-        'profit_amount': 5.685,
-        'profit_ratio': 0.09451372,
+        'open_rate': open_rate,
+        'current_rate': current_rate,
+        'profit_amount': profit_amount,
+        'profit_ratio': profit_ratio,
         'stake_currency': 'USDT',
         'fiat_currency': 'USD',
         'sell_reason': SellType.ROI.value,
@@ -3000,7 +3309,8 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee,
     } == last_msg
 
 
-def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_usdt, fee,
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_usdt, fee, is_short,
                                                      ticker_usdt_sell_up, mocker) -> None:
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
     mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds')
@@ -3013,12 +3323,13 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u
             InsufficientFundsError(),
         ]),
     )
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     # Create some test data
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
 
     # Increase the price and sell it
@@ -3028,24 +3339,29 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u
     )
 
     sell_reason = SellCheckTuple(sell_type=SellType.ROI)
+    # TODO-lev: side="buy"
     assert not freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'],
                                             sell_reason=sell_reason)
     assert mock_insuf.call_count == 1
 
 
-@pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,sell_type', [
+@ pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,sell_type,is_short', [
     # Enable profit
-    (True, 1.9, 2.2, False, True, SellType.SELL_SIGNAL.value),
-    # Disable profit
-    (False, 2.9, 3.2, True,  False, SellType.SELL_SIGNAL.value),
-    # Enable loss
-    # * Shouldn't this be SellType.STOP_LOSS.value
-    (True, 0.19, 0.22, False, False, None),
+    (True, 2.18, 2.2, False, True, SellType.SELL_SIGNAL.value, False),
+    (True, 2.18, 2.2, False, True, SellType.SELL_SIGNAL.value, True),
+    # # Disable profit
+    (False, 3.19, 3.2, True,  False, SellType.SELL_SIGNAL.value, False),
+    (False, 3.19, 3.2, True,  False, SellType.SELL_SIGNAL.value, True),
+    # # Enable loss
+    # # * Shouldn't this be SellType.STOP_LOSS.value
+    (True, 0.21, 0.22, False, False, None, False),
+    (True, 2.41, 2.42, False, False, None, True),
     # Disable loss
-    (False, 0.10, 0.22, True, False, SellType.SELL_SIGNAL.value),
+    (False, 0.10, 0.22, True, False, SellType.SELL_SIGNAL.value, False),
+    (False, 0.10, 0.22, True, False, SellType.SELL_SIGNAL.value, True),
 ])
 def test_sell_profit_only(
-        default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open,
+        default_conf_usdt, limit_order, limit_order_open, is_short,
         fee, mocker, profit_only, bid, ask, handle_first, handle_second, sell_type) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
@@ -3057,7 +3373,7 @@ def test_sell_profit_only(
             'last': bid
         }),
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
+            limit_order_open[enter_side(is_short)],
             {'id': 1234553382},
         ]),
         get_fee=fee,
@@ -3068,7 +3384,7 @@ def test_sell_profit_only(
         'sell_profit_offset': 0.1,
     })
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     if sell_type == SellType.SELL_SIGNAL.value:
         freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
     else:
@@ -3077,9 +3393,10 @@ def test_sell_profit_only(
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
-    trade.update(limit_buy_order_usdt)
+    trade.is_short = is_short
+    trade.update(limit_order[enter_side(is_short)])
     freqtrade.wallets.update()
-    patch_get_signal(freqtrade, enter_long=False, exit_long=True)
+    patch_get_signal(freqtrade, enter_long=False, exit_short=is_short, exit_long=not is_short)
     assert freqtrade.handle_trade(trade) is handle_first
 
     if handle_second:
@@ -3087,7 +3404,7 @@ def test_sell_profit_only(
         assert freqtrade.handle_trade(trade) is True
 
 
-def test_sell_not_enough_balance(default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open,
+def test_sell_not_enough_balance(default_conf_usdt, limit_order, limit_order_open,
                                  fee, mocker, caplog) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
@@ -3099,7 +3416,7 @@ def test_sell_not_enough_balance(default_conf_usdt, limit_buy_order_usdt, limit_
             'last': 0.00002172
         }),
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
+            limit_order_open['buy'],
             {'id': 1234553382},
         ]),
         get_fee=fee,
@@ -3113,7 +3430,7 @@ def test_sell_not_enough_balance(default_conf_usdt, limit_buy_order_usdt, limit_
 
     trade = Trade.query.first()
     amnt = trade.amount
-    trade.update(limit_buy_order_usdt)
+    trade.update(limit_order['buy'])
     patch_get_signal(freqtrade, enter_long=False, exit_long=True)
     mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985))
 
@@ -3122,7 +3439,7 @@ def test_sell_not_enough_balance(default_conf_usdt, limit_buy_order_usdt, limit_
     assert trade.amount != amnt
 
 
-@pytest.mark.parametrize('amount_wallet,has_err', [
+@ pytest.mark.parametrize('amount_wallet,has_err', [
     (95.29, False),
     (91.29, True)
 ])
@@ -3159,8 +3476,9 @@ def test__safe_exit_amount(default_conf_usdt, fee, caplog, mocker, amount_wallet
         assert wallet_update.call_count == 1
 
 
-def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, mocker,
-                      caplog) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_locked_pairs(default_conf_usdt, ticker_usdt, fee,
+                      ticker_usdt_sell_down, mocker, caplog, is_short) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -3169,12 +3487,13 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down
         get_fee=fee,
     )
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
 
     # Create some test data
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
+    trade.is_short = is_short
     assert trade
 
     # Decrease the price and sell it
@@ -3183,6 +3502,7 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down
         fetch_ticker=ticker_usdt_sell_down
     )
 
+    # TODO-lev: side="buy"
     freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_down()['bid'],
                                  sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
     trade.close(ticker_usdt_sell_down()['bid'])
@@ -3195,8 +3515,9 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down
     assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog)
 
 
-def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt,
-                                  limit_buy_order_usdt_open, fee, mocker) -> None:
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_open, is_short,
+                                  fee, mocker) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -3207,7 +3528,7 @@ def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt,
             'last': 2.19
         }),
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
+            limit_order_open[enter_side(is_short)],
             {'id': 1234553382},
         ]),
         get_fee=fee,
@@ -3215,25 +3536,37 @@ def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt,
     default_conf_usdt['ignore_roi_if_buy_signal'] = True
 
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
 
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
-    trade.update(limit_buy_order_usdt)
+    trade.is_short = is_short
+    trade.update(limit_order[enter_side(is_short)])
     freqtrade.wallets.update()
-    patch_get_signal(freqtrade, enter_long=True, exit_long=True)
+    if is_short:
+        patch_get_signal(freqtrade, enter_long=False, enter_short=True, exit_short=True)
+    else:
+        patch_get_signal(freqtrade, enter_long=True, exit_long=True)
+
     assert freqtrade.handle_trade(trade) is False
 
     # Test if buy-signal is absent (should sell due to roi = true)
-    patch_get_signal(freqtrade, enter_long=False, exit_long=True)
+    if is_short:
+        patch_get_signal(freqtrade, enter_long=False, exit_short=True)
+    else:
+        patch_get_signal(freqtrade, enter_long=False, exit_long=True)
     assert freqtrade.handle_trade(trade) is True
     assert trade.sell_reason == SellType.ROI.value
 
 
-def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open,
-                            fee, caplog, mocker) -> None:
+@ pytest.mark.parametrize("is_short,val1,val2", [
+    (False, 1.5, 1.1),
+    (True, 0.5, 0.9)
+])
+def test_trailing_stop_loss(default_conf_usdt, limit_order_open,
+                            is_short, val1, val2, fee, caplog, mocker) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -3244,7 +3577,7 @@ def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open,
             'last': 2.0
         }),
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
+            limit_order_open[enter_side(is_short)],
             {'id': 1234553382},
         ]),
         get_fee=fee,
@@ -3252,19 +3585,20 @@ def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open,
     default_conf_usdt['trailing_stop'] = True
     patch_whitelist(mocker, default_conf_usdt)
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
 
     freqtrade.enter_positions()
     trade = Trade.query.first()
+    assert trade.is_short == is_short
     assert freqtrade.handle_trade(trade) is False
 
-    # Raise ticker_usdt above buy price
+    # Raise praise into profits
     mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
                  MagicMock(return_value={
-                     'bid': 2.0 * 1.5,
-                     'ask': 2.0 * 1.5,
-                     'last': 2.0 * 1.5
+                     'bid': 2.0 * val1,
+                     'ask': 2.0 * val1,
+                     'last': 2.0 * val1
                  }))
 
     # Stoploss should be adjusted
@@ -3273,40 +3607,46 @@ def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open,
     # Price fell
     mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
                  MagicMock(return_value={
-                     'bid': 2.0 * 1.1,
-                     'ask': 2.0 * 1.1,
-                     'last': 2.0 * 1.1
+                     'bid': 2.0 * val2,
+                     'ask': 2.0 * val2,
+                     'last': 2.0 * val2
                  }))
 
     caplog.set_level(logging.DEBUG)
     # Sell as trailing-stop is reached
     assert freqtrade.handle_trade(trade) is True
-    assert log_has("ETH/USDT - HIT STOP: current price at 2.200000, stoploss is 2.700000, "
-                   "initial stoploss was at 1.800000, trade opened at 2.000000", caplog)
+    stop_multi = 1.1 if is_short else 0.9
+    assert log_has(f"ETH/USDT - HIT STOP: current price at {(2.0 * val2):6f}, "
+                   f"stoploss is {(2.0 * val1 * stop_multi):6f}, "
+                   f"initial stoploss was at {(2.0 * stop_multi):6f}, trade opened at 2.000000",
+                   caplog)
     assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
 
 
-@pytest.mark.parametrize('offset,trail_if_reached,second_sl', [
-    (0, False, 2.0394),
-    (0.011, False, 2.0394),
-    (0.055, True, 1.8),
+@ pytest.mark.parametrize('offset,trail_if_reached,second_sl,is_short', [
+    (0, False, 2.0394, False),
+    (0.011, False, 2.0394, False),
+    (0.055, True, 1.8, False),
+    (0, False, 2.1614, True),
+    (0.011, False, 2.1614, True),
+    (0.055, True, 2.42, True),
 ])
 def test_trailing_stop_loss_positive(
-    default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open,
-    offset, fee, caplog, mocker, trail_if_reached, second_sl
+    default_conf_usdt, limit_order, limit_order_open,
+    offset, fee, caplog, mocker, trail_if_reached, second_sl, is_short
 ) -> None:
-    buy_price = limit_buy_order_usdt['price']
+    enter_price = limit_order[enter_side(is_short)]['price']
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=MagicMock(return_value={
-            'bid': buy_price - 0.01,
-            'ask': buy_price - 0.01,
-            'last': buy_price - 0.01
+            'bid': enter_price - (-0.01 if is_short else 0.01),
+            'ask': enter_price - (-0.01 if is_short else 0.01),
+            'last': enter_price - (-0.01 if is_short else 0.01),
         }),
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
+            limit_order_open[enter_side(is_short)],
             {'id': 1234553382},
         ]),
         get_fee=fee,
@@ -3319,12 +3659,13 @@ def test_trailing_stop_loss_positive(
     patch_whitelist(mocker, default_conf_usdt)
 
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
-    trade.update(limit_buy_order_usdt)
+    trade.is_short = is_short
+    trade.update(limit_order[enter_side(is_short)])
     caplog.set_level(logging.DEBUG)
     # stop-loss not reached
     assert freqtrade.handle_trade(trade) is False
@@ -3333,34 +3674,36 @@ def test_trailing_stop_loss_positive(
     mocker.patch(
         'freqtrade.exchange.Exchange.fetch_ticker',
         MagicMock(return_value={
-            'bid': buy_price + 0.06,
-            'ask': buy_price + 0.06,
-            'last': buy_price + 0.06
+            'bid': enter_price + (-0.06 if is_short else 0.06),
+            'ask': enter_price + (-0.06 if is_short else 0.06),
+            'last': enter_price + (-0.06 if is_short else 0.06),
         })
     )
     # stop-loss not reached, adjusted stoploss
     assert freqtrade.handle_trade(trade) is False
-    caplog_text = f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 0.0249%"
+    caplog_text = (f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: "
+                   f"{'0.0249' if not is_short else '0.0224'}%")
     if trail_if_reached:
         assert not log_has(caplog_text, caplog)
         assert not log_has("ETH/USDT - Adjusting stoploss...", caplog)
     else:
         assert log_has(caplog_text, caplog)
         assert log_has("ETH/USDT - Adjusting stoploss...", caplog)
-    assert trade.stop_loss == second_sl
+    assert pytest.approx(trade.stop_loss) == second_sl
     caplog.clear()
 
     mocker.patch(
         'freqtrade.exchange.Exchange.fetch_ticker',
         MagicMock(return_value={
-            'bid': buy_price + 0.125,
-            'ask': buy_price + 0.125,
-            'last': buy_price + 0.125,
+            'bid': enter_price + (-0.135 if is_short else 0.125),
+            'ask': enter_price + (-0.135 if is_short else 0.125),
+            'last': enter_price + (-0.135 if is_short else 0.125),
         })
     )
     assert freqtrade.handle_trade(trade) is False
     assert log_has(
-        f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 0.0572%",
+        f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: "
+        f"{'0.0572' if not is_short else '0.0567'}%",
         caplog
     )
     assert log_has("ETH/USDT - Adjusting stoploss...", caplog)
@@ -3368,22 +3711,25 @@ def test_trailing_stop_loss_positive(
     mocker.patch(
         'freqtrade.exchange.Exchange.fetch_ticker',
         MagicMock(return_value={
-            'bid': buy_price + 0.02,
-            'ask': buy_price + 0.02,
-            'last': buy_price + 0.02
+            'bid': enter_price + (-0.02 if is_short else 0.02),
+            'ask': enter_price + (-0.02 if is_short else 0.02),
+            'last': enter_price + (-0.02 if is_short else 0.02),
         })
     )
     # Lower price again (but still positive)
     assert freqtrade.handle_trade(trade) is True
     assert log_has(
-        f"ETH/USDT - HIT STOP: current price at {buy_price + 0.02:.6f}, "
+        f"ETH/USDT - HIT STOP: current price at {enter_price + (-0.02 if is_short else 0.02):.6f}, "
         f"stoploss is {trade.stop_loss:.6f}, "
-        f"initial stoploss was at 1.800000, trade opened at 2.000000", caplog)
+        f"initial stoploss was at {'2.42' if is_short else '1.80'}0000, "
+        f"trade opened at {2.2 if is_short else 2.0}00000",
+        caplog)
     assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
 
 
-def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt,
-                                          limit_buy_order_usdt_open, fee, mocker) -> None:
+@pytest.mark.parametrize("is_short", [False, True])
+def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_open,
+                                          is_short, fee, mocker) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(
@@ -3394,7 +3740,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usd
             'last': 2.0
         }),
         create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
+            limit_order_open[enter_side(is_short)],
             {'id': 1234553382},
             {'id': 1234553383}
         ]),
@@ -3405,19 +3751,21 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usd
         'ignore_roi_if_buy_signal': False
     }
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
 
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
-    trade.update(limit_buy_order_usdt)
+    trade.is_short = is_short
+    trade.update(limit_order[enter_side(is_short)])
     # Sell due to min_roi_reached
-    patch_get_signal(freqtrade, enter_long=True, exit_long=True)
+    patch_get_signal(freqtrade, enter_long=not is_short, exit_long=not is_short,
+                     enter_short=is_short, exit_short=is_short)
     assert freqtrade.handle_trade(trade) is True
 
     # Test if buy-signal is absent
-    patch_get_signal(freqtrade, enter_long=False, exit_long=True)
+    patch_get_signal(freqtrade, enter_long=False, exit_long=not is_short, exit_short=is_short)
     assert freqtrade.handle_trade(trade) is True
     assert trade.sell_reason == SellType.ROI.value
 
@@ -3497,7 +3845,7 @@ def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mock
     )
 
 
-@pytest.mark.parametrize(
+@ pytest.mark.parametrize(
     'fee_par,fee_reduction_amount,use_ticker_usdt_rate,expected_log', [
         # basic, amount does not change
         ({'cost': 0.008, 'currency': 'ETH'}, 0, False, None),
@@ -3550,7 +3898,7 @@ def test_get_real_amount(
         assert log_has(expected_log, caplog)
 
 
-@pytest.mark.parametrize(
+@ pytest.mark.parametrize(
     'fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount', [
         # basic, amount is reduced by fee
         (None, None, 0.001, 0.001, 7.992),
@@ -3681,7 +4029,7 @@ def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_ord
     )
 
 
-def test_get_real_amount_open_trade(default_conf_usdt, fee, mocker):
+def test_get_real_amount_open_trade_usdt(default_conf_usdt, fee, mocker):
     amount = 12345
     trade = Trade(
         pair='LTC/ETH',
@@ -3702,7 +4050,7 @@ def test_get_real_amount_open_trade(default_conf_usdt, fee, mocker):
     assert freqtrade.get_real_amount(trade, order) == amount
 
 
-@pytest.mark.parametrize('amount,fee_abs,wallet,amount_exp', [
+@ pytest.mark.parametrize('amount,fee_abs,wallet,amount_exp', [
     (8.0, 0.0, 10, 8),
     (8.0, 0.0, 0, 8),
     (8.0, 0.1, 0, 7.9),
@@ -3731,13 +4079,17 @@ def test_apply_fee_conditional(default_conf_usdt, fee, mocker,
     assert walletmock.call_count == 1
 
 
-@pytest.mark.parametrize("delta, is_high_delta", [
+@ pytest.mark.parametrize("delta, is_high_delta", [
     (0.1, False),
     (100, True),
 ])
+@ pytest.mark.parametrize('is_short, open_rate', [
+    (False, 2.0),
+    (True, 2.02),
+])
 def test_order_book_depth_of_market(
-    default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt,
-    fee, mocker, order_book_l2, delta, is_high_delta
+    default_conf_usdt, ticker_usdt, limit_order, limit_order_open,
+    fee, mocker, order_book_l2, delta, is_high_delta, is_short, open_rate
 ):
     default_conf_usdt['bid_strategy']['check_depth_of_market']['enabled'] = True
     default_conf_usdt['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = delta
@@ -3747,20 +4099,21 @@ def test_order_book_depth_of_market(
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
+        create_order=MagicMock(return_value=limit_order_open[enter_side(is_short)]),
         get_fee=fee,
     )
 
     # Save state of current whitelist
     whitelist = deepcopy(default_conf_usdt['exchange']['pair_whitelist'])
     freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
+    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
     freqtrade.enter_positions()
 
     trade = Trade.query.first()
     if is_high_delta:
         assert trade is None
     else:
+        trade.is_short = is_short
         assert trade is not None
         assert trade.stake_amount == 60.0
         assert trade.is_open
@@ -3770,13 +4123,13 @@ def test_order_book_depth_of_market(
         assert len(Trade.query.all()) == 1
 
         # Simulate fulfilled LIMIT_BUY order for trade
-        trade.update(limit_buy_order_usdt)
+        trade.update(limit_order_open[enter_side(is_short)])
 
-        assert trade.open_rate == 2.0
+        assert trade.open_rate == open_rate  # TODO-lev: double check
         assert whitelist == default_conf_usdt['exchange']['pair_whitelist']
 
 
-@pytest.mark.parametrize('exception_thrown,ask,last,order_book_top,order_book', [
+@ pytest.mark.parametrize('exception_thrown,ask,last,order_book_top,order_book', [
     (False, 0.045, 0.046, 2, None),
     (True,  0.042, 0.046, 1, {'bids': [[]], 'asks': [[]]})
 ])
@@ -3809,7 +4162,7 @@ def test_order_book_bid_strategy1(mocker, default_conf_usdt, order_book_l2, exce
         assert ticker_usdt_mock.call_count == 0
 
 
-def test_check_depth_of_market_buy(default_conf_usdt, mocker, order_book_l2) -> None:
+def test_check_depth_of_market(default_conf_usdt, mocker, order_book_l2) -> None:
     """
     test check depth of market
     """
@@ -3826,11 +4179,12 @@ def test_check_depth_of_market_buy(default_conf_usdt, mocker, order_book_l2) ->
     freqtrade = FreqtradeBot(default_conf_usdt)
 
     conf = default_conf_usdt['bid_strategy']['check_depth_of_market']
-    assert freqtrade._check_depth_of_market_buy('ETH/USDT', conf) is False
+    assert freqtrade._check_depth_of_market('ETH/BTC', conf, side=SignalDirection.LONG) is False
 
 
+@ pytest.mark.parametrize('is_short', [False, True])
 def test_order_book_ask_strategy(
-        default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee,
+        default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, is_short,
         limit_sell_order_usdt_open, mocker, order_book_l2, caplog) -> None:
     """
     test order book ask strategy
@@ -3868,7 +4222,10 @@ def test_order_book_ask_strategy(
     freqtrade.wallets.update()
     assert trade.is_open is True
 
-    patch_get_signal(freqtrade, enter_long=False, exit_long=True)
+    if is_short:
+        patch_get_signal(freqtrade, enter_long=False,  exit_short=True)
+    else:
+        patch_get_signal(freqtrade, enter_long=False, exit_long=True)
     assert freqtrade.handle_trade(trade) is True
     assert trade.close_rate_requested == order_book_l2.return_value['asks'][0][0]
 
@@ -3906,7 +4263,7 @@ def test_startup_trade_reinit(default_conf_usdt, edge_conf, mocker):
     assert reinit_mock.call_count == 0
 
 
-@pytest.mark.usefixtures("init_persistence")
+@ pytest.mark.usefixtures("init_persistence")
 def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_buy_order_usdt_open,
                              caplog):
     default_conf_usdt['dry_run'] = True
@@ -3939,38 +4296,46 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_
                       caplog)
 
 
-@pytest.mark.usefixtures("init_persistence")
-def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_buy_order_usdt,
-                                limit_sell_order_usdt):
+@ pytest.mark.usefixtures("init_persistence")
+@ pytest.mark.parametrize("is_short,buy_calls,sell_calls", [
+    (False, 1, 2),
+    (True, 2, 1),
+])
+def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_order, limit_order_open,
+                                is_short, buy_calls, sell_calls):
     default_conf_usdt['cancel_open_orders_on_exit'] = True
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
-                 side_effect=[
-                     ExchangeError(),
-                     limit_sell_order_usdt,
-                     limit_buy_order_usdt,
-                     limit_sell_order_usdt
-                 ])
+    mocker.patch(
+        'freqtrade.exchange.Exchange.fetch_order',
+        side_effect=[
+            ExchangeError(),
+            limit_order[exit_side(is_short)],
+            limit_order_open[enter_side(is_short)],
+            limit_order_open[exit_side(is_short)],
+        ]
+    )
     buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_enter')
     sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit')
 
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    create_mock_trades(fee)
+    create_mock_trades(fee, is_short=is_short)
     trades = Trade.query.all()
     assert len(trades) == MOCK_TRADE_COUNT
     freqtrade.cancel_all_open_orders()
-    assert buy_mock.call_count == 1
-    assert sell_mock.call_count == 2
+    assert buy_mock.call_count == buy_calls
+    assert sell_mock.call_count == sell_calls
 
 
-@pytest.mark.usefixtures("init_persistence")
-def test_check_for_open_trades(mocker, default_conf_usdt, fee):
+@ pytest.mark.usefixtures("init_persistence")
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_check_for_open_trades(mocker, default_conf_usdt, fee, is_short):
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
 
     freqtrade.check_for_open_trades()
     assert freqtrade.rpc.send_msg.call_count == 0
 
-    create_mock_trades(fee)
+    create_mock_trades(fee, is_short)
     trade = Trade.query.first()
+    trade.is_short = is_short
     trade.is_open = True
 
     freqtrade.check_for_open_trades()
@@ -3978,10 +4343,11 @@ def test_check_for_open_trades(mocker, default_conf_usdt, fee):
     assert 'Handle these trades manually' in freqtrade.rpc.send_msg.call_args[0][0]['status']
 
 
-@pytest.mark.usefixtures("init_persistence")
-def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog):
+@ pytest.mark.parametrize("is_short", [False, True])
+@ pytest.mark.usefixtures("init_persistence")
+def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog, is_short):
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    create_mock_trades(fee)
+    create_mock_trades(fee, is_short=is_short)
 
     freqtrade.startup_update_open_orders()
     assert not log_has_re(r"Error updating Order .*", caplog)
@@ -3994,7 +4360,7 @@ def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog):
     caplog.clear()
 
     assert len(Order.get_open_orders()) == 3
-    matching_buy_order = mock_order_4()
+    matching_buy_order = mock_order_4(is_short=is_short)
     matching_buy_order.update({
         'status': 'closed',
     })
@@ -4004,8 +4370,9 @@ def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog):
     assert len(Order.get_open_orders()) == 2
 
 
-@pytest.mark.usefixtures("init_persistence")
-def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, fee):
+@ pytest.mark.usefixtures("init_persistence")
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, fee, is_short):
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
 
     def patch_with_fee(order):
@@ -4015,19 +4382,20 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, f
 
     mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
                  side_effect=[
-                     patch_with_fee(mock_order_2_sell()),
-                     patch_with_fee(mock_order_3_sell()),
-                     patch_with_fee(mock_order_1()),
-                     patch_with_fee(mock_order_2()),
-                     patch_with_fee(mock_order_3()),
-                     patch_with_fee(mock_order_4()),
+                     patch_with_fee(mock_order_2_sell(is_short=is_short)),
+                     patch_with_fee(mock_order_3_sell(is_short=is_short)),
+                     patch_with_fee(mock_order_1(is_short=is_short)),
+                     patch_with_fee(mock_order_2(is_short=is_short)),
+                     patch_with_fee(mock_order_3(is_short=is_short)),
+                     patch_with_fee(mock_order_4(is_short=is_short)),
                  ]
                  )
 
-    create_mock_trades(fee)
+    create_mock_trades(fee, is_short=is_short)
     trades = Trade.get_trades().all()
     assert len(trades) == MOCK_TRADE_COUNT
     for trade in trades:
+        trade.is_short = is_short
         assert trade.fee_open_cost is None
         assert trade.fee_open_currency is None
         assert trade.fee_close_cost is None
@@ -4054,7 +4422,7 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, f
     for trade in trades:
         if trade.is_open:
             # Exclude Trade 4 - as the order is still open.
-            if trade.select_order('buy', False):
+            if trade.select_order(enter_side(is_short), False):
                 assert trade.fee_open_cost is not None
                 assert trade.fee_open_currency is not None
             else:
@@ -4066,20 +4434,29 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, f
             assert trade.fee_close_currency is not None
 
 
-@pytest.mark.usefixtures("init_persistence")
-def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog):
+@ pytest.mark.usefixtures("init_persistence")
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog, is_short):
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
     mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state')
 
-    create_mock_trades(fee)
+    create_mock_trades(fee, is_short=is_short)
     trades = Trade.get_trades().all()
 
     freqtrade.reupdate_enter_order_fees(trades[0])
-    assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
+    assert log_has_re(
+        f"Trying to reupdate {enter_side(is_short)} "
+        r"fees for .*",
+        caplog
+    )
     assert mock_uts.call_count == 1
     assert mock_uts.call_args_list[0][0][0] == trades[0]
-    assert mock_uts.call_args_list[0][0][1] == mock_order_1()['id']
-    assert log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog)
+    assert mock_uts.call_args_list[0][0][1] == mock_order_1(is_short=is_short)['id']
+    assert log_has_re(
+        f"Updating {enter_side(is_short)}-fee on trade "
+        r".* for order .*\.",
+        caplog
+    )
     mock_uts.reset_mock()
     caplog.clear()
 
@@ -4094,21 +4471,24 @@ def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog):
         amount=30,
         open_rate=2.0,
         exchange='binance',
+        is_short=is_short
     )
     Trade.query.session.add(trade)
 
     freqtrade.reupdate_enter_order_fees(trade)
-    assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
+    assert log_has_re(f"Trying to reupdate {enter_side(is_short)} fees for "
+                      r".*", caplog)
     assert mock_uts.call_count == 0
-    assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog)
+    assert not log_has_re(f"Updating {enter_side(is_short)}-fee on trade "
+                          r".* for order .*\.", caplog)
 
 
-@pytest.mark.usefixtures("init_persistence")
+@ pytest.mark.usefixtures("init_persistence")
 def test_handle_insufficient_funds(mocker, default_conf_usdt, fee):
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
     mock_rlo = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.refind_lost_order')
     mock_bof = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.reupdate_enter_order_fees')
-    create_mock_trades(fee)
+    create_mock_trades(fee, is_short=False)
     trades = Trade.get_trades().all()
 
     # Trade 0 has only a open buy order, no closed order
@@ -4141,8 +4521,9 @@ def test_handle_insufficient_funds(mocker, default_conf_usdt, fee):
     assert mock_bof.call_count == 1
 
 
-@pytest.mark.usefixtures("init_persistence")
-def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
+@ pytest.mark.usefixtures("init_persistence")
+@ pytest.mark.parametrize("is_short", [False, True])
+def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog, is_short):
     caplog.set_level(logging.DEBUG)
     freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
     mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state')
@@ -4153,8 +4534,9 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
     def reset_open_orders(trade):
         trade.open_order_id = None
         trade.stoploss_order_id = None
+        trade.is_short = is_short
 
-    create_mock_trades(fee)
+    create_mock_trades(fee, is_short=is_short)
     trades = Trade.get_trades().all()
 
     caplog.clear()
@@ -4166,7 +4548,7 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
     assert trade.stoploss_order_id is None
 
     freqtrade.refind_lost_order(trade)
-    order = mock_order_1()
+    order = mock_order_1(is_short=is_short)
     assert log_has_re(r"Order Order(.*order_id=" + order['id'] + ".*) is no longer open.", caplog)
     assert mock_fo.call_count == 0
     assert mock_uts.call_count == 0
@@ -4184,7 +4566,7 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
     assert trade.stoploss_order_id is None
 
     freqtrade.refind_lost_order(trade)
-    order = mock_order_4()
+    order = mock_order_4(is_short=is_short)
     assert log_has_re(r"Trying to refind Order\(.*", caplog)
     assert mock_fo.call_count == 0
     assert mock_uts.call_count == 0
@@ -4202,7 +4584,7 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
     assert trade.stoploss_order_id is None
 
     freqtrade.refind_lost_order(trade)
-    order = mock_order_5_stoploss()
+    order = mock_order_5_stoploss(is_short=is_short)
     assert log_has_re(r"Trying to refind Order\(.*", caplog)
     assert mock_fo.call_count == 1
     assert mock_uts.call_count == 1
@@ -4221,7 +4603,7 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
     assert trade.stoploss_order_id is None
 
     freqtrade.refind_lost_order(trade)
-    order = mock_order_6_sell()
+    order = mock_order_6_sell(is_short=is_short)
     assert log_has_re(r"Trying to refind Order\(.*", caplog)
     assert mock_fo.call_count == 1
     assert mock_uts.call_count == 1
@@ -4234,7 +4616,7 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
     # Test error case
     mock_fo = mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
                            side_effect=ExchangeError())
-    order = mock_order_5_stoploss()
+    order = mock_order_5_stoploss(is_short=is_short)
 
     freqtrade.refind_lost_order(trades[4])
     assert log_has(f"Error updating {order['id']}.", caplog)
@@ -4280,20 +4662,25 @@ def test_get_valid_price(mocker, default_conf_usdt) -> None:
     assert valid_price_at_min_alwd < proposed_price
 
 
+def test_leverage_prep():
+    # TODO-lev
+    return
+
+
 @pytest.mark.parametrize('trading_mode,calls,t1,t2', [
-    (TradingMode.SPOT, 0, "2021-09-01 00:00:00", "2021-09-01 08:00:00"),
-    (TradingMode.MARGIN, 0, "2021-09-01 00:00:00", "2021-09-01 08:00:00"),
-    (TradingMode.FUTURES, 31, "2021-09-01 00:00:02", "2021-09-01 08:00:01"),
-    (TradingMode.FUTURES, 32, "2021-09-01 00:00:00", "2021-09-01 08:00:01"),
-    (TradingMode.FUTURES, 32, "2021-09-01 00:00:02", "2021-09-01 08:00:02"),
-    (TradingMode.FUTURES, 33, "2021-09-01 00:00:00", "2021-09-01 08:00:02"),
-    (TradingMode.FUTURES, 33, "2021-08-31 23:59:59", "2021-09-01 08:00:02"),
-    (TradingMode.FUTURES, 33, "2021-08-31 23:59:59", "2021-09-01 08:00:03"),
-    (TradingMode.FUTURES, 33, "2021-08-31 23:59:59", "2021-09-01 08:00:04"),
-    (TradingMode.FUTURES, 33, "2021-08-31 23:59:59", "2021-09-01 08:00:05"),
-    (TradingMode.FUTURES, 33, "2021-08-31 23:59:59", "2021-09-01 08:00:06"),
-    (TradingMode.FUTURES, 33, "2021-08-31 23:59:59", "2021-09-01 08:00:07"),
-    (TradingMode.FUTURES, 33, "2021-08-31 23:59:58", "2021-09-01 08:00:07"),
+    ('spot', 0, "2021-09-01 00:00:00", "2021-09-01 08:00:00"),
+    ('margin', 0, "2021-09-01 00:00:00", "2021-09-01 08:00:00"),
+    ('futures', 31, "2021-09-01 00:00:02", "2021-09-01 08:00:01"),
+    ('futures', 32, "2021-09-01 00:00:00", "2021-09-01 08:00:01"),
+    ('futures', 32, "2021-09-01 00:00:02", "2021-09-01 08:00:02"),
+    ('futures', 33, "2021-09-01 00:00:00", "2021-09-01 08:00:02"),
+    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:02"),
+    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:03"),
+    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:04"),
+    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:05"),
+    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:06"),
+    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:07"),
+    ('futures', 33, "2021-08-31 23:59:58", "2021-09-01 08:00:07"),
 ])
 def test_update_funding_fees(mocker, default_conf, trading_mode, calls, time_machine,
                              t1, t2):
diff --git a/tests/test_persistence.py b/tests/test_persistence.py
index 7128fcd89..6f9bd6555 100644
--- a/tests/test_persistence.py
+++ b/tests/test_persistence.py
@@ -1514,11 +1514,12 @@ def test_adjust_min_max_rates(fee):
 
 @pytest.mark.usefixtures("init_persistence")
 @pytest.mark.parametrize('use_db', [True, False])
-def test_get_open(fee, use_db):
+@pytest.mark.parametrize('is_short', [True, False])
+def test_get_open(fee, is_short, use_db):
     Trade.use_db = use_db
     Trade.reset_trades()
 
-    create_mock_trades(fee, use_db)
+    create_mock_trades(fee, is_short, use_db)
     assert len(Trade.get_open_trades()) == 4
 
     Trade.use_db = True
@@ -1874,14 +1875,15 @@ def test_fee_updated(fee):
 
 
 @pytest.mark.usefixtures("init_persistence")
+@pytest.mark.parametrize('is_short', [True, False])
 @pytest.mark.parametrize('use_db', [True, False])
-def test_total_open_trades_stakes(fee, use_db):
+def test_total_open_trades_stakes(fee, is_short, use_db):
 
     Trade.use_db = use_db
     Trade.reset_trades()
     res = Trade.total_open_trades_stakes()
     assert res == 0
-    create_mock_trades(fee, use_db)
+    create_mock_trades(fee, is_short, use_db)
     res = Trade.total_open_trades_stakes()
     assert res == 0.004
 
@@ -1889,6 +1891,7 @@ def test_total_open_trades_stakes(fee, use_db):
 
 
 @pytest.mark.usefixtures("init_persistence")
+# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
 @pytest.mark.parametrize('use_db', [True, False])
 def test_get_total_closed_profit(fee, use_db):
 
@@ -1896,7 +1899,7 @@ def test_get_total_closed_profit(fee, use_db):
     Trade.reset_trades()
     res = Trade.get_total_closed_profit()
     assert res == 0
-    create_mock_trades(fee, use_db)
+    create_mock_trades(fee, False, use_db)
     res = Trade.get_total_closed_profit()
     assert res == 0.000739127
 
@@ -1904,11 +1907,12 @@ def test_get_total_closed_profit(fee, use_db):
 
 
 @pytest.mark.usefixtures("init_persistence")
+# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
 @pytest.mark.parametrize('use_db', [True, False])
 def test_get_trades_proxy(fee, use_db):
     Trade.use_db = use_db
     Trade.reset_trades()
-    create_mock_trades(fee, use_db)
+    create_mock_trades(fee, False, use_db)
     trades = Trade.get_trades_proxy()
     assert len(trades) == 6
 
@@ -1937,9 +1941,10 @@ def test_get_trades_backtest():
 
 
 @pytest.mark.usefixtures("init_persistence")
+# @pytest.mark.parametrize('is_short', [True, False])
 def test_get_overall_performance(fee):
 
-    create_mock_trades(fee)
+    create_mock_trades(fee, False)
     res = Trade.get_overall_performance()
 
     assert len(res) == 2
@@ -1949,12 +1954,13 @@ def test_get_overall_performance(fee):
 
 
 @pytest.mark.usefixtures("init_persistence")
+# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
 def test_get_best_pair(fee):
 
     res = Trade.get_best_pair()
     assert res is None
 
-    create_mock_trades(fee)
+    create_mock_trades(fee, False)
     res = Trade.get_best_pair()
     assert len(res) == 2
     assert res[0] == 'XRP/BTC'
@@ -2036,8 +2042,9 @@ def test_update_order_from_ccxt(caplog):
 
 
 @pytest.mark.usefixtures("init_persistence")
+# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
 def test_select_order(fee):
-    create_mock_trades(fee)
+    create_mock_trades(fee, False)
 
     trades = Trade.get_trades().all()