contract-sizes tests
This commit is contained in:
parent
d0a300a2e1
commit
78d1a267f0
@ -372,13 +372,17 @@ class Exchange:
|
||||
|
||||
def _get_contract_size(self, pair: str) -> int:
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
return self.markets[pair]['contract_size']
|
||||
market = self.markets[pair]
|
||||
contract_size = 1
|
||||
if 'contractSize' in market and market['contractSize'] is not None:
|
||||
contract_size = market['contractSize']
|
||||
return contract_size
|
||||
else:
|
||||
return 1
|
||||
|
||||
def _trades_contracts_to_amount(self, trades: List) -> List:
|
||||
if len(trades) > 0:
|
||||
contract_size = self._get_contract_size(trades[0]['pair'])
|
||||
contract_size = self._get_contract_size(trades[0]['symbol'])
|
||||
if contract_size != 1:
|
||||
for trade in trades:
|
||||
trade['amount'] = trade['amount'] * contract_size
|
||||
@ -387,10 +391,10 @@ class Exchange:
|
||||
return trades
|
||||
|
||||
def _order_contracts_to_amount(self, order: Dict) -> Dict:
|
||||
contract_size = self._get_contract_size(order['pair'])
|
||||
contract_size = self._get_contract_size(order['symbol'])
|
||||
if contract_size != 1:
|
||||
for prop in ['amount', 'cost', 'filled', 'remaining']:
|
||||
if prop in order:
|
||||
if prop in order and order[prop] is not None:
|
||||
order[prop] = order[prop] * contract_size
|
||||
return order
|
||||
|
||||
@ -611,7 +615,7 @@ class Exchange:
|
||||
Re-implementation of ccxt internal methods - ensuring we can test the result is correct
|
||||
based on our definitions.
|
||||
"""
|
||||
amount = self._amount_to_contract_size(pair, amount)
|
||||
amount = self._amount_to_contracts(pair, amount)
|
||||
if self.markets[pair]['precision']['amount']:
|
||||
amount = float(decimal_to_precision(amount, rounding_mode=TRUNCATE,
|
||||
precision=self.markets[pair]['precision']['amount'],
|
||||
@ -670,12 +674,17 @@ class Exchange:
|
||||
limits = market['limits']
|
||||
if ('cost' in limits and 'min' in limits['cost']
|
||||
and limits['cost']['min'] is not None):
|
||||
min_stake_amounts.append(limits['cost']['min'])
|
||||
min_stake_amounts.append(
|
||||
self._contracts_to_amount(
|
||||
pair,
|
||||
limits['cost']['min']
|
||||
)
|
||||
)
|
||||
|
||||
if ('amount' in limits and 'min' in limits['amount']
|
||||
and limits['amount']['min'] is not None):
|
||||
min_stake_amounts.append(
|
||||
self._contract_size_to_amount(
|
||||
self._contracts_to_amount(
|
||||
pair,
|
||||
limits['amount']['min'] * price
|
||||
)
|
||||
@ -715,7 +724,7 @@ class Exchange:
|
||||
def create_dry_run_order(self, pair: str, ordertype: str, side: str, amount: float,
|
||||
rate: float, leverage: float, params: Dict = {}) -> Dict[str, Any]:
|
||||
order_id = f'dry_run_{side}_{datetime.now().timestamp()}'
|
||||
_amount = self._contract_size_to_amount(pair, self.amount_to_precision(pair, amount))
|
||||
_amount = self._contracts_to_amount(pair, self.amount_to_precision(pair, amount))
|
||||
dry_order: Dict[str, Any] = {
|
||||
'id': order_id,
|
||||
'symbol': pair,
|
||||
@ -865,19 +874,19 @@ class Exchange:
|
||||
params.update({param: time_in_force})
|
||||
return params
|
||||
|
||||
def _amount_to_contract_size(self, pair: str, amount: float):
|
||||
def _amount_to_contracts(self, pair: str, amount: float):
|
||||
|
||||
if ('contractSize' in self.markets[pair]):
|
||||
return amount / self.markets[pair]['contractSize']
|
||||
else:
|
||||
return amount
|
||||
|
||||
def _contract_size_to_amount(self, pair: str, amount: float):
|
||||
def _contracts_to_amount(self, pair: str, num_contracts: float):
|
||||
|
||||
if ('contractSize' in self.markets[pair]):
|
||||
return amount * self.markets[pair]['contractSize']
|
||||
return num_contracts * self.markets[pair]['contractSize']
|
||||
else:
|
||||
return amount
|
||||
return num_contracts
|
||||
|
||||
def create_order(self, pair: str, ordertype: str, side: str, amount: float,
|
||||
rate: float, leverage: float = 1.0, time_in_force: str = 'gtc') -> Dict:
|
||||
|
@ -914,6 +914,7 @@ def get_markets():
|
||||
'active': True,
|
||||
'spot': True,
|
||||
'type': 'spot',
|
||||
'contractSize': None,
|
||||
'precision': {
|
||||
'amount': 8,
|
||||
'price': 8
|
||||
@ -937,7 +938,8 @@ def get_markets():
|
||||
'quote': 'USDT',
|
||||
'active': True,
|
||||
'spot': False,
|
||||
'type': 'SomethingElse',
|
||||
'type': 'swap',
|
||||
'contractSize': 0.01,
|
||||
'precision': {
|
||||
'amount': 8,
|
||||
'price': 8
|
||||
@ -985,6 +987,59 @@ def get_markets():
|
||||
'info': {
|
||||
}
|
||||
},
|
||||
'ETH/USDT:USDT': {
|
||||
'id': 'ETH_USDT',
|
||||
'symbol': 'ETH/USDT:USDT',
|
||||
'base': 'ETH',
|
||||
'quote': 'USDT',
|
||||
'settle': 'USDT',
|
||||
'baseId': 'ETH',
|
||||
'quoteId': 'USDT',
|
||||
'settleId': 'USDT',
|
||||
'type': 'swap',
|
||||
'spot': False,
|
||||
'margin': False,
|
||||
'swap': True,
|
||||
'futures': False,
|
||||
'option': False,
|
||||
'derivative': True,
|
||||
'contract': True,
|
||||
'linear': True,
|
||||
'inverse': False,
|
||||
'tierBased': False,
|
||||
'percentage': True,
|
||||
'taker': 0.0006,
|
||||
'maker': 0.0002,
|
||||
'contractSize': 10,
|
||||
'active': True,
|
||||
'expiry': None,
|
||||
'expiryDatetime': None,
|
||||
'strike': None,
|
||||
'optionType': None,
|
||||
'limits': {
|
||||
'leverage': {
|
||||
'min': 1,
|
||||
'max': 100
|
||||
},
|
||||
'amount': {
|
||||
'min': 1,
|
||||
'max': 300000
|
||||
},
|
||||
'price': {
|
||||
'min': None,
|
||||
'max': None,
|
||||
},
|
||||
'cost': {
|
||||
'min': None,
|
||||
'max': None,
|
||||
}
|
||||
},
|
||||
'precision': {
|
||||
'price': 0.05,
|
||||
'amount': 1
|
||||
},
|
||||
'info': {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,6 +20,7 @@ from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, API_RETRY_CO
|
||||
from freqtrade.exchange.exchange import (market_is_active, timeframe_to_minutes, timeframe_to_msecs,
|
||||
timeframe_to_next_date, timeframe_to_prev_date,
|
||||
timeframe_to_seconds)
|
||||
from freqtrade.persistence.models import Trade
|
||||
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||
from tests.conftest import get_mock_coro, get_patched_exchange, log_has, log_has_re, num_log_has_re
|
||||
|
||||
@ -224,28 +225,34 @@ def test_validate_order_time_in_force(default_conf, mocker, caplog):
|
||||
ex.validate_order_time_in_force(tif2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("amount,precision_mode,precision,expected", [
|
||||
(2.34559, 2, 4, 2.3455),
|
||||
(2.34559, 2, 5, 2.34559),
|
||||
(2.34559, 2, 3, 2.345),
|
||||
(2.9999, 2, 3, 2.999),
|
||||
(2.9909, 2, 3, 2.990),
|
||||
@pytest.mark.parametrize("amount,precision_mode,precision,contract_size,expected", [
|
||||
(2.34559, 2, 4, 1, 2.3455),
|
||||
(2.34559, 2, 5, 1, 2.34559),
|
||||
(2.34559, 2, 3, 1, 2.345),
|
||||
(2.9999, 2, 3, 1, 2.999),
|
||||
(2.9909, 2, 3, 1, 2.990),
|
||||
# Tests for Tick-size
|
||||
(2.34559, 4, 0.0001, 2.3455),
|
||||
(2.34559, 4, 0.00001, 2.34559),
|
||||
(2.34559, 4, 0.001, 2.345),
|
||||
(2.9999, 4, 0.001, 2.999),
|
||||
(2.9909, 4, 0.001, 2.990),
|
||||
(2.9909, 4, 0.005, 2.990),
|
||||
(2.9999, 4, 0.005, 2.995),
|
||||
(2.34559, 4, 0.0001, 1, 2.3455),
|
||||
(2.34559, 4, 0.00001, 1, 2.34559),
|
||||
(2.34559, 4, 0.001, 1, 2.345),
|
||||
(2.9999, 4, 0.001, 1, 2.999),
|
||||
(2.9909, 4, 0.001, 1, 2.990),
|
||||
(2.9909, 4, 0.005, 0.01, 0.025),
|
||||
(2.9999, 4, 0.005, 10, 29.995),
|
||||
])
|
||||
def test_amount_to_precision(default_conf, mocker, amount, precision_mode, precision, expected):
|
||||
def test_amount_to_precision(default_conf, mocker, amount, precision_mode, precision, contract_size, expected):
|
||||
"""
|
||||
Test rounds down
|
||||
"""
|
||||
# TODO-lev: Test for contract sizes of 0.01 and 10
|
||||
|
||||
markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'amount': precision}}})
|
||||
markets = PropertyMock(return_value={
|
||||
'ETH/BTC': {
|
||||
'contractSize': contract_size,
|
||||
'precision': {
|
||||
'amount': precision
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||
# digits counting mode
|
||||
@ -324,12 +331,11 @@ 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:
|
||||
# TODO-lev: Test for contract sizes of 0.01 and 10
|
||||
def test_get_min_pair_stake_amount(mocker, default_conf, markets) -> None:
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||
stoploss = -0.05
|
||||
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}}
|
||||
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}, }
|
||||
|
||||
# no pair found
|
||||
mocker.patch(
|
||||
@ -467,6 +473,25 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0)
|
||||
assert isclose(result, expected_result/12)
|
||||
|
||||
markets["ETH/BTC"]["contractSize"] = 0.01
|
||||
mocker.patch(
|
||||
'freqtrade.exchange.Exchange.markets',
|
||||
PropertyMock(return_value=markets)
|
||||
)
|
||||
|
||||
# Contract size 0.01
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1)
|
||||
assert isclose(result, expected_result * 0.01)
|
||||
|
||||
markets["ETH/BTC"]["contractSize"] = 10
|
||||
mocker.patch(
|
||||
'freqtrade.exchange.Exchange.markets',
|
||||
PropertyMock(return_value=markets)
|
||||
)
|
||||
# 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)
|
||||
|
||||
|
||||
def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||
@ -1006,7 +1031,6 @@ def test_exchange_has(default_conf, mocker):
|
||||
])
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_create_dry_run_order(default_conf, mocker, side, exchange_name):
|
||||
# TODO-lev: Test for contract sizes of 0.01 and 10
|
||||
default_conf['dry_run'] = True
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
|
||||
@ -1023,6 +1047,7 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name):
|
||||
assert order["side"] == side
|
||||
assert order["type"] == "limit"
|
||||
assert order["symbol"] == "ETH/BTC"
|
||||
assert order["amount"] == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("side,startprice,endprice", [
|
||||
@ -1123,7 +1148,6 @@ def test_create_dry_run_order_market_fill(default_conf, mocker, side, rate, amou
|
||||
])
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice, exchange_name):
|
||||
# TODO-lev: Test for contract sizes of 0.01 and 10
|
||||
api_mock = MagicMock()
|
||||
order_id = 'test_prod_{}_{}'.format(side, randint(0, 10 ** 6))
|
||||
api_mock.options = {} if not marketprice else {"createMarketBuyOrderRequiresPrice": True}
|
||||
@ -1131,9 +1155,12 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
||||
'id': order_id,
|
||||
'info': {
|
||||
'foo': 'bar'
|
||||
}
|
||||
},
|
||||
'symbol': 'XLTCUSDT',
|
||||
'amount': 1
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
default_conf['collateral'] = 'isolated'
|
||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
@ -1141,7 +1168,7 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
||||
exchange.set_margin_mode = MagicMock()
|
||||
|
||||
order = exchange.create_order(
|
||||
pair='ETH/BTC',
|
||||
pair='XLTCUSDT',
|
||||
ordertype=ordertype,
|
||||
side=side,
|
||||
amount=1,
|
||||
@ -1152,7 +1179,8 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
||||
assert 'id' in order
|
||||
assert 'info' in order
|
||||
assert order['id'] == order_id
|
||||
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
|
||||
assert order['amount'] == 1
|
||||
assert api_mock.create_order.call_args[0][0] == 'XLTCUSDT'
|
||||
assert api_mock.create_order.call_args[0][1] == ordertype
|
||||
assert api_mock.create_order.call_args[0][2] == side
|
||||
assert api_mock.create_order.call_args[0][3] == 1
|
||||
@ -1162,7 +1190,7 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
||||
|
||||
exchange.trading_mode = TradingMode.FUTURES
|
||||
order = exchange.create_order(
|
||||
pair='ETH/BTC',
|
||||
pair='XLTCUSDT',
|
||||
ordertype=ordertype,
|
||||
side=side,
|
||||
amount=1,
|
||||
@ -1172,6 +1200,8 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
||||
|
||||
assert exchange._set_leverage.call_count == 1
|
||||
assert exchange.set_margin_mode.call_count == 1
|
||||
# assert api_mock.create_order.call_args[0][3] == 100
|
||||
assert order['amount'] == 0.01
|
||||
|
||||
|
||||
def test_buy_dry_run(default_conf, mocker):
|
||||
@ -2291,6 +2321,43 @@ async def test__async_fetch_trades(default_conf, mocker, caplog, exchange_name,
|
||||
await exchange._async_fetch_trades(pair, since=(arrow.utcnow().int_timestamp - 2000) * 1000)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
async def test__async_fetch_trades_contract_size(default_conf, mocker, caplog, exchange_name,
|
||||
fetch_trades_result):
|
||||
caplog.set_level(logging.DEBUG)
|
||||
default_conf['collateral'] = 'isolated'
|
||||
default_conf['trading_mode'] = 'futures'
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
# Monkey-patch async function
|
||||
exchange._api_async.fetch_trades = get_mock_coro([
|
||||
{'info': {'a': 126181333,
|
||||
'p': '0.01952600',
|
||||
'q': '0.01200000',
|
||||
'f': 138604158,
|
||||
'l': 138604158,
|
||||
'T': 1565798399872,
|
||||
'm': True,
|
||||
'M': True},
|
||||
'timestamp': 1565798399872,
|
||||
'datetime': '2019-08-14T15:59:59.872Z',
|
||||
'symbol': 'ETH/USDT:USDT',
|
||||
'id': '126181383',
|
||||
'order': None,
|
||||
'type': None,
|
||||
'takerOrMaker': None,
|
||||
'side': 'sell',
|
||||
'price': 2.0,
|
||||
'amount': 30.0,
|
||||
'cost': 60.0,
|
||||
'fee': None}]
|
||||
)
|
||||
|
||||
pair = 'ETH/USDT:USDT'
|
||||
res = await exchange._async_fetch_trades(pair, since=None, params=None)
|
||||
assert res[0][5] == 300
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
async def test__async_get_trade_history_id(default_conf, mocker, exchange_name,
|
||||
@ -2515,24 +2582,31 @@ def test_cancel_order_with_result_error(default_conf, mocker, exchange_name, cap
|
||||
|
||||
|
||||
# Ensure that if not dry_run, we should call API
|
||||
@pytest.mark.parametrize("trading_mode,amount", [
|
||||
('spot', 2),
|
||||
('futures', 20),
|
||||
])
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_cancel_order(default_conf, mocker, exchange_name):
|
||||
# TODO-lev: Test for contract sizes of 0.01 and 10
|
||||
def test_cancel_order(default_conf, mocker, exchange_name, trading_mode, amount):
|
||||
default_conf['dry_run'] = False
|
||||
default_conf['trading_mode'] = trading_mode
|
||||
default_conf['collateral'] = 'isolated'
|
||||
api_mock = MagicMock()
|
||||
api_mock.cancel_order = MagicMock(return_value={'id': '123'})
|
||||
api_mock.cancel_order = MagicMock(
|
||||
return_value={'id': '123', 'amount': 2, 'symbol': 'ETH/USDT:USDT'})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == {'id': '123'}
|
||||
assert exchange.cancel_order(
|
||||
order_id='_', pair='ETH/USDT:USDT') == {'id': '123', 'amount': amount, 'symbol': 'ETH/USDT:USDT'}
|
||||
|
||||
with pytest.raises(InvalidOrderException):
|
||||
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order"))
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.cancel_order(order_id='_', pair='TKN/BTC')
|
||||
exchange.cancel_order(order_id='_', pair='ETH/USDT:USDT')
|
||||
assert api_mock.cancel_order.call_count == 1
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
"cancel_order", "cancel_order",
|
||||
order_id='_', pair='TKN/BTC')
|
||||
order_id='_', pair='ETH/USDT:USDT')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
@ -2594,13 +2668,17 @@ def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
|
||||
exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=123)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("trading_mode,amount", [
|
||||
('spot', 2),
|
||||
('futures', 20),
|
||||
])
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_fetch_order(default_conf, mocker, exchange_name, caplog):
|
||||
# TODO-lev: Test for contract sizes of 0.01 and 10
|
||||
def test_fetch_order(default_conf, mocker, exchange_name, caplog, trading_mode, amount):
|
||||
default_conf['dry_run'] = True
|
||||
default_conf['exchange']['log_responses'] = True
|
||||
order = MagicMock()
|
||||
order.myid = 123
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
exchange._dry_run_open_orders['X'] = order
|
||||
assert exchange.fetch_order('X', 'TKN/BTC').myid == 123
|
||||
@ -2609,11 +2687,20 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog):
|
||||
exchange.fetch_order('Y', 'TKN/BTC')
|
||||
|
||||
default_conf['dry_run'] = False
|
||||
default_conf['trading_mode'] = trading_mode
|
||||
default_conf['collateral'] = 'isolated'
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_order = MagicMock(return_value=456)
|
||||
api_mock.fetch_order = MagicMock(
|
||||
return_value={'id': '123', 'amount': 2, 'symbol': 'ETH/USDT:USDT'})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
assert exchange.fetch_order('X', 'TKN/BTC') == 456
|
||||
assert log_has("API fetch_order: 456", caplog)
|
||||
assert exchange.fetch_order(
|
||||
'X', 'TKN/BTC') == {'id': '123', 'amount': amount, 'symbol': 'ETH/USDT:USDT'}
|
||||
assert log_has(
|
||||
("API fetch_order: {\'id\': \'123\', \'amount\': "
|
||||
+ str(amount) + ", \'symbol\': \'ETH/USDT:USDT\'}"
|
||||
),
|
||||
caplog
|
||||
)
|
||||
|
||||
with pytest.raises(InvalidOrderException):
|
||||
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
|
||||
@ -2706,12 +2793,17 @@ def test_name(default_conf, mocker, exchange_name):
|
||||
assert exchange.id == exchange_name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("trading_mode,amount", [
|
||||
('spot', 0.2340606),
|
||||
('futures', 2.340606),
|
||||
])
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_trades_for_order(default_conf, mocker, exchange_name):
|
||||
# TODO-lev: Test for contract sizes of 0.01 and 10
|
||||
def test_get_trades_for_order(default_conf, mocker, exchange_name, trading_mode, amount):
|
||||
order_id = 'ABCD-ABCD'
|
||||
since = datetime(2018, 5, 5, 0, 0, 0)
|
||||
default_conf["dry_run"] = False
|
||||
default_conf["trading_mode"] = trading_mode
|
||||
default_conf["collateral"] = 'isolated'
|
||||
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
||||
api_mock = MagicMock()
|
||||
|
||||
@ -2728,22 +2820,24 @@ def test_get_trades_for_order(default_conf, mocker, exchange_name):
|
||||
'id': 'ABCD-ABCD'},
|
||||
'timestamp': 1519860024438,
|
||||
'datetime': '2018-02-28T23:20:24.438Z',
|
||||
'symbol': 'LTC/BTC',
|
||||
'symbol': 'ETH/USDT:USDT',
|
||||
'type': 'limit',
|
||||
'side': 'buy',
|
||||
'price': 165.0,
|
||||
'amount': 0.2340606,
|
||||
'fee': {'cost': 0.06179, 'currency': 'BTC'}
|
||||
}])
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
orders = exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
|
||||
orders = exchange.get_trades_for_order(order_id, 'ETH/USDT:USDT', since)
|
||||
assert len(orders) == 1
|
||||
assert orders[0]['price'] == 165
|
||||
assert orders[0]['amount'] == amount
|
||||
assert api_mock.fetch_my_trades.call_count == 1
|
||||
# since argument should be
|
||||
assert isinstance(api_mock.fetch_my_trades.call_args[0][1], int)
|
||||
assert api_mock.fetch_my_trades.call_args[0][0] == 'LTC/BTC'
|
||||
assert api_mock.fetch_my_trades.call_args[0][0] == 'ETH/USDT:USDT'
|
||||
# Same test twice, hardcoded number and doing the same calculation
|
||||
assert api_mock.fetch_my_trades.call_args[0][1] == 1525478395000
|
||||
assert api_mock.fetch_my_trades.call_args[0][1] == int(since.replace(
|
||||
@ -2751,10 +2845,10 @@ def test_get_trades_for_order(default_conf, mocker, exchange_name):
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
'get_trades_for_order', 'fetch_my_trades',
|
||||
order_id=order_id, pair='LTC/BTC', since=since)
|
||||
order_id=order_id, pair='ETH/USDT:USDT', since=since)
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=False))
|
||||
assert exchange.get_trades_for_order(order_id, 'LTC/BTC', since) == []
|
||||
assert exchange.get_trades_for_order(order_id, 'ETH/USDT:USDT', since) == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
@ -3462,6 +3556,81 @@ def test__get_funding_fee(
|
||||
assert kraken._get_funding_fee(size, funding_rate, mark_price, time_in_ratio) == kraken_fee
|
||||
|
||||
|
||||
def test__get_mark_price_history(mocker, default_conf, mark_ohlcv):
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_ohlcv = MagicMock(return_value=mark_ohlcv)
|
||||
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
||||
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_funding_fees', lambda pair, since: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
mark_prices = exchange._get_mark_price_history("ADA/USDT", 1630454400000)
|
||||
assert mark_prices == {
|
||||
1630454400000: 2.77,
|
||||
1630458000000: 2.73,
|
||||
1630461600000: 2.74,
|
||||
1630465200000: 2.76,
|
||||
1630468800000: 2.76,
|
||||
1630472400000: 2.77,
|
||||
1630476000000: 2.78,
|
||||
1630479600000: 2.78,
|
||||
1630483200000: 2.77,
|
||||
1630486800000: 2.77,
|
||||
1630490400000: 2.84,
|
||||
1630494000000: 2.81,
|
||||
1630497600000: 2.81,
|
||||
1630501200000: 2.82,
|
||||
}
|
||||
|
||||
ccxt_exceptionhandlers(
|
||||
mocker,
|
||||
default_conf,
|
||||
api_mock,
|
||||
"binance",
|
||||
"_get_mark_price_history",
|
||||
"fetch_ohlcv",
|
||||
pair="ADA/USDT",
|
||||
since=1635580800001
|
||||
)
|
||||
|
||||
|
||||
def test_get_funding_rate_history(mocker, default_conf, funding_rate_history_hourly):
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_funding_rate_history = MagicMock(return_value=funding_rate_history_hourly)
|
||||
type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True})
|
||||
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_funding_fees', lambda pair, since: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
funding_rates = exchange.get_funding_rate_history('ADA/USDT', 1635580800001)
|
||||
|
||||
assert funding_rates == {
|
||||
1630454400000: -0.000008,
|
||||
1630458000000: -0.000004,
|
||||
1630461600000: 0.000012,
|
||||
1630465200000: -0.000003,
|
||||
1630468800000: -0.000007,
|
||||
1630472400000: 0.000003,
|
||||
1630476000000: 0.000019,
|
||||
1630479600000: 0.000003,
|
||||
1630483200000: -0.000003,
|
||||
1630486800000: 0,
|
||||
1630490400000: 0.000013,
|
||||
1630494000000: 0.000077,
|
||||
1630497600000: 0.000072,
|
||||
1630501200000: 0.000097,
|
||||
}
|
||||
|
||||
ccxt_exceptionhandlers(
|
||||
mocker,
|
||||
default_conf,
|
||||
api_mock,
|
||||
"binance",
|
||||
"get_funding_rate_history",
|
||||
"fetch_funding_rate_history",
|
||||
pair="ADA/USDT",
|
||||
since=1630454400000
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('exchange,rate_start,rate_end,d1,d2,amount,expected_fees', [
|
||||
('binance', 0, 2, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, -0.0009140999999999999),
|
||||
('binance', 0, 2, "2021-09-01 00:00:15", "2021-09-01 08:00:00", 30.0, -0.0009140999999999999),
|
||||
@ -3583,26 +3752,163 @@ def test__calculate_funding_fees_datetime_called(
|
||||
assert funding_fees == expected_fees
|
||||
|
||||
|
||||
def test__get_contract_size():
|
||||
# TODO-lev
|
||||
return
|
||||
@pytest.mark.parametrize('pair,expected_size,trading_mode', [
|
||||
('XLTCUSDT', 1, 'spot'),
|
||||
('LTC/USD', 1, 'futures'),
|
||||
('XLTCUSDT', 0.01, 'futures'),
|
||||
('LTC/ETH', 1, 'futures'),
|
||||
('ETH/USDT:USDT', 10, 'futures')
|
||||
])
|
||||
def test__get_contract_size(mocker, default_conf, markets, pair, expected_size, trading_mode):
|
||||
api_mock = MagicMock()
|
||||
default_conf['trading_mode'] = trading_mode
|
||||
default_conf['collateral'] = 'isolated'
|
||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
size = exchange._get_contract_size(pair)
|
||||
assert expected_size == size
|
||||
|
||||
|
||||
def test__trades_contracts_to_amount():
|
||||
# TODO-lev
|
||||
return
|
||||
@pytest.mark.parametrize('pair,contract_size,trading_mode', [
|
||||
('XLTCUSDT', 1, 'spot'),
|
||||
('LTC/USD', 1, 'futures'),
|
||||
('XLTCUSDT', 0.01, 'futures'),
|
||||
('LTC/ETH', 1, 'futures'),
|
||||
('ETH/USDT:USDT', 10, 'futures'),
|
||||
])
|
||||
def test__order_contracts_to_amount(
|
||||
mocker,
|
||||
default_conf,
|
||||
markets,
|
||||
pair,
|
||||
contract_size,
|
||||
trading_mode,
|
||||
):
|
||||
api_mock = MagicMock()
|
||||
default_conf['trading_mode'] = trading_mode
|
||||
default_conf['collateral'] = 'isolated'
|
||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
orders = [
|
||||
{
|
||||
'id': '123456320',
|
||||
'clientOrderId': '12345632018',
|
||||
'timestamp': 1640124992000,
|
||||
'datetime': 'Tue 21 Dec 2021 22:16:32 UTC',
|
||||
'lastTradeTimestamp': 1640124911000,
|
||||
'status': 'active',
|
||||
'symbol': pair,
|
||||
'type': 'limit',
|
||||
'timeInForce': 'gtc',
|
||||
'postOnly': None,
|
||||
'side': 'buy',
|
||||
'price': 2.0,
|
||||
'stopPrice': None,
|
||||
'average': None,
|
||||
'amount': 30.0,
|
||||
'cost': 60.0,
|
||||
'filled': None,
|
||||
'remaining': 30.0,
|
||||
'fee': 0.06,
|
||||
'fees': [{
|
||||
'currency': 'USDT',
|
||||
'cost': 0.06,
|
||||
}],
|
||||
'trades': None,
|
||||
'info': {},
|
||||
},
|
||||
{
|
||||
'id': '123456380',
|
||||
'clientOrderId': '12345638203',
|
||||
'timestamp': 1640124992000,
|
||||
'datetime': 'Tue 21 Dec 2021 22:16:32 UTC',
|
||||
'lastTradeTimestamp': 1640124911000,
|
||||
'status': 'active',
|
||||
'symbol': pair,
|
||||
'type': 'limit',
|
||||
'timeInForce': 'gtc',
|
||||
'postOnly': None,
|
||||
'side': 'sell',
|
||||
'price': 2.2,
|
||||
'stopPrice': None,
|
||||
'average': None,
|
||||
'amount': 40.0,
|
||||
'cost': 80.0,
|
||||
'filled': None,
|
||||
'remaining': 40.0,
|
||||
'fee': 0.08,
|
||||
'fees': [{
|
||||
'currency': 'USDT',
|
||||
'cost': 0.08,
|
||||
}],
|
||||
'trades': None,
|
||||
'info': {},
|
||||
},
|
||||
]
|
||||
|
||||
order1 = exchange._order_contracts_to_amount(orders[0])
|
||||
order2 = exchange._order_contracts_to_amount(orders[1])
|
||||
assert order1['amount'] == 30.0 * contract_size
|
||||
assert order2['amount'] == 40.0 * contract_size
|
||||
|
||||
|
||||
def test__order_contracts_to_amount():
|
||||
# TODO-lev
|
||||
return
|
||||
@pytest.mark.parametrize('pair,contract_size,trading_mode', [
|
||||
('XLTCUSDT', 1, 'spot'),
|
||||
('LTC/USD', 1, 'futures'),
|
||||
('XLTCUSDT', 0.01, 'futures'),
|
||||
('LTC/ETH', 1, 'futures'),
|
||||
('ETH/USDT:USDT', 10, 'futures'),
|
||||
])
|
||||
def test__trades_contracts_to_amount(
|
||||
mocker,
|
||||
default_conf,
|
||||
markets,
|
||||
pair,
|
||||
contract_size,
|
||||
trading_mode,
|
||||
):
|
||||
api_mock = MagicMock()
|
||||
default_conf['trading_mode'] = trading_mode
|
||||
default_conf['collateral'] = 'isolated'
|
||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
trades = [
|
||||
{
|
||||
'symbol': pair,
|
||||
'amount': 30.0,
|
||||
},
|
||||
{
|
||||
'symbol': pair,
|
||||
'amount': 40.0,
|
||||
}
|
||||
]
|
||||
|
||||
new_amount_trades = exchange._trades_contracts_to_amount(trades)
|
||||
assert new_amount_trades[0]['amount'] == 30.0 * contract_size
|
||||
assert new_amount_trades[1]['amount'] == 40.0 * contract_size
|
||||
|
||||
|
||||
def test__amount_to_contract_size():
|
||||
# TODO-lev
|
||||
return
|
||||
|
||||
|
||||
def test__contract_size_to_amount():
|
||||
# TODO-lev
|
||||
return
|
||||
@pytest.mark.parametrize('pair,param_amount,param_size', [
|
||||
('XLTCUSDT', 40, 4000),
|
||||
('LTC/ETH', 30, 30),
|
||||
('ETH/USDT:USDT', 10, 1),
|
||||
])
|
||||
def test__amount_to_contracts(
|
||||
mocker,
|
||||
default_conf,
|
||||
markets,
|
||||
pair,
|
||||
param_amount,
|
||||
param_size
|
||||
):
|
||||
api_mock = MagicMock()
|
||||
default_conf['trading_mode'] = 'futures'
|
||||
default_conf['collateral'] = 'isolated'
|
||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
result_size = exchange._amount_to_contracts(pair, param_amount)
|
||||
assert result_size == param_size
|
||||
result_amount = exchange._contracts_to_amount(pair, param_size)
|
||||
assert result_amount == param_amount
|
||||
|
Loading…
Reference in New Issue
Block a user