merged with lev-exchange
This commit is contained in:
commit
49acfc887f
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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}.")
|
||||||
|
@ -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 '
|
||||||
|
@ -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}.')
|
||||||
|
|
||||||
|
@ -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 (
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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',
|
||||||
|
Loading…
Reference in New Issue
Block a user