Merge pull request #2307 from freqtrade/rounding

Don't compare floats when updating fees
This commit is contained in:
hroff-1902 2019-09-28 12:39:27 +03:00 committed by GitHub
commit c31f118d0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 2 deletions

View File

@ -22,6 +22,7 @@ ORDERTYPE_POSSIBILITIES = ['limit', 'market']
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList']
DRY_RUN_WALLET = 999.9
MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons
TICKER_INTERVALS = [
'1m', '3m', '5m', '15m', '30m',

View File

@ -6,6 +6,7 @@ import copy
import logging
import traceback
from datetime import datetime
from math import isclose
from typing import Any, Dict, List, Optional, Tuple
import arrow
@ -510,7 +511,7 @@ class FreqtradeBot:
trade.pair.startswith(exectrade['fee']['currency'])):
fee_abs += exectrade['fee']['cost']
if amount != order_amount:
if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC):
logger.warning(f"Amount {amount} does not match amount {trade.amount}")
raise OperationalException("Half bought? Amounts don't match")
real_amount = amount - fee_abs
@ -535,7 +536,7 @@ class FreqtradeBot:
# Try update amount (binance-fix)
try:
new_amount = self.get_real_amount(trade, order)
if order['amount'] != new_amount:
if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC):
order['amount'] = new_amount
# Fee was applied, so set to 0
trade.fee_open = 0

View File

@ -4,6 +4,7 @@
import logging
import time
from copy import deepcopy
from math import isclose
from unittest.mock import MagicMock, PropertyMock
import arrow
@ -12,6 +13,7 @@ import requests
from freqtrade import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError, constants)
from freqtrade.constants import MATH_CLOSE_PREC
from freqtrade.data.dataprovider import DataProvider
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade
@ -1635,6 +1637,31 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_
assert trade.amount == limit_buy_order['amount']
def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_order,
limit_buy_order, mocker, caplog):
trades_for_order[0]['amount'] = limit_buy_order['amount'] + 1e-14
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
# get_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.get_order', MagicMock(side_effect=ValueError))
patch_exchange(mocker)
Trade.session = MagicMock()
amount = sum(x['amount'] for x in trades_for_order)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456",
is_open=True,
open_date=arrow.utcnow().datetime,
)
freqtrade.update_trade_state(trade, limit_buy_order)
assert trade.amount != amount
assert trade.amount == limit_buy_order['amount']
assert log_has_re(r'Applying fee on amount for .*', caplog)
def test_update_trade_state_exception(mocker, default_conf,
limit_buy_order, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
@ -3205,6 +3232,54 @@ def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount
def test_get_real_amount_wrong_amount(default_conf, trades_for_order, buy_order_fee, mocker):
limit_buy_order = deepcopy(buy_order_fee)
limit_buy_order['amount'] = limit_buy_order['amount'] - 0.001
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
amount = float(sum(x['amount'] for x in trades_for_order))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# Amount does not change
with pytest.raises(OperationalException, match=r"Half bought\? Amounts don't match"):
freqtrade.get_real_amount(trade, limit_buy_order)
def test_get_real_amount_wrong_amount_rounding(default_conf, trades_for_order, buy_order_fee,
mocker):
# Floats should not be compared directly.
limit_buy_order = deepcopy(buy_order_fee)
trades_for_order[0]['amount'] = trades_for_order[0]['amount'] + 1e-15
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
amount = float(sum(x['amount'] for x in trades_for_order))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# Amount changes by fee amount.
assert isclose(freqtrade.get_real_amount(trade, limit_buy_order), amount - (amount * 0.001),
abs_tol=MATH_CLOSE_PREC,)
def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee, mocker):
# Remove "Currency" from fee dict
trades_for_order[0]['fee'] = {'cost': 0.008}