merged with lev-exchange

This commit is contained in:
Sam Germain 2021-09-13 00:00:22 -06:00
commit 49acfc887f
9 changed files with 28 additions and 24 deletions

View File

@ -102,3 +102,4 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None:
HyperoptTools.show_epoch_details(val, total_epochs, print_json, no_header, HyperoptTools.show_epoch_details(val, total_epochs, print_json, no_header,
header_str="Epoch details") header_str="Epoch details")
# TODO-lev: Hyperopt optimal leverage

View File

@ -148,6 +148,7 @@ def start_list_markets(args: Dict[str, Any], pairs_only: bool = False) -> None:
quote_currencies = args.get('quote_currencies', []) quote_currencies = args.get('quote_currencies', [])
try: try:
# TODO-lev: Add leverage amount to get markets that support a certain leverage
pairs = exchange.get_markets(base_currencies=base_currencies, pairs = exchange.get_markets(base_currencies=base_currencies,
quote_currencies=quote_currencies, quote_currencies=quote_currencies,
pairs_only=pairs_only, pairs_only=pairs_only,

View File

@ -371,7 +371,7 @@ class FreqtradeBot(LoggingMixin):
def enter_positions(self) -> int: def enter_positions(self) -> int:
""" """
Tries to execute long buy/short sell orders for new trades (positions) Tries to execute entry orders for new trades (positions)
""" """
trades_created = 0 trades_created = 0
@ -534,10 +534,6 @@ class FreqtradeBot(LoggingMixin):
# is_short=is_short # is_short=is_short
# ) # )
if self.trading_mode == TradingMode.FUTURES:
self.exchange.set_leverage(pair, leverage)
self.exchange.set_margin_mode(pair, self.collateral_type)
return interest_rate, isolated_liq return interest_rate, isolated_liq
def execute_entry( def execute_entry(
@ -708,7 +704,7 @@ class FreqtradeBot(LoggingMixin):
def _notify_enter(self, trade: Trade, order_type: str) -> None: def _notify_enter(self, trade: Trade, order_type: str) -> None:
""" """
Sends rpc notification when a buy/short occurred. Sends rpc notification when a entry order occurred.
""" """
msg = { msg = {
'trade_id': trade.id, 'trade_id': trade.id,
@ -731,7 +727,7 @@ class FreqtradeBot(LoggingMixin):
def _notify_enter_cancel(self, trade: Trade, order_type: str, reason: str) -> None: def _notify_enter_cancel(self, trade: Trade, order_type: str, reason: str) -> None:
""" """
Sends rpc notification when a buy/short cancel occurred. Sends rpc notification when a entry order cancel occurred.
""" """
current_rate = self.exchange.get_rate(trade.pair, refresh=False, side=trade.enter_side) 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_type = RPCMessageType.SHORT_CANCEL if trade.is_short else RPCMessageType.BUY_CANCEL
@ -778,7 +774,7 @@ class FreqtradeBot(LoggingMixin):
def exit_positions(self, trades: List[Any]) -> int: def exit_positions(self, trades: List[Any]) -> int:
""" """
Tries to execute sell/exit_short orders for open trades (positions) Tries to execute exit orders for open trades (positions)
""" """
trades_closed = 0 trades_closed = 0
for trade in trades: for trade in trades:
@ -1149,7 +1145,7 @@ class FreqtradeBot(LoggingMixin):
def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str: def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str:
""" """
Sell/exit_short cancel - cancel order and update trade exit order cancel - cancel order and update trade
:return: Reason for cancel :return: Reason for cancel
""" """
# if trade is not partially completed, just cancel the order # if trade is not partially completed, just cancel the order
@ -1275,7 +1271,7 @@ class FreqtradeBot(LoggingMixin):
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)(
pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit, pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit,
time_in_force=time_in_force, sell_reason=sell_reason.sell_reason, time_in_force=time_in_force, sell_reason=sell_reason.sell_reason,
current_time=datetime.now(timezone.utc)): current_time=datetime.now(timezone.utc)): # TODO-lev: Update to exit
logger.info(f"User requested abortion of exiting {trade.pair}") logger.info(f"User requested abortion of exiting {trade.pair}")
return False return False
@ -1284,10 +1280,10 @@ class FreqtradeBot(LoggingMixin):
order = self.exchange.create_order( order = self.exchange.create_order(
pair=trade.pair, pair=trade.pair,
ordertype=order_type, ordertype=order_type,
side="sell",
amount=amount, amount=amount,
rate=limit, rate=limit,
time_in_force=time_in_force, time_in_force=time_in_force
side=trade.exit_side
) )
except InsufficientFundsError as e: except InsufficientFundsError as e:
logger.warning(f"Unable to place order {e}.") logger.warning(f"Unable to place order {e}.")

View File

@ -18,6 +18,7 @@ class PrecisionFilter(IPairList):
pairlist_pos: int) -> None: pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
# TODO-lev: Liquidation price?
if 'stoploss' not in self._config: if 'stoploss' not in self._config:
raise OperationalException( raise OperationalException(
'PrecisionFilter can only work with stoploss defined. Please add the ' 'PrecisionFilter can only work with stoploss defined. Please add the '

View File

@ -36,6 +36,7 @@ class MaxDrawdown(IProtection):
""" """
LockReason to use LockReason to use
""" """
# TODO-lev: < for shorts?
return (f'{drawdown} > {self._max_allowed_drawdown} in {self.lookback_period_str}, ' return (f'{drawdown} > {self._max_allowed_drawdown} in {self.lookback_period_str}, '
f'locking for {self.stop_duration_str}.') f'locking for {self.stop_duration_str}.')

View File

@ -32,6 +32,7 @@ class StoplossGuard(IProtection):
def _reason(self) -> str: def _reason(self) -> str:
""" """
LockReason to use LockReason to use
#TODO-lev: check if min is the right word for shorts
""" """
return (f'{self._trade_limit} stoplosses in {self._lookback_period} min, ' return (f'{self._trade_limit} stoplosses in {self._lookback_period} min, '
f'locking for {self._stop_duration} min.') f'locking for {self._stop_duration} min.')
@ -51,6 +52,7 @@ class StoplossGuard(IProtection):
# if pair: # if pair:
# filters.append(Trade.pair == pair) # filters.append(Trade.pair == pair)
# trades = Trade.get_trades(filters).all() # trades = Trade.get_trades(filters).all()
# TODO-lev: Liquidation price?
trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until) trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until)
trades = [trade for trade in trades1 if (str(trade.sell_reason) in ( trades = [trade for trade in trades1 if (str(trade.sell_reason) in (

View File

@ -168,7 +168,7 @@ class IStrategy(ABC, HyperStrategyMixin):
""" """
Check buy timeout function callback. Check buy timeout function callback.
This method can be used to override the enter-timeout. This method can be used to override the enter-timeout.
It is called whenever a limit buy/short order has been created, It is called whenever a limit entry order has been created,
and is not yet fully filled. and is not yet fully filled.
Configuration options in `unfilledtimeout` will be verified before this, Configuration options in `unfilledtimeout` will be verified before this,
so ensure to set these timeouts high enough. so ensure to set these timeouts high enough.
@ -178,7 +178,7 @@ class IStrategy(ABC, HyperStrategyMixin):
:param trade: trade object. :param trade: trade object.
:param order: Order dictionary as returned from CCXT. :param order: Order dictionary as returned from CCXT.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return bool: When True is returned, then the buy/short-order is cancelled. :return bool: When True is returned, then the entry order is cancelled.
""" """
return False return False
@ -213,7 +213,7 @@ class IStrategy(ABC, HyperStrategyMixin):
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, **kwargs) -> bool: time_in_force: str, current_time: datetime, **kwargs) -> bool:
""" """
Called right before placing a buy/short order. Called right before placing a entry order.
Timing for this function is critical, so avoid doing heavy computations or Timing for this function is critical, so avoid doing heavy computations or
network requests in this method. network requests in this method.
@ -237,7 +237,7 @@ class IStrategy(ABC, HyperStrategyMixin):
rate: float, time_in_force: str, sell_reason: str, rate: float, time_in_force: str, sell_reason: str,
current_time: datetime, **kwargs) -> bool: current_time: datetime, **kwargs) -> bool:
""" """
Called right before placing a regular sell/exit_short order. Called right before placing a regular exit order.
Timing for this function is critical, so avoid doing heavy computations or Timing for this function is critical, so avoid doing heavy computations or
network requests in this method. network requests in this method.
@ -412,7 +412,7 @@ class IStrategy(ABC, HyperStrategyMixin):
Checks if a pair is currently locked Checks if a pair is currently locked
The 2nd, optional parameter ensures that locks are applied until the new candle arrives, The 2nd, optional parameter ensures that locks are applied until the new candle arrives,
and not stop at 14:00:00 - while the next candle arrives at 14:00:02 leaving a gap and not stop at 14:00:00 - while the next candle arrives at 14:00:02 leaving a gap
of 2 seconds for a buy/short to happen on an old signal. of 2 seconds for an entry order to happen on an old signal.
:param pair: "Pair to check" :param pair: "Pair to check"
:param candle_date: Date of the last candle. Optional, defaults to current date :param candle_date: Date of the last candle. Optional, defaults to current date
:returns: locking state of the pair in question. :returns: locking state of the pair in question.
@ -428,7 +428,7 @@ class IStrategy(ABC, HyperStrategyMixin):
def analyze_ticker(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def analyze_ticker(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Parses the given candle (OHLCV) data and returns a populated DataFrame Parses the given candle (OHLCV) data and returns a populated DataFrame
add several TA indicators and buy/short signal to it add several TA indicators and entry order signal to it
:param dataframe: Dataframe containing data from exchange :param dataframe: Dataframe containing data from exchange
:param metadata: Metadata dictionary with additional data (e.g. 'pair') :param metadata: Metadata dictionary with additional data (e.g. 'pair')
:return: DataFrame of candle (OHLCV) data with indicator data and signals added :return: DataFrame of candle (OHLCV) data with indicator data and signals added
@ -547,7 +547,9 @@ class IStrategy(ABC, HyperStrategyMixin):
dataframe: DataFrame, dataframe: DataFrame,
) -> Tuple[Optional[DataFrame], Optional[arrow.Arrow]]: ) -> Tuple[Optional[DataFrame], Optional[arrow.Arrow]]:
""" """
Get the latest candle. Used only during real mode Calculates current signal based based on the entry order or exit order
columns of the dataframe.
Used by Bot to get the signal to buy, sell, short, or exit_short
:param pair: pair in format ANT/BTC :param pair: pair in format ANT/BTC
:param timeframe: timeframe to use :param timeframe: timeframe to use
:param dataframe: Analyzed dataframe to get signal from. :param dataframe: Analyzed dataframe to get signal from.
@ -672,7 +674,7 @@ class IStrategy(ABC, HyperStrategyMixin):
low: float = None, high: float = None, low: float = None, high: float = None,
force_stoploss: float = 0) -> SellCheckTuple: force_stoploss: float = 0) -> SellCheckTuple:
""" """
This function evaluates if one of the conditions required to trigger a sell/exit_short This function evaluates if one of the conditions required to trigger an exit order
has been reached, which can either be a stop-loss, ROI or exit-signal. has been reached, which can either be a stop-loss, ROI or exit-signal.
:param low: Only used during backtesting to simulate (long)stoploss/(short)ROI :param low: Only used during backtesting to simulate (long)stoploss/(short)ROI
:param high: Only used during backtesting, to simulate (short)stoploss/(long)ROI :param high: Only used during backtesting, to simulate (short)stoploss/(long)ROI
@ -884,7 +886,7 @@ class IStrategy(ABC, HyperStrategyMixin):
def advise_buy(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def advise_buy(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Based on TA indicators, populates the buy/short signal for the given dataframe Based on TA indicators, populates the entry order signal for the given dataframe
This method should not be overridden. This method should not be overridden.
:param dataframe: DataFrame :param dataframe: DataFrame
:param metadata: Additional information dictionary, with details like the :param metadata: Additional information dictionary, with details like the
@ -907,7 +909,7 @@ class IStrategy(ABC, HyperStrategyMixin):
def advise_sell(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def advise_sell(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Based on TA indicators, populates the sell/exit_short signal for the given dataframe Based on TA indicators, populates the exit order signal for the given dataframe
This method should not be overridden. This method should not be overridden.
:param dataframe: DataFrame :param dataframe: DataFrame
:param metadata: Additional information dictionary, with details like the :param metadata: Additional information dictionary, with details like the

View File

@ -3379,7 +3379,7 @@ def test__safe_exit_amount_error(default_conf, fee, caplog, mocker):
) )
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
with pytest.raises(DependencyException, match=r"Not enough amount to exit trade."): with pytest.raises(DependencyException, match=r"Not enough amount to exit."):
assert freqtrade._safe_exit_amount(trade.pair, trade.amount) assert freqtrade._safe_exit_amount(trade.pair, trade.amount)

View File

@ -90,7 +90,7 @@ def test_enter_exit_side(fee):
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test__set_stop_loss_isolated_liq(fee): def test_set_stop_loss_isolated_liq(fee):
trade = Trade( trade = Trade(
id=2, id=2,
pair='ADA/USDT', pair='ADA/USDT',