Implement fee rate calculation
This commit is contained in:
parent
45c97fde2d
commit
59bafc8d02
@ -22,7 +22,7 @@ from freqtrade.data.converter import ohlcv_to_dataframe
|
|||||||
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async
|
from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async
|
||||||
from freqtrade.misc import deep_merge_dicts
|
from freqtrade.misc import deep_merge_dicts, safe_value_fallback
|
||||||
|
|
||||||
|
|
||||||
CcxtModuleType = Any
|
CcxtModuleType = Any
|
||||||
@ -1084,8 +1084,33 @@ class Exchange:
|
|||||||
and order['fee']['cost'] is not None
|
and order['fee']['cost'] is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
def calculate_fee_rate(self, order: Dict) -> Optional[float]:
|
||||||
def extract_cost_curr_rate(order: Dict) -> Tuple[float, str, Optional[float]]:
|
"""
|
||||||
|
Calculate fee rate if it's not given by the exchange.
|
||||||
|
:param order: Order or trade (one trade) dict
|
||||||
|
"""
|
||||||
|
if order['fee'].get('rate') is not None:
|
||||||
|
return order['fee'].get('rate')
|
||||||
|
fee_curr = order['fee']['currency']
|
||||||
|
# Calculate fee based on order details
|
||||||
|
if fee_curr in self.get_pair_base_currency(order['symbol']):
|
||||||
|
# Base currency - divide by amount
|
||||||
|
return round(order['fee']['cost'] / order['amount'], 8)
|
||||||
|
elif fee_curr in self.get_pair_quote_currency(order['symbol']):
|
||||||
|
# Quote currency - divide by cost
|
||||||
|
return round(order['fee']['cost'] / order['cost'], 8)
|
||||||
|
else:
|
||||||
|
# If Fee currency is a different currency
|
||||||
|
try:
|
||||||
|
comb = self.get_valid_pair_combination(fee_curr, self._config['stake_currency'])
|
||||||
|
tick = self.fetch_ticker(comb)
|
||||||
|
|
||||||
|
fee_to_quote_rate = safe_value_fallback(tick, tick, 'last', 'ask')
|
||||||
|
return round((order['fee']['cost'] * fee_to_quote_rate) / order['cost'], 8)
|
||||||
|
except DependencyException:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def extract_cost_curr_rate(self, order: Dict) -> Tuple[float, str, Optional[float]]:
|
||||||
"""
|
"""
|
||||||
Extract tuple of cost, currency, rate.
|
Extract tuple of cost, currency, rate.
|
||||||
Requires order_has_fee to run first!
|
Requires order_has_fee to run first!
|
||||||
@ -1094,7 +1119,7 @@ class Exchange:
|
|||||||
"""
|
"""
|
||||||
return (order['fee']['cost'],
|
return (order['fee']['cost'],
|
||||||
order['fee']['currency'],
|
order['fee']['currency'],
|
||||||
order['fee'].get('rate', None))
|
self.calculate_fee_rate(order))
|
||||||
# calculate rate ? (order['fee']['cost'] / (order['amount'] * order['price']))
|
# calculate rate ? (order['fee']['cost'] / (order['amount'] * order['price']))
|
||||||
|
|
||||||
|
|
||||||
|
@ -2159,9 +2159,44 @@ def test_order_has_fee(order, expected) -> None:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("order,expected", [
|
@pytest.mark.parametrize("order,expected", [
|
||||||
({'fee': {'currency': 'ETH/BTC', 'cost': 0.43}}, (0.43, 'ETH/BTC', None)),
|
({'symbol': 'ETH/BTC', 'fee': {'currency': 'ETH', 'cost': 0.43}},
|
||||||
({'fee': {'currency': 'ETH/USDT', 'cost': 0.01}}, (0.01, 'ETH/USDT', None)),
|
(0.43, 'ETH', 0.01)),
|
||||||
({'fee': {'currency': 'ETH/USDT', 'cost': 0.34, 'rate': 0.01}}, (0.34, 'ETH/USDT', 0.01)),
|
({'symbol': 'ETH/USDT', 'fee': {'currency': 'USDT', 'cost': 0.01}},
|
||||||
|
(0.01, 'USDT', 0.01)),
|
||||||
|
({'symbol': 'BTC/USDT', 'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.01}},
|
||||||
|
(0.34, 'USDT', 0.01)),
|
||||||
])
|
])
|
||||||
def test_extract_cost_curr_rate(order, expected) -> None:
|
def test_extract_cost_curr_rate(mocker, default_conf, order, expected) -> None:
|
||||||
assert Exchange.extract_cost_curr_rate(order) == expected
|
mocker.patch('freqtrade.exchange.Exchange.calculate_fee_rate', MagicMock(return_value=0.01))
|
||||||
|
ex = get_patched_exchange(mocker, default_conf)
|
||||||
|
assert ex.extract_cost_curr_rate(order) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("order,expected", [
|
||||||
|
# Using base-currency
|
||||||
|
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
|
||||||
|
'fee': {'currency': 'ETH', 'cost': 0.004, 'rate': None}}, 0.1),
|
||||||
|
({'symbol': 'ETH/BTC', 'amount': 0.05, 'cost': 0.05,
|
||||||
|
'fee': {'currency': 'ETH', 'cost': 0.004, 'rate': None}}, 0.08),
|
||||||
|
# Using quote currency
|
||||||
|
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
|
||||||
|
'fee': {'currency': 'BTC', 'cost': 0.005}}, 0.1),
|
||||||
|
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
|
||||||
|
'fee': {'currency': 'BTC', 'cost': 0.002, 'rate': None}}, 0.04),
|
||||||
|
# Using foreign currency
|
||||||
|
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
|
||||||
|
'fee': {'currency': 'NEO', 'cost': 0.0012}}, 0.001944),
|
||||||
|
({'symbol': 'ETH/BTC', 'amount': 2.21, 'cost': 0.02992561,
|
||||||
|
'fee': {'currency': 'NEO', 'cost': 0.00027452}}, 0.00074305),
|
||||||
|
# TODO: More tests here!
|
||||||
|
# Rate included in return - return as is
|
||||||
|
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
|
||||||
|
'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.01}}, 0.01),
|
||||||
|
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
|
||||||
|
'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.005}}, 0.005),
|
||||||
|
])
|
||||||
|
def test_calculate_fee_rate(mocker, default_conf, order, expected) -> None:
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'last': 0.081})
|
||||||
|
|
||||||
|
ex = get_patched_exchange(mocker, default_conf)
|
||||||
|
assert ex.calculate_fee_rate(order) == expected
|
||||||
|
Loading…
Reference in New Issue
Block a user