diff --git a/docs/configuration.md b/docs/configuration.md
index 00ab66ceb..f18f947e7 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -135,6 +135,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `exchange.markets_refresh_interval` | The interval in minutes in which markets are reloaded.
*Defaults to `60` minutes.*
**Datatype:** Positive Integer
| `exchange.skip_pair_validation` | Skip pairlist validation on startup.
*Defaults to `false`
**Datatype:** Boolean
| `exchange.skip_open_order_update` | Skips open order updates on startup should the exchange cause problems. Only relevant in live conditions.
*Defaults to `false`
**Datatype:** Boolean
+| `exchange.unknown_fee_rate` | Fallback value to use when calculating trading fees. This can be useful for exchanges which have fees in non-tradable currencies. The value provided here will be multiplied with the "fee cost".
*Defaults to `None`
**Datatype:** float
| `exchange.log_responses` | Log relevant exchange responses. For debug mode only - use with care.
*Defaults to `false`
**Datatype:** Boolean
| `edge.*` | Please refer to [edge configuration document](edge.md) for detailed explanation.
| `experimental.block_bad_exchanges` | Block exchanges known to not work with freqtrade. Leave on default unless you want to test if that exchange works now.
*Defaults to `true`.*
**Datatype:** Boolean
diff --git a/docs/exchanges.md b/docs/exchanges.md
index 3883e0b1d..374a6b8cc 100644
--- a/docs/exchanges.md
+++ b/docs/exchanges.md
@@ -199,6 +199,11 @@ OKEX requires a passphrase for each api key, you will therefore need to add this
!!! Warning
OKEX only provides 100 candles per api call. Therefore, the strategy will only have a pretty low amount of data available in backtesting mode.
+## Gate.io
+
+Gate.io allows the use of `POINT` to pay for fees. As this is not a tradable currency (no regular market available), automatic fee calculations will fail (and default to a fee of 0).
+The configuration parameter `exchange.unknown_fee_rate` can be used to specify the exchange rate between Point and the stake currency. Obviously, changing the stake-currency will also require changes to this value.
+
## All exchanges
Should you experience constant errors with Nonce (like `InvalidNonce`), it is best to regenerate the API keys. Resetting Nonce is difficult and it's usually easier to regenerate the API keys.
diff --git a/freqtrade/constants.py b/freqtrade/constants.py
index e074718ca..f15759ea5 100644
--- a/freqtrade/constants.py
+++ b/freqtrade/constants.py
@@ -395,6 +395,7 @@ CONF_SCHEMA = {
},
'uniqueItems': True
},
+ 'unknown_fee_rate': {'type': 'number'},
'outdated_offset': {'type': 'integer', 'minimum': 1},
'markets_refresh_interval': {'type': 'integer'},
'ccxt_config': {'type': 'object'},
diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py
index 22041ddef..66d6b3b55 100644
--- a/freqtrade/exchange/exchange.py
+++ b/freqtrade/exchange/exchange.py
@@ -1194,9 +1194,11 @@ class Exchange:
tick = self.fetch_ticker(comb)
fee_to_quote_rate = safe_value_fallback2(tick, tick, 'last', 'ask')
- return round((order['fee']['cost'] * fee_to_quote_rate) / order['cost'], 8)
except ExchangeError:
- return None
+ fee_to_quote_rate = self._config['exchange'].get('unknown_fee_rate', None)
+ if not fee_to_quote_rate:
+ return None
+ return round((order['fee']['cost'] * fee_to_quote_rate) / order['cost'], 8)
def extract_cost_curr_rate(self, order: Dict) -> Tuple[float, str, Optional[float]]:
"""
diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
index b33e0cbb7..a4b151742 100644
--- a/tests/exchange/test_exchange.py
+++ b/tests/exchange/test_exchange.py
@@ -2948,39 +2948,49 @@ def test_extract_cost_curr_rate(mocker, default_conf, order, expected) -> None:
assert ex.extract_cost_curr_rate(order) == expected
-@pytest.mark.parametrize("order,expected", [
+@pytest.mark.parametrize("order,unknown_fee_rate,expected", [
# Using base-currency
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
- 'fee': {'currency': 'ETH', 'cost': 0.004, 'rate': None}}, 0.1),
+ 'fee': {'currency': 'ETH', 'cost': 0.004, 'rate': None}}, None, 0.1),
({'symbol': 'ETH/BTC', 'amount': 0.05, 'cost': 0.05,
- 'fee': {'currency': 'ETH', 'cost': 0.004, 'rate': None}}, 0.08),
+ 'fee': {'currency': 'ETH', 'cost': 0.004, 'rate': None}}, None, 0.08),
# Using quote currency
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
- 'fee': {'currency': 'BTC', 'cost': 0.005}}, 0.1),
+ 'fee': {'currency': 'BTC', 'cost': 0.005}}, None, 0.1),
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
- 'fee': {'currency': 'BTC', 'cost': 0.002, 'rate': None}}, 0.04),
+ 'fee': {'currency': 'BTC', 'cost': 0.002, 'rate': None}}, None, 0.04),
# Using foreign currency
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
- 'fee': {'currency': 'NEO', 'cost': 0.0012}}, 0.001944),
+ 'fee': {'currency': 'NEO', 'cost': 0.0012}}, None, 0.001944),
({'symbol': 'ETH/BTC', 'amount': 2.21, 'cost': 0.02992561,
- 'fee': {'currency': 'NEO', 'cost': 0.00027452}}, 0.00074305),
+ 'fee': {'currency': 'NEO', 'cost': 0.00027452}}, None, 0.00074305),
# 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),
+ 'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.01}}, None, 0.01),
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05,
- 'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.005}}, 0.005),
+ 'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.005}}, None, 0.005),
# 0.1% filled - no costs (kraken - #3431)
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.0,
- 'fee': {'currency': 'BTC', 'cost': 0.0, 'rate': None}}, None),
+ 'fee': {'currency': 'BTC', 'cost': 0.0, 'rate': None}}, None, None),
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.0,
- 'fee': {'currency': 'ETH', 'cost': 0.0, 'rate': None}}, 0.0),
+ 'fee': {'currency': 'ETH', 'cost': 0.0, 'rate': None}}, None, 0.0),
({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.0,
- 'fee': {'currency': 'NEO', 'cost': 0.0, 'rate': None}}, None),
+ 'fee': {'currency': 'NEO', 'cost': 0.0, 'rate': None}}, None, None),
+ # Invalid pair combination - POINT/BTC is not a pair
+ ({'symbol': 'POINT/BTC', 'amount': 0.04, 'cost': 0.5,
+ 'fee': {'currency': 'POINT', 'cost': 2.0, 'rate': None}}, None, None),
+ ({'symbol': 'POINT/BTC', 'amount': 0.04, 'cost': 0.5,
+ 'fee': {'currency': 'POINT', 'cost': 2.0, 'rate': None}}, 1, 4.0),
+ ({'symbol': 'POINT/BTC', 'amount': 0.04, 'cost': 0.5,
+ 'fee': {'currency': 'POINT', 'cost': 2.0, 'rate': None}}, 2, 8.0),
])
-def test_calculate_fee_rate(mocker, default_conf, order, expected) -> None:
+def test_calculate_fee_rate(mocker, default_conf, order, expected, unknown_fee_rate) -> None:
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'last': 0.081})
+ if unknown_fee_rate:
+ default_conf['exchange']['unknown_fee_rate'] = unknown_fee_rate
ex = get_patched_exchange(mocker, default_conf)
+
assert ex.calculate_fee_rate(order) == expected