Merge pull request #6316 from samgermain/max-amount
exchange.get_max_pair_stake_amount
This commit is contained in:
commit
412fe65344
@ -9,7 +9,7 @@ import logging
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
import ccxt
|
import ccxt
|
||||||
@ -677,33 +677,63 @@ class Exchange:
|
|||||||
else:
|
else:
|
||||||
return 1 / pow(10, precision)
|
return 1 / pow(10, precision)
|
||||||
|
|
||||||
def get_min_pair_stake_amount(self, pair: str, price: float, stoploss: float,
|
def get_min_pair_stake_amount(
|
||||||
leverage: Optional[float] = 1.0) -> Optional[float]:
|
self,
|
||||||
|
pair: str,
|
||||||
|
price: float,
|
||||||
|
stoploss: float,
|
||||||
|
leverage: Optional[float] = 1.0
|
||||||
|
) -> Optional[float]:
|
||||||
|
return self._get_stake_amount_limit(pair, price, stoploss, 'min', leverage)
|
||||||
|
|
||||||
|
def get_max_pair_stake_amount(
|
||||||
|
self,
|
||||||
|
pair: str,
|
||||||
|
price: float,
|
||||||
|
) -> float:
|
||||||
|
max_stake_amount = self._get_stake_amount_limit(pair, price, 0.0, 'max')
|
||||||
|
if max_stake_amount is None:
|
||||||
|
# * Should never be executed
|
||||||
|
raise OperationalException(f'{self.name}.get_max_pair_stake_amount should'
|
||||||
|
'never set max_stake_amount to None')
|
||||||
|
return max_stake_amount
|
||||||
|
|
||||||
|
def _get_stake_amount_limit(
|
||||||
|
self,
|
||||||
|
pair: str,
|
||||||
|
price: float,
|
||||||
|
stoploss: float,
|
||||||
|
limit: Literal['min', 'max'],
|
||||||
|
leverage: Optional[float] = 1.0
|
||||||
|
) -> Optional[float]:
|
||||||
|
|
||||||
|
isMin = limit == 'min'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
market = self.markets[pair]
|
market = self.markets[pair]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError(f"Can't get market information for symbol {pair}")
|
raise ValueError(f"Can't get market information for symbol {pair}")
|
||||||
|
|
||||||
min_stake_amounts = []
|
stake_limits = []
|
||||||
limits = market['limits']
|
limits = market['limits']
|
||||||
if (limits['cost']['min'] is not None):
|
if (limits['cost'][limit] is not None):
|
||||||
min_stake_amounts.append(
|
stake_limits.append(
|
||||||
self._contracts_to_amount(
|
self._contracts_to_amount(
|
||||||
pair,
|
pair,
|
||||||
limits['cost']['min']
|
limits['cost'][limit]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (limits['amount']['min'] is not None):
|
if (limits['amount'][limit] is not None):
|
||||||
min_stake_amounts.append(
|
stake_limits.append(
|
||||||
self._contracts_to_amount(
|
self._contracts_to_amount(
|
||||||
pair,
|
pair,
|
||||||
limits['amount']['min'] * price
|
limits['amount'][limit] * price
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not min_stake_amounts:
|
if not stake_limits:
|
||||||
return None
|
return None if isMin else float('inf')
|
||||||
|
|
||||||
# reserve some percent defined in config (5% default) + stoploss
|
# reserve some percent defined in config (5% default) + stoploss
|
||||||
amount_reserve_percent = 1.0 + self._config.get('amount_reserve_percent',
|
amount_reserve_percent = 1.0 + self._config.get('amount_reserve_percent',
|
||||||
@ -718,9 +748,9 @@ class Exchange:
|
|||||||
# for cost (quote, stake currency), so max() is used here.
|
# for cost (quote, stake currency), so max() is used here.
|
||||||
# See also #2575 at github.
|
# See also #2575 at github.
|
||||||
return self._get_stake_amount_considering_leverage(
|
return self._get_stake_amount_considering_leverage(
|
||||||
max(min_stake_amounts) * amount_reserve_percent,
|
max(stake_limits) * amount_reserve_percent,
|
||||||
leverage or 1.0
|
leverage or 1.0
|
||||||
)
|
) if isMin else min(stake_limits) # TODO-lev: Account 4 max_reserve_percent in max limits?
|
||||||
|
|
||||||
def _get_stake_amount_considering_leverage(self, stake_amount: float, leverage: float):
|
def _get_stake_amount_considering_leverage(self, stake_amount: float, leverage: float):
|
||||||
"""
|
"""
|
||||||
@ -904,6 +934,7 @@ class Exchange:
|
|||||||
|
|
||||||
def create_order(
|
def create_order(
|
||||||
self,
|
self,
|
||||||
|
*,
|
||||||
pair: str,
|
pair: str,
|
||||||
ordertype: str,
|
ordertype: str,
|
||||||
side: str,
|
side: str,
|
||||||
@ -935,7 +966,7 @@ class Exchange:
|
|||||||
side,
|
side,
|
||||||
amount,
|
amount,
|
||||||
rate_for_order,
|
rate_for_order,
|
||||||
params
|
params,
|
||||||
)
|
)
|
||||||
self._log_exchange_response('create_order', order)
|
self._log_exchange_response('create_order', order)
|
||||||
order = self._order_contracts_to_amount(order)
|
order = self._order_contracts_to_amount(order)
|
||||||
|
@ -543,12 +543,14 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair,
|
min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair,
|
||||||
current_rate,
|
current_rate,
|
||||||
self.strategy.stoploss)
|
self.strategy.stoploss)
|
||||||
max_stake_amount = self.wallets.get_available_stake_amount()
|
max_stake_amount = self.exchange.get_max_pair_stake_amount(trade.pair, current_rate)
|
||||||
|
stake_available = self.wallets.get_available_stake_amount()
|
||||||
logger.debug(f"Calling adjust_trade_position for pair {trade.pair}")
|
logger.debug(f"Calling adjust_trade_position for pair {trade.pair}")
|
||||||
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
|
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
|
||||||
default_retval=None)(
|
default_retval=None)(
|
||||||
trade=trade, current_time=datetime.now(timezone.utc), current_rate=current_rate,
|
trade=trade, current_time=datetime.now(timezone.utc), current_rate=current_rate,
|
||||||
current_profit=current_profit, min_stake=min_stake_amount, max_stake=max_stake_amount)
|
current_profit=current_profit, min_stake=min_stake_amount,
|
||||||
|
max_stake=min(max_stake_amount, stake_available))
|
||||||
|
|
||||||
if stake_amount is not None and stake_amount > 0.0:
|
if stake_amount is not None and stake_amount > 0.0:
|
||||||
# We should increase our position
|
# We should increase our position
|
||||||
@ -824,7 +826,9 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
self, pair: str, price: Optional[float], stake_amount: float,
|
self, pair: str, price: Optional[float], stake_amount: float,
|
||||||
side: str, trade_side: str,
|
side: str, trade_side: str,
|
||||||
entry_tag: Optional[str],
|
entry_tag: Optional[str],
|
||||||
trade: Optional[Trade]) -> Tuple[float, float]:
|
trade: Optional[Trade]
|
||||||
|
) -> Tuple[float, float]:
|
||||||
|
|
||||||
if price:
|
if price:
|
||||||
enter_limit_requested = price
|
enter_limit_requested = price
|
||||||
else:
|
else:
|
||||||
@ -845,19 +849,25 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# We do however also need min-stake to determine leverage, therefore this is ignored as
|
# We do however also need min-stake to determine leverage, therefore this is ignored as
|
||||||
# edge-case for now.
|
# edge-case for now.
|
||||||
min_stake_amount = self.exchange.get_min_pair_stake_amount(
|
min_stake_amount = self.exchange.get_min_pair_stake_amount(
|
||||||
pair, enter_limit_requested, self.strategy.stoploss,)
|
pair, enter_limit_requested, self.strategy.stoploss)
|
||||||
|
max_stake_amount = self.exchange.get_max_pair_stake_amount(pair, enter_limit_requested)
|
||||||
|
|
||||||
if not self.edge and trade is None:
|
if not self.edge and trade is None:
|
||||||
max_stake_amount = self.wallets.get_available_stake_amount()
|
stake_available = self.wallets.get_available_stake_amount()
|
||||||
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
|
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
|
||||||
default_retval=stake_amount)(
|
default_retval=stake_amount)(
|
||||||
pair=pair, current_time=datetime.now(timezone.utc),
|
pair=pair, current_time=datetime.now(timezone.utc),
|
||||||
current_rate=enter_limit_requested, proposed_stake=stake_amount,
|
current_rate=enter_limit_requested, proposed_stake=stake_amount,
|
||||||
min_stake=min_stake_amount, max_stake=max_stake_amount,
|
min_stake=min_stake_amount, max_stake=min(max_stake_amount, stake_available),
|
||||||
entry_tag=entry_tag, side=trade_side
|
entry_tag=entry_tag, side=trade_side
|
||||||
)
|
)
|
||||||
|
|
||||||
stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount)
|
stake_amount = self.wallets.validate_stake_amount(
|
||||||
|
pair=pair,
|
||||||
|
stake_amount=stake_amount,
|
||||||
|
min_stake_amount=min_stake_amount,
|
||||||
|
max_stake_amount=max_stake_amount,
|
||||||
|
)
|
||||||
|
|
||||||
return enter_limit_requested, stake_amount
|
return enter_limit_requested, stake_amount
|
||||||
|
|
||||||
|
@ -414,14 +414,15 @@ class Backtesting:
|
|||||||
|
|
||||||
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
|
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
|
||||||
) -> LocalTrade:
|
) -> LocalTrade:
|
||||||
|
|
||||||
current_profit = trade.calc_profit_ratio(row[OPEN_IDX])
|
current_profit = trade.calc_profit_ratio(row[OPEN_IDX])
|
||||||
min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, row[OPEN_IDX], -0.1)
|
min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, row[OPEN_IDX], -0.1)
|
||||||
max_stake = self.wallets.get_available_stake_amount()
|
max_stake = self.exchange.get_max_pair_stake_amount(trade.pair, row[OPEN_IDX])
|
||||||
|
stake_available = self.wallets.get_available_stake_amount()
|
||||||
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
|
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
|
||||||
default_retval=None)(
|
default_retval=None)(
|
||||||
trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX],
|
trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX],
|
||||||
current_profit=current_profit, min_stake=min_stake, max_stake=max_stake)
|
current_profit=current_profit, min_stake=min_stake,
|
||||||
|
max_stake=min(max_stake, stake_available))
|
||||||
|
|
||||||
# Check if we should increase our position
|
# Check if we should increase our position
|
||||||
if stake_amount is not None and stake_amount > 0.0:
|
if stake_amount is not None and stake_amount > 0.0:
|
||||||
@ -555,7 +556,8 @@ class Backtesting:
|
|||||||
propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX])
|
propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX])
|
||||||
|
|
||||||
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, propose_rate, -0.05) or 0
|
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, propose_rate, -0.05) or 0
|
||||||
max_stake_amount = self.wallets.get_available_stake_amount()
|
max_stake_amount = self.exchange.get_max_pair_stake_amount(pair, propose_rate)
|
||||||
|
stake_available = self.wallets.get_available_stake_amount()
|
||||||
|
|
||||||
pos_adjust = trade is not None
|
pos_adjust = trade is not None
|
||||||
if not pos_adjust:
|
if not pos_adjust:
|
||||||
@ -567,10 +569,16 @@ class Backtesting:
|
|||||||
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
|
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
|
||||||
default_retval=stake_amount)(
|
default_retval=stake_amount)(
|
||||||
pair=pair, current_time=current_time, current_rate=propose_rate,
|
pair=pair, current_time=current_time, current_rate=propose_rate,
|
||||||
proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount,
|
proposed_stake=stake_amount, min_stake=min_stake_amount,
|
||||||
|
max_stake=min(stake_available, max_stake_amount),
|
||||||
entry_tag=entry_tag, side=direction)
|
entry_tag=entry_tag, side=direction)
|
||||||
|
|
||||||
stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount)
|
stake_amount = self.wallets.validate_stake_amount(
|
||||||
|
pair=pair,
|
||||||
|
stake_amount=stake_amount,
|
||||||
|
min_stake_amount=min_stake_amount,
|
||||||
|
max_stake_amount=max_stake_amount,
|
||||||
|
)
|
||||||
|
|
||||||
if not stake_amount:
|
if not stake_amount:
|
||||||
# In case of pos adjust, still return the original trade
|
# In case of pos adjust, still return the original trade
|
||||||
|
@ -238,12 +238,12 @@ class Wallets:
|
|||||||
|
|
||||||
return self._check_available_stake_amount(stake_amount, available_amount)
|
return self._check_available_stake_amount(stake_amount, available_amount)
|
||||||
|
|
||||||
def validate_stake_amount(self, pair, stake_amount, min_stake_amount):
|
def validate_stake_amount(self, pair, stake_amount, min_stake_amount, max_stake_amount):
|
||||||
if not stake_amount:
|
if not stake_amount:
|
||||||
logger.debug(f"Stake amount is {stake_amount}, ignoring possible trade for {pair}.")
|
logger.debug(f"Stake amount is {stake_amount}, ignoring possible trade for {pair}.")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
max_stake_amount = self.get_available_stake_amount()
|
max_stake_amount = min(max_stake_amount, self.get_available_stake_amount())
|
||||||
|
|
||||||
if min_stake_amount > max_stake_amount:
|
if min_stake_amount > max_stake_amount:
|
||||||
if self._log:
|
if self._log:
|
||||||
|
@ -587,7 +587,7 @@ def get_markets():
|
|||||||
'limits': {
|
'limits': {
|
||||||
'amount': {
|
'amount': {
|
||||||
'min': 0.01,
|
'min': 0.01,
|
||||||
'max': 1000,
|
'max': 100000000,
|
||||||
},
|
},
|
||||||
'price': {
|
'price': {
|
||||||
'min': None,
|
'min': None,
|
||||||
@ -622,7 +622,7 @@ def get_markets():
|
|||||||
'limits': {
|
'limits': {
|
||||||
'amount': {
|
'amount': {
|
||||||
'min': 0.01,
|
'min': 0.01,
|
||||||
'max': 1000,
|
'max': 100000000,
|
||||||
},
|
},
|
||||||
'price': {
|
'price': {
|
||||||
'min': None,
|
'min': None,
|
||||||
@ -690,7 +690,7 @@ def get_markets():
|
|||||||
'limits': {
|
'limits': {
|
||||||
'amount': {
|
'amount': {
|
||||||
'min': 0.01,
|
'min': 0.01,
|
||||||
'max': 1000,
|
'max': 100000000,
|
||||||
},
|
},
|
||||||
'price': {
|
'price': {
|
||||||
'min': None,
|
'min': None,
|
||||||
@ -725,7 +725,7 @@ def get_markets():
|
|||||||
'limits': {
|
'limits': {
|
||||||
'amount': {
|
'amount': {
|
||||||
'min': 0.01,
|
'min': 0.01,
|
||||||
'max': 1000,
|
'max': 100000000,
|
||||||
},
|
},
|
||||||
'price': {
|
'price': {
|
||||||
'min': None,
|
'min': None,
|
||||||
@ -760,7 +760,7 @@ def get_markets():
|
|||||||
'limits': {
|
'limits': {
|
||||||
'amount': {
|
'amount': {
|
||||||
'min': 0.01,
|
'min': 0.01,
|
||||||
'max': 1000,
|
'max': 100000000,
|
||||||
},
|
},
|
||||||
'price': {
|
'price': {
|
||||||
'min': None,
|
'min': None,
|
||||||
@ -988,7 +988,7 @@ def get_markets():
|
|||||||
'limits': {
|
'limits': {
|
||||||
'amount': {
|
'amount': {
|
||||||
'min': 0.01,
|
'min': 0.01,
|
||||||
'max': 1000,
|
'max': 100000000000,
|
||||||
},
|
},
|
||||||
'price': {
|
'price': {
|
||||||
'min': None,
|
'min': None,
|
||||||
|
@ -342,7 +342,7 @@ def test_price_get_one_pip(default_conf, mocker, price, precision_mode, precisio
|
|||||||
assert pytest.approx(exchange.price_get_one_pip(pair, price)) == expected
|
assert pytest.approx(exchange.price_get_one_pip(pair, price)) == expected
|
||||||
|
|
||||||
|
|
||||||
def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
def test__get_stake_amount_limit(mocker, default_conf) -> None:
|
||||||
|
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||||
stoploss = -0.05
|
stoploss = -0.05
|
||||||
@ -356,7 +356,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
with pytest.raises(ValueError, match=r'.*get market information.*'):
|
with pytest.raises(ValueError, match=r'.*get market information.*'):
|
||||||
exchange.get_min_pair_stake_amount('BNB/BTC', 1, stoploss)
|
exchange.get_min_pair_stake_amount('BNB/BTC', 1, stoploss)
|
||||||
|
|
||||||
# no cost Min
|
# no cost/amount Min
|
||||||
markets["ETH/BTC"]["limits"] = {
|
markets["ETH/BTC"]["limits"] = {
|
||||||
'cost': {'min': None, 'max': None},
|
'cost': {'min': None, 'max': None},
|
||||||
'amount': {'min': None, 'max': None},
|
'amount': {'min': None, 'max': None},
|
||||||
@ -367,51 +367,33 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
)
|
)
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss)
|
||||||
assert result is None
|
assert result is None
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 1)
|
||||||
|
assert result == float('inf')
|
||||||
|
|
||||||
# no amount Min
|
# min/max cost is set
|
||||||
markets["ETH/BTC"]["limits"] = {
|
markets["ETH/BTC"]["limits"] = {
|
||||||
'cost': {'min': None, 'max': None},
|
'cost': {'min': 2, 'max': 10000},
|
||||||
'amount': {'min': None, 'max': None},
|
|
||||||
}
|
|
||||||
mocker.patch(
|
|
||||||
'freqtrade.exchange.Exchange.markets',
|
|
||||||
PropertyMock(return_value=markets)
|
|
||||||
)
|
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss)
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
# empty 'cost'/'amount' section
|
|
||||||
markets["ETH/BTC"]["limits"] = {
|
|
||||||
'cost': {'min': None, 'max': None},
|
|
||||||
'amount': {'min': None, 'max': None},
|
|
||||||
}
|
|
||||||
mocker.patch(
|
|
||||||
'freqtrade.exchange.Exchange.markets',
|
|
||||||
PropertyMock(return_value=markets)
|
|
||||||
)
|
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss)
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
# min cost is set
|
|
||||||
markets["ETH/BTC"]["limits"] = {
|
|
||||||
'cost': {'min': 2, 'max': None},
|
|
||||||
'amount': {'min': None, 'max': None},
|
'amount': {'min': None, 'max': None},
|
||||||
}
|
}
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.exchange.Exchange.markets',
|
'freqtrade.exchange.Exchange.markets',
|
||||||
PropertyMock(return_value=markets)
|
PropertyMock(return_value=markets)
|
||||||
)
|
)
|
||||||
|
# min
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss)
|
||||||
expected_result = 2 * (1+0.05) / (1-abs(stoploss))
|
expected_result = 2 * (1+0.05) / (1-abs(stoploss))
|
||||||
assert isclose(result, expected_result)
|
assert isclose(result, expected_result)
|
||||||
# With Leverage
|
# With Leverage
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss, 3.0)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss, 3.0)
|
||||||
assert isclose(result, expected_result/3)
|
assert isclose(result, expected_result/3)
|
||||||
|
# max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||||
|
assert result == 10000
|
||||||
|
|
||||||
# min amount is set
|
# min amount is set
|
||||||
markets["ETH/BTC"]["limits"] = {
|
markets["ETH/BTC"]["limits"] = {
|
||||||
'cost': {'min': None, 'max': None},
|
'cost': {'min': None, 'max': None},
|
||||||
'amount': {'min': 2, 'max': None},
|
'amount': {'min': 2, 'max': 10000},
|
||||||
}
|
}
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.exchange.Exchange.markets',
|
'freqtrade.exchange.Exchange.markets',
|
||||||
@ -423,6 +405,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
# With Leverage
|
# With Leverage
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 5.0)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 5.0)
|
||||||
assert isclose(result, expected_result/5)
|
assert isclose(result, expected_result/5)
|
||||||
|
# max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||||
|
assert result == 20000
|
||||||
|
|
||||||
# min amount and cost are set (cost is minimal)
|
# min amount and cost are set (cost is minimal)
|
||||||
markets["ETH/BTC"]["limits"] = {
|
markets["ETH/BTC"]["limits"] = {
|
||||||
@ -442,8 +427,8 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
# min amount and cost are set (amount is minial)
|
# min amount and cost are set (amount is minial)
|
||||||
markets["ETH/BTC"]["limits"] = {
|
markets["ETH/BTC"]["limits"] = {
|
||||||
'cost': {'min': 8, 'max': None},
|
'cost': {'min': 8, 'max': 10000},
|
||||||
'amount': {'min': 2, 'max': None},
|
'amount': {'min': 2, 'max': 500},
|
||||||
}
|
}
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.exchange.Exchange.markets',
|
'freqtrade.exchange.Exchange.markets',
|
||||||
@ -455,6 +440,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
# With Leverage
|
# With Leverage
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 7.0)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 7.0)
|
||||||
assert isclose(result, expected_result/7.0)
|
assert isclose(result, expected_result/7.0)
|
||||||
|
# Max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||||
|
assert result == 1000
|
||||||
|
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4)
|
||||||
expected_result = max(8, 2 * 2) * 1.5
|
expected_result = max(8, 2 * 2) * 1.5
|
||||||
@ -462,6 +450,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
# With Leverage
|
# With Leverage
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4, 8.0)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4, 8.0)
|
||||||
assert isclose(result, expected_result/8.0)
|
assert isclose(result, expected_result/8.0)
|
||||||
|
# Max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||||
|
assert result == 1000
|
||||||
|
|
||||||
# Really big stoploss
|
# Really big stoploss
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1)
|
||||||
@ -470,6 +461,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
# With Leverage
|
# With Leverage
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0)
|
||||||
assert isclose(result, expected_result/12)
|
assert isclose(result, expected_result/12)
|
||||||
|
# Max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||||
|
assert result == 1000
|
||||||
|
|
||||||
markets["ETH/BTC"]["contractSize"] = '0.01'
|
markets["ETH/BTC"]["contractSize"] = '0.01'
|
||||||
default_conf['trading_mode'] = 'futures'
|
default_conf['trading_mode'] = 'futures'
|
||||||
@ -483,6 +477,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
# Contract size 0.01
|
# Contract size 0.01
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1)
|
||||||
assert isclose(result, expected_result * 0.01)
|
assert isclose(result, expected_result * 0.01)
|
||||||
|
# Max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||||
|
assert result == 10
|
||||||
|
|
||||||
markets["ETH/BTC"]["contractSize"] = '10'
|
markets["ETH/BTC"]["contractSize"] = '10'
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
@ -492,6 +489,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
# With Leverage, Contract size 10
|
# With Leverage, Contract size 10
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0)
|
||||||
assert isclose(result, (expected_result/12) * 10.0)
|
assert isclose(result, (expected_result/12) * 10.0)
|
||||||
|
# Max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||||
|
assert result == 10000
|
||||||
|
|
||||||
|
|
||||||
def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
|
def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
|
||||||
@ -499,10 +499,10 @@ def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
|
|||||||
stoploss = -0.05
|
stoploss = -0.05
|
||||||
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}}
|
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}}
|
||||||
|
|
||||||
# Real Binance data
|
# ~Real Binance data
|
||||||
markets["ETH/BTC"]["limits"] = {
|
markets["ETH/BTC"]["limits"] = {
|
||||||
'cost': {'min': 0.0001},
|
'cost': {'min': 0.0001, 'max': 4000},
|
||||||
'amount': {'min': 0.001}
|
'amount': {'min': 0.001, 'max': 10000},
|
||||||
}
|
}
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.exchange.Exchange.markets',
|
'freqtrade.exchange.Exchange.markets',
|
||||||
@ -511,9 +511,23 @@ def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
|
|||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss)
|
||||||
expected_result = max(0.0001, 0.001 * 0.020405) * (1+0.05) / (1-abs(stoploss))
|
expected_result = max(0.0001, 0.001 * 0.020405) * (1+0.05) / (1-abs(stoploss))
|
||||||
assert round(result, 8) == round(expected_result, 8)
|
assert round(result, 8) == round(expected_result, 8)
|
||||||
|
# Max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2.0)
|
||||||
|
assert result == 4000
|
||||||
|
|
||||||
|
# Leverage
|
||||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss, 3.0)
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss, 3.0)
|
||||||
assert round(result, 8) == round(expected_result/3, 8)
|
assert round(result, 8) == round(expected_result/3, 8)
|
||||||
|
|
||||||
|
# Contract_size
|
||||||
|
markets["ETH/BTC"]["contractSize"] = 0.1
|
||||||
|
result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss, 3.0)
|
||||||
|
assert round(result, 8) == round((expected_result/3), 8)
|
||||||
|
|
||||||
|
# Max
|
||||||
|
result = exchange.get_max_pair_stake_amount('ETH/BTC', 12.0)
|
||||||
|
assert result == 4000
|
||||||
|
|
||||||
|
|
||||||
def test_set_sandbox(default_conf, mocker):
|
def test_set_sandbox(default_conf, mocker):
|
||||||
"""
|
"""
|
||||||
@ -2537,7 +2551,15 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name):
|
|||||||
assert exchange.cancel_order(order_id='123', pair='TKN/BTC') == {}
|
assert exchange.cancel_order(order_id='123', pair='TKN/BTC') == {}
|
||||||
assert exchange.cancel_stoploss_order(order_id='123', pair='TKN/BTC') == {}
|
assert exchange.cancel_stoploss_order(order_id='123', pair='TKN/BTC') == {}
|
||||||
|
|
||||||
order = exchange.create_order('ETH/BTC', 'limit', "buy", 5, 0.55, 'gtc')
|
order = exchange.create_order(
|
||||||
|
pair='ETH/BTC',
|
||||||
|
ordertype='limit',
|
||||||
|
side='buy',
|
||||||
|
amount=5,
|
||||||
|
rate=0.55,
|
||||||
|
time_in_force='gtc',
|
||||||
|
leverage=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
cancel_order = exchange.cancel_order(order_id=order['id'], pair='ETH/BTC')
|
cancel_order = exchange.cancel_order(order_id=order['id'], pair='ETH/BTC')
|
||||||
assert order['id'] == cancel_order['id']
|
assert order['id'] == cancel_order['id']
|
||||||
@ -4062,3 +4084,126 @@ def test_liquidation_price(
|
|||||||
upnl_ex_1=upnl_ex_1,
|
upnl_ex_1=upnl_ex_1,
|
||||||
position=position,
|
position=position,
|
||||||
), 2), expected)
|
), 2), expected)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_max_pair_stake_amount(
|
||||||
|
mocker,
|
||||||
|
default_conf,
|
||||||
|
):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
markets = {
|
||||||
|
'XRP/USDT:USDT': {
|
||||||
|
'limits': {
|
||||||
|
'amount': {
|
||||||
|
'min': 0.001,
|
||||||
|
'max': 10000
|
||||||
|
},
|
||||||
|
'cost': {
|
||||||
|
'min': 5,
|
||||||
|
'max': None
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'contractSize': None,
|
||||||
|
'spot': False,
|
||||||
|
},
|
||||||
|
'LTC/USDT:USDT': {
|
||||||
|
'limits': {
|
||||||
|
'amount': {
|
||||||
|
'min': 0.001,
|
||||||
|
'max': None
|
||||||
|
},
|
||||||
|
'cost': {
|
||||||
|
'min': 5,
|
||||||
|
'max': None
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'contractSize': 0.01,
|
||||||
|
'spot': False,
|
||||||
|
},
|
||||||
|
'ETH/USDT:USDT': {
|
||||||
|
'limits': {
|
||||||
|
'amount': {
|
||||||
|
'min': 0.001,
|
||||||
|
'max': 10000
|
||||||
|
},
|
||||||
|
'cost': {
|
||||||
|
'min': 5,
|
||||||
|
'max': 30000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'contractSize': 0.01,
|
||||||
|
'spot': False,
|
||||||
|
},
|
||||||
|
'BTC/USDT': {
|
||||||
|
'limits': {
|
||||||
|
'amount': {
|
||||||
|
'min': 0.001,
|
||||||
|
'max': 10000
|
||||||
|
},
|
||||||
|
'cost': {
|
||||||
|
'min': 5,
|
||||||
|
'max': None
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'contractSize': 0.01,
|
||||||
|
'spot': True,
|
||||||
|
},
|
||||||
|
'ADA/USDT': {
|
||||||
|
'limits': {
|
||||||
|
'amount': {
|
||||||
|
'min': 0.001,
|
||||||
|
'max': 10000
|
||||||
|
},
|
||||||
|
'cost': {
|
||||||
|
'min': 5,
|
||||||
|
'max': 500,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'contractSize': 0.01,
|
||||||
|
'spot': True,
|
||||||
|
},
|
||||||
|
'DOGE/USDT:USDT': {
|
||||||
|
'limits': {
|
||||||
|
'amount': {
|
||||||
|
'min': 0.001,
|
||||||
|
'max': 10000
|
||||||
|
},
|
||||||
|
'cost': {
|
||||||
|
'min': 5,
|
||||||
|
'max': 500
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'contractSize': None,
|
||||||
|
'spot': False,
|
||||||
|
},
|
||||||
|
'LUNA/USDT:USDT': {
|
||||||
|
'limits': {
|
||||||
|
'amount': {
|
||||||
|
'min': 0.001,
|
||||||
|
'max': 10000
|
||||||
|
},
|
||||||
|
'cost': {
|
||||||
|
'min': 5,
|
||||||
|
'max': 500
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'contractSize': 0.01,
|
||||||
|
'spot': False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||||
|
assert exchange.get_max_pair_stake_amount('XRP/USDT:USDT', 2.0) == 20000
|
||||||
|
assert exchange.get_max_pair_stake_amount('LTC/USDT:USDT', 2.0) == float('inf')
|
||||||
|
assert exchange.get_max_pair_stake_amount('ETH/USDT:USDT', 2.0) == 200
|
||||||
|
assert exchange.get_max_pair_stake_amount('DOGE/USDT:USDT', 2.0) == 500
|
||||||
|
assert exchange.get_max_pair_stake_amount('LUNA/USDT:USDT', 2.0) == 5.0
|
||||||
|
|
||||||
|
default_conf['trading_mode'] = 'spot'
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||||
|
assert exchange.get_max_pair_stake_amount('BTC/USDT', 2.0) == 20000
|
||||||
|
assert exchange.get_max_pair_stake_amount('ADA/USDT', 2.0) == 500
|
||||||
|
@ -609,6 +609,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
|||||||
|
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_fee", return_value=0.0)
|
mocker.patch("freqtrade.exchange.Exchange.get_fee", return_value=0.0)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
mocker.patch("freqtrade.exchange.Binance.get_max_leverage", return_value=100)
|
mocker.patch("freqtrade.exchange.Binance.get_max_leverage", return_value=100)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
frame = _build_backtest_dataframe(data.data)
|
frame = _build_backtest_dataframe(data.data)
|
||||||
|
@ -497,9 +497,11 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
|
|||||||
|
|
||||||
|
|
||||||
def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
|
def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
|
||||||
|
# TODO-lev: test max_pair_stake_amount
|
||||||
default_conf['use_sell_signal'] = False
|
default_conf['use_sell_signal'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
default_conf['stake_amount'] = 'unlimited'
|
default_conf['stake_amount'] = 'unlimited'
|
||||||
default_conf['max_open_trades'] = 2
|
default_conf['max_open_trades'] = 2
|
||||||
@ -546,6 +548,11 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
|
|||||||
assert trade.stake_amount == 495
|
assert trade.stake_amount == 495
|
||||||
assert trade.is_short is True
|
assert trade.is_short is True
|
||||||
|
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=300.0)
|
||||||
|
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||||
|
assert trade
|
||||||
|
assert trade.stake_amount == 300.0
|
||||||
|
|
||||||
# Stake-amount too high!
|
# Stake-amount too high!
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0)
|
||||||
|
|
||||||
@ -566,6 +573,7 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
|
|||||||
default_conf['use_sell_signal'] = False
|
default_conf['use_sell_signal'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
default_conf['timeframe_detail'] = '1m'
|
default_conf['timeframe_detail'] = '1m'
|
||||||
default_conf['max_open_trades'] = 2
|
default_conf['max_open_trades'] = 2
|
||||||
@ -659,6 +667,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
|||||||
default_conf['use_sell_signal'] = False
|
default_conf['use_sell_signal'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting._set_strategy(backtesting.strategylist[0])
|
backtesting._set_strategy(backtesting.strategylist[0])
|
||||||
@ -724,6 +733,7 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
|
|||||||
default_conf['use_sell_signal'] = False
|
default_conf['use_sell_signal'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting._set_strategy(backtesting.strategylist[0])
|
backtesting._set_strategy(backtesting.strategylist[0])
|
||||||
@ -772,6 +782,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
|
|||||||
default_conf['enable_protections'] = True
|
default_conf['enable_protections'] = True
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
tests = [
|
tests = [
|
||||||
['sine', 9],
|
['sine', 9],
|
||||||
['raise', 10],
|
['raise', 10],
|
||||||
@ -806,6 +817,7 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir,
|
|||||||
default_conf['enable_protections'] = True
|
default_conf['enable_protections'] = True
|
||||||
|
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
# While buy-signals are unrealistic, running backtesting
|
# While buy-signals are unrealistic, running backtesting
|
||||||
# over and over again should not cause different results
|
# over and over again should not cause different results
|
||||||
@ -846,6 +858,7 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir):
|
|||||||
|
|
||||||
def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
|
def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
|
backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
|
||||||
pair='UNITTEST/BTC', datadir=testdatadir)
|
pair='UNITTEST/BTC', datadir=testdatadir)
|
||||||
@ -892,6 +905,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
|||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
|
|||||||
default_conf['use_sell_signal'] = False
|
default_conf['use_sell_signal'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||||
|
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
default_conf.update({
|
default_conf.update({
|
||||||
"stake_amount": 100.0,
|
"stake_amount": 100.0,
|
||||||
|
@ -743,6 +743,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
(open_rate - (wallet_balance / position)) / (1 - (mm_ratio + taker_fee_rate))
|
(open_rate - (wallet_balance / position)) / (1 - (mm_ratio + taker_fee_rate))
|
||||||
(10 - (2 / 1)) / (1 - (0.01 + 0.0006)) = 8.085708510208207
|
(10 - (2 / 1)) / (1 - (0.01 + 0.0006)) = 8.085708510208207
|
||||||
"""
|
"""
|
||||||
|
# TODO: Split this test into multiple tests to improve readability
|
||||||
open_order = limit_order_open[enter_side(is_short)]
|
open_order = limit_order_open[enter_side(is_short)]
|
||||||
order = limit_order[enter_side(is_short)]
|
order = limit_order[enter_side(is_short)]
|
||||||
default_conf_usdt['trading_mode'] = trading_mode
|
default_conf_usdt['trading_mode'] = trading_mode
|
||||||
@ -932,6 +933,22 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
assert trade.open_rate_requested == 10
|
assert trade.open_rate_requested == 10
|
||||||
assert trade.isolated_liq == liq_price
|
assert trade.isolated_liq == liq_price
|
||||||
|
|
||||||
|
# In case of too high stake amount
|
||||||
|
|
||||||
|
order['status'] = 'open'
|
||||||
|
order['id'] = '55672'
|
||||||
|
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
get_max_pair_stake_amount=MagicMock(return_value=500),
|
||||||
|
)
|
||||||
|
freqtrade.exchange.get_max_pair_stake_amount = MagicMock(return_value=500)
|
||||||
|
|
||||||
|
assert freqtrade.execute_entry(pair, 2000, is_short=is_short)
|
||||||
|
trade = Trade.query.all()[9]
|
||||||
|
trade.is_short = is_short
|
||||||
|
assert trade.stake_amount == 500
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("is_short", [False, True])
|
@pytest.mark.parametrize("is_short", [False, True])
|
||||||
def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_order, is_short) -> None:
|
def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_order, is_short) -> None:
|
||||||
|
@ -180,23 +180,31 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r
|
|||||||
assert result == 0
|
assert result == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('stake_amount,min_stake_amount,max_stake_amount,expected', [
|
@pytest.mark.parametrize('stake_amount,min_stake,stake_available,max_stake,expected', [
|
||||||
(22, 11, 50, 22),
|
(22, 11, 50, 10000, 22),
|
||||||
(100, 11, 500, 100),
|
(100, 11, 500, 10000, 100),
|
||||||
(1000, 11, 500, 500), # Above max-stake
|
(1000, 11, 500, 10000, 500), # Above stake_available
|
||||||
(20, 15, 10, 0), # Minimum stake > max-stake
|
(700, 11, 1000, 400, 400), # Above max_stake, below stake available
|
||||||
(9, 11, 100, 11), # Below min stake
|
(20, 15, 10, 10000, 0), # Minimum stake > stake_available
|
||||||
(1, 15, 10, 0), # Below min stake and min_stake > max_stake
|
(9, 11, 100, 10000, 11), # Below min stake
|
||||||
(20, 50, 100, 0), # Below min stake and stake * 1.3 > min_stake
|
(1, 15, 10, 10000, 0), # Below min stake and min_stake > stake_available
|
||||||
|
(20, 50, 100, 10000, 0), # Below min stake and stake * 1.3 > min_stake
|
||||||
|
|
||||||
])
|
])
|
||||||
def test_validate_stake_amount(mocker, default_conf,
|
def test_validate_stake_amount(
|
||||||
stake_amount, min_stake_amount, max_stake_amount, expected):
|
mocker,
|
||||||
|
default_conf,
|
||||||
|
stake_amount,
|
||||||
|
min_stake,
|
||||||
|
stake_available,
|
||||||
|
max_stake,
|
||||||
|
expected,
|
||||||
|
):
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount",
|
mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount",
|
||||||
return_value=max_stake_amount)
|
return_value=stake_available)
|
||||||
res = freqtrade.wallets.validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount)
|
res = freqtrade.wallets.validate_stake_amount('XRP/USDT', stake_amount, min_stake, max_stake)
|
||||||
assert res == expected
|
assert res == expected
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user