Merge pull request #6780 from samgermain/dry-taker-or-maker
Dry run taker or maker fees
This commit is contained in:
commit
3bb4f2c7c2
@ -542,3 +542,4 @@ TradeList = List[List]
|
|||||||
LongShort = Literal['long', 'short']
|
LongShort = Literal['long', 'short']
|
||||||
EntryExit = Literal['entry', 'exit']
|
EntryExit = Literal['entry', 'exit']
|
||||||
BuySell = Literal['buy', 'sell']
|
BuySell = Literal['buy', 'sell']
|
||||||
|
MakerTaker = Literal['maker', 'taker']
|
||||||
|
@ -20,7 +20,7 @@ from ccxt import ROUND_DOWN, ROUND_UP, TICK_SIZE, TRUNCATE, Precise, decimal_to_
|
|||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BuySell,
|
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.data.converter import ohlcv_to_dataframe, trades_dict_to_list
|
||||||
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
|
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
|
||||||
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
|
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
|
||||||
@ -850,20 +850,27 @@ class Exchange:
|
|||||||
'filled': _amount,
|
'filled': _amount,
|
||||||
'cost': (dry_order['amount'] * average) / leverage
|
'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
|
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
|
# Copy order and close it - so the returned order is open unless it's a market order
|
||||||
return dry_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({
|
dry_order.update({
|
||||||
'fee': {
|
'fee': {
|
||||||
'currency': self.get_pair_quote_currency(pair),
|
'currency': self.get_pair_quote_currency(pair),
|
||||||
'cost': dry_order['cost'] * self.get_fee(pair),
|
'cost': dry_order['cost'] * fee,
|
||||||
'rate': self.get_fee(pair)
|
'rate': fee
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return dry_order
|
return dry_order
|
||||||
@ -929,7 +936,8 @@ class Exchange:
|
|||||||
pass
|
pass
|
||||||
return False
|
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).
|
Check dry-run limit order fill and update fee (if it filled).
|
||||||
"""
|
"""
|
||||||
@ -943,7 +951,12 @@ class Exchange:
|
|||||||
'filled': order['amount'],
|
'filled': order['amount'],
|
||||||
'remaining': 0,
|
'remaining': 0,
|
||||||
})
|
})
|
||||||
self.add_dry_order_fee(pair, order)
|
|
||||||
|
self.add_dry_order_fee(
|
||||||
|
pair,
|
||||||
|
order,
|
||||||
|
'taker' if immediate else 'maker',
|
||||||
|
)
|
||||||
|
|
||||||
return order
|
return order
|
||||||
|
|
||||||
@ -1601,7 +1614,7 @@ class Exchange:
|
|||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def get_fee(self, symbol: str, type: str = '', side: str = '', amount: float = 1,
|
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:
|
try:
|
||||||
if self._config['dry_run'] and self._config.get('fee', None) is not None:
|
if self._config['dry_run'] and self._config.get('fee', None) is not None:
|
||||||
return self._config['fee']
|
return self._config['fee']
|
||||||
|
@ -1138,6 +1138,57 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverag
|
|||||||
assert order["cost"] == 1 * 200
|
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", [
|
@pytest.mark.parametrize("side,startprice,endprice", [
|
||||||
("buy", 25.563, 25.566),
|
("buy", 25.563, 25.566),
|
||||||
("sell", 25.566, 25.563)
|
("sell", 25.566, 25.563)
|
||||||
|
Loading…
Reference in New Issue
Block a user