Merge pull request #6780 from samgermain/dry-taker-or-maker

Dry run taker or maker fees
This commit is contained in:
Matthias 2022-07-16 18:15:02 +02:00 committed by GitHub
commit 3bb4f2c7c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 74 additions and 9 deletions

View File

@ -542,3 +542,4 @@ TradeList = List[List]
LongShort = Literal['long', 'short']
EntryExit = Literal['entry', 'exit']
BuySell = Literal['buy', 'sell']
MakerTaker = Literal['maker', 'taker']

View File

@ -20,7 +20,7 @@ from ccxt import ROUND_DOWN, ROUND_UP, TICK_SIZE, TRUNCATE, Precise, decimal_to_
from pandas import DataFrame
from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BuySell,
EntryExit, ListPairsWithTimeframes, PairWithTimeframe)
EntryExit, ListPairsWithTimeframes, MakerTaker, PairWithTimeframe)
from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
@ -850,20 +850,27 @@ class Exchange:
'filled': _amount,
'cost': (dry_order['amount'] * average) / leverage
})
dry_order = self.add_dry_order_fee(pair, dry_order)
# market orders will always incurr taker fees
dry_order = self.add_dry_order_fee(pair, dry_order, 'taker')
dry_order = self.check_dry_limit_order_filled(dry_order)
dry_order = self.check_dry_limit_order_filled(dry_order, immediate=True)
self._dry_run_open_orders[dry_order["id"]] = dry_order
# Copy order and close it - so the returned order is open unless it's a market order
return dry_order
def add_dry_order_fee(self, pair: str, dry_order: Dict[str, Any]) -> Dict[str, Any]:
def add_dry_order_fee(
self,
pair: str,
dry_order: Dict[str, Any],
taker_or_maker: MakerTaker,
) -> Dict[str, Any]:
fee = self.get_fee(pair, taker_or_maker=taker_or_maker)
dry_order.update({
'fee': {
'currency': self.get_pair_quote_currency(pair),
'cost': dry_order['cost'] * self.get_fee(pair),
'rate': self.get_fee(pair)
'cost': dry_order['cost'] * fee,
'rate': fee
}
})
return dry_order
@ -929,7 +936,8 @@ class Exchange:
pass
return False
def check_dry_limit_order_filled(self, order: Dict[str, Any]) -> Dict[str, Any]:
def check_dry_limit_order_filled(
self, order: Dict[str, Any], immediate: bool = False) -> Dict[str, Any]:
"""
Check dry-run limit order fill and update fee (if it filled).
"""
@ -943,7 +951,12 @@ class Exchange:
'filled': order['amount'],
'remaining': 0,
})
self.add_dry_order_fee(pair, order)
self.add_dry_order_fee(
pair,
order,
'taker' if immediate else 'maker',
)
return order
@ -1601,7 +1614,7 @@ class Exchange:
@retrier
def get_fee(self, symbol: str, type: str = '', side: str = '', amount: float = 1,
price: float = 1, taker_or_maker: str = 'maker') -> float:
price: float = 1, taker_or_maker: MakerTaker = 'maker') -> float:
try:
if self._config['dry_run'] and self._config.get('fee', None) is not None:
return self._config['fee']

View File

@ -1138,6 +1138,57 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverag
assert order["cost"] == 1 * 200
@pytest.mark.parametrize('side,is_short,order_reason', [
("buy", False, "entry"),
("sell", False, "exit"),
("buy", True, "exit"),
("sell", True, "entry"),
])
@pytest.mark.parametrize("order_type,price_side,fee", [
("limit", "same", 1.0),
("limit", "other", 2.0),
("market", "same", 2.0),
("market", "other", 2.0),
])
def test_create_dry_run_order_fees(
default_conf,
mocker,
side,
order_type,
is_short,
order_reason,
price_side,
fee,
):
mocker.patch(
'freqtrade.exchange.Exchange.get_fee',
side_effect=lambda symbol, taker_or_maker: 2.0 if taker_or_maker == 'taker' else 1.0
)
mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled',
return_value=price_side == 'other')
exchange = get_patched_exchange(mocker, default_conf)
order = exchange.create_dry_run_order(
pair='LTC/USDT',
ordertype=order_type,
side=side,
amount=10,
rate=2.0,
leverage=1.0
)
if price_side == 'other' or order_type == 'market':
assert order['fee']['rate'] == fee
return
else:
assert order['fee'] is None
mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled',
return_value=price_side != 'other')
order1 = exchange.fetch_dry_run_order(order['id'])
assert order1['fee']['rate'] == fee
@pytest.mark.parametrize("side,startprice,endprice", [
("buy", 25.563, 25.566),
("sell", 25.566, 25.563)