Merge pull request #6316 from samgermain/max-amount

exchange.get_max_pair_stake_amount
This commit is contained in:
Matthias 2022-02-06 07:44:29 +01:00 committed by GitHub
commit 412fe65344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 321 additions and 86 deletions

View File

@ -9,7 +9,7 @@ import logging
from copy import deepcopy
from datetime import datetime, timedelta, timezone
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 ccxt
@ -677,33 +677,63 @@ class Exchange:
else:
return 1 / pow(10, precision)
def get_min_pair_stake_amount(self, pair: str, price: float, stoploss: float,
leverage: Optional[float] = 1.0) -> Optional[float]:
def get_min_pair_stake_amount(
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:
market = self.markets[pair]
except KeyError:
raise ValueError(f"Can't get market information for symbol {pair}")
min_stake_amounts = []
stake_limits = []
limits = market['limits']
if (limits['cost']['min'] is not None):
min_stake_amounts.append(
if (limits['cost'][limit] is not None):
stake_limits.append(
self._contracts_to_amount(
pair,
limits['cost']['min']
limits['cost'][limit]
)
)
if (limits['amount']['min'] is not None):
min_stake_amounts.append(
if (limits['amount'][limit] is not None):
stake_limits.append(
self._contracts_to_amount(
pair,
limits['amount']['min'] * price
limits['amount'][limit] * price
)
)
if not min_stake_amounts:
return None
if not stake_limits:
return None if isMin else float('inf')
# reserve some percent defined in config (5% default) + stoploss
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.
# See also #2575 at github.
return self._get_stake_amount_considering_leverage(
max(min_stake_amounts) * amount_reserve_percent,
max(stake_limits) * amount_reserve_percent,
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):
"""
@ -904,6 +934,7 @@ class Exchange:
def create_order(
self,
*,
pair: str,
ordertype: str,
side: str,
@ -935,7 +966,7 @@ class Exchange:
side,
amount,
rate_for_order,
params
params,
)
self._log_exchange_response('create_order', order)
order = self._order_contracts_to_amount(order)

View File

@ -543,12 +543,14 @@ class FreqtradeBot(LoggingMixin):
min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair,
current_rate,
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}")
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
default_retval=None)(
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:
# We should increase our position
@ -824,7 +826,9 @@ class FreqtradeBot(LoggingMixin):
self, pair: str, price: Optional[float], stake_amount: float,
side: str, trade_side: str,
entry_tag: Optional[str],
trade: Optional[Trade]) -> Tuple[float, float]:
trade: Optional[Trade]
) -> Tuple[float, float]:
if price:
enter_limit_requested = price
else:
@ -845,19 +849,25 @@ class FreqtradeBot(LoggingMixin):
# We do however also need min-stake to determine leverage, therefore this is ignored as
# edge-case for now.
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:
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,
default_retval=stake_amount)(
pair=pair, current_time=datetime.now(timezone.utc),
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
)
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

View File

@ -414,14 +414,15 @@ class Backtesting:
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
) -> LocalTrade:
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)
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,
default_retval=None)(
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
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])
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
if not pos_adjust:
@ -567,10 +569,16 @@ class Backtesting:
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
default_retval=stake_amount)(
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)
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:
# In case of pos adjust, still return the original trade

View File

@ -238,12 +238,12 @@ class Wallets:
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:
logger.debug(f"Stake amount is {stake_amount}, ignoring possible trade for {pair}.")
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 self._log:

View File

@ -587,7 +587,7 @@ def get_markets():
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
'max': 100000000,
},
'price': {
'min': None,
@ -622,7 +622,7 @@ def get_markets():
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
'max': 100000000,
},
'price': {
'min': None,
@ -690,7 +690,7 @@ def get_markets():
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
'max': 100000000,
},
'price': {
'min': None,
@ -725,7 +725,7 @@ def get_markets():
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
'max': 100000000,
},
'price': {
'min': None,
@ -760,7 +760,7 @@ def get_markets():
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
'max': 100000000,
},
'price': {
'min': None,
@ -988,7 +988,7 @@ def get_markets():
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
'max': 100000000000,
},
'price': {
'min': None,

View File

@ -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
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")
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.*'):
exchange.get_min_pair_stake_amount('BNB/BTC', 1, stoploss)
# no cost Min
# no cost/amount Min
markets["ETH/BTC"]["limits"] = {
'cost': {'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)
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"] = {
'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
# 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},
'cost': {'min': 2, 'max': 10000},
'amount': {'min': None, 'max': None},
}
mocker.patch(
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
)
# min
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss)
expected_result = 2 * (1+0.05) / (1-abs(stoploss))
assert isclose(result, expected_result)
# With Leverage
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss, 3.0)
assert isclose(result, expected_result/3)
# max
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
assert result == 10000
# min amount is set
markets["ETH/BTC"]["limits"] = {
'cost': {'min': None, 'max': None},
'amount': {'min': 2, 'max': None},
'amount': {'min': 2, 'max': 10000},
}
mocker.patch(
'freqtrade.exchange.Exchange.markets',
@ -423,6 +405,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
# With Leverage
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 5.0)
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)
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)
markets["ETH/BTC"]["limits"] = {
'cost': {'min': 8, 'max': None},
'amount': {'min': 2, 'max': None},
'cost': {'min': 8, 'max': 10000},
'amount': {'min': 2, 'max': 500},
}
mocker.patch(
'freqtrade.exchange.Exchange.markets',
@ -455,6 +440,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
# With Leverage
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 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)
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
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4, 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
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
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0)
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'
default_conf['trading_mode'] = 'futures'
@ -483,6 +477,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
# Contract size 0.01
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1)
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'
mocker.patch(
@ -492,6 +489,9 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
# With Leverage, Contract size 10
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.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:
@ -499,10 +499,10 @@ def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
stoploss = -0.05
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}}
# Real Binance data
# ~Real Binance data
markets["ETH/BTC"]["limits"] = {
'cost': {'min': 0.0001},
'amount': {'min': 0.001}
'cost': {'min': 0.0001, 'max': 4000},
'amount': {'min': 0.001, 'max': 10000},
}
mocker.patch(
'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)
expected_result = max(0.0001, 0.001 * 0.020405) * (1+0.05) / (1-abs(stoploss))
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)
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):
"""
@ -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_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')
assert order['id'] == cancel_order['id']
@ -4062,3 +4084,126 @@ def test_liquidation_price(
upnl_ex_1=upnl_ex_1,
position=position,
), 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

View File

@ -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_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)
patch_exchange(mocker)
frame = _build_backtest_dataframe(data.data)

View File

@ -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:
# TODO-lev: test max_pair_stake_amount
default_conf['use_sell_signal'] = False
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_max_pair_stake_amount", return_value=float('inf'))
patch_exchange(mocker)
default_conf['stake_amount'] = 'unlimited'
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.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!
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
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_max_pair_stake_amount", return_value=float('inf'))
patch_exchange(mocker)
default_conf['timeframe_detail'] = '1m'
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
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_max_pair_stake_amount", return_value=float('inf'))
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
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
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_max_pair_stake_amount", return_value=float('inf'))
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
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
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_max_pair_stake_amount", return_value=float('inf'))
tests = [
['sine', 9],
['raise', 10],
@ -806,6 +817,7 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir,
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_max_pair_stake_amount", return_value=float('inf'))
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
# While buy-signals are unrealistic, running backtesting
# 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):
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)
backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
pair='UNITTEST/BTC', datadir=testdatadir)
@ -892,6 +905,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
return dataframe
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)
patch_exchange(mocker)

View File

@ -17,6 +17,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
default_conf['use_sell_signal'] = False
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_max_pair_stake_amount", return_value=float('inf'))
patch_exchange(mocker)
default_conf.update({
"stake_amount": 100.0,

View File

@ -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))
(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)]
order = limit_order[enter_side(is_short)]
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.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])
def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_order, is_short) -> None:

View File

@ -180,23 +180,31 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r
assert result == 0
@pytest.mark.parametrize('stake_amount,min_stake_amount,max_stake_amount,expected', [
(22, 11, 50, 22),
(100, 11, 500, 100),
(1000, 11, 500, 500), # Above max-stake
(20, 15, 10, 0), # Minimum stake > max-stake
(9, 11, 100, 11), # Below min stake
(1, 15, 10, 0), # Below min stake and min_stake > max_stake
(20, 50, 100, 0), # Below min stake and stake * 1.3 > min_stake
@pytest.mark.parametrize('stake_amount,min_stake,stake_available,max_stake,expected', [
(22, 11, 50, 10000, 22),
(100, 11, 500, 10000, 100),
(1000, 11, 500, 10000, 500), # Above stake_available
(700, 11, 1000, 400, 400), # Above max_stake, below stake available
(20, 15, 10, 10000, 0), # Minimum stake > stake_available
(9, 11, 100, 10000, 11), # Below 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,
stake_amount, min_stake_amount, max_stake_amount, expected):
def test_validate_stake_amount(
mocker,
default_conf,
stake_amount,
min_stake,
stake_available,
max_stake,
expected,
):
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount",
return_value=max_stake_amount)
res = freqtrade.wallets.validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount)
return_value=stake_available)
res = freqtrade.wallets.validate_stake_amount('XRP/USDT', stake_amount, min_stake, max_stake)
assert res == expected