refactor get_real_amount

This commit is contained in:
Matthias Voppichler 2018-04-25 08:52:08 +02:00
parent f6ecd8e514
commit 9c2115c917
2 changed files with 83 additions and 34 deletions

View File

@ -367,16 +367,13 @@ class FreqtradeBot(object):
order = exchange.get_order(trade.open_order_id, trade.pair)
# Try update amount (binance-fix)
try:
# Only run for closed orders
if trade.fee_open != 0 and not (order['status'] == 'open'):
new_amount = self.get_real_amount(trade)
# This may break if a exchange applies no fee (which appears highly unlikely)
if order['amount'] != new_amount:
logger.info("Applying fee to amount for Trade {} from {} to {} ".format(
trade, order['amount'], new_amount))
order['amount'] = new_amount
# Fee was applied, so set to 0
trade.fee_open = 0
new_amount = self.get_real_amount(trade, order)
if order['amount'] != new_amount:
logger.info("Applying fee to amount for Trade {} from {} to {} ".format(
trade, order['amount'], new_amount))
order['amount'] = new_amount
# Fee was applied, so set to 0
trade.fee_open = 0
except OperationalException as exception:
logger.warning("could not update trade amount: %s", exception)
@ -388,30 +385,46 @@ class FreqtradeBot(object):
return self.handle_trade(trade)
return False
def get_real_amount(self, order: Trade) -> float:
def get_real_amount(self, trade: Trade, order: Dict) -> float:
"""
Get real amount for the trade
This is needed for exchanges which charge fees in target currency (e.g. binance)
Necessary for exchanges which charge fees in base currency (e.g. binance)
"""
order_amount = order['amount']
# Only run for closed orders
if trade.fee_open == 0 or not order['status'] == 'open':
return order_amount
trades = exchange.get_trades_for_order(
order.open_order_id, order.pair, order.open_date)
# use fee from order-dict if possible
if order['fee']:
if trade.pair.startswith(order['fee']['currency']):
new_amount = order_amount - order['fee']['cost']
logger.info("Applying fee on amount for %s (from %s to %s) from Order",
trade, order['amount'], new_amount)
return new_amount
# Fallback to Trades
trades = exchange.get_trades_for_order(trade.open_order_id, trade.pair, trade.open_date)
if len(trades) == 0:
raise OperationalException("get_real_amount: no trade found")
logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade)
return order_amount
amount = 0
fee_abs = 0
for trade in trades:
amount += trade["amount"]
if "fee" in trade:
for exectrade in trades:
amount += exectrade['amount']
if "fee" in exectrade:
# only applies if fee is in quote currency!
if order.pair.startswith(trade["fee"]["currency"]):
fee_abs += trade["fee"]["cost"]
if trade.pair.startswith(exectrade['fee']['currency']):
fee_abs += exectrade['fee']['cost']
if amount != order.amount:
logger.warning("amount {} does not match amount {}".format(amount, order.amount))
if amount != order_amount:
logger.warning("amount {} does not match amount {}".format(amount, trade.amount))
raise OperationalException("Half bought? Amounts don't match")
real_amount = amount - fee_abs
if fee_abs != 0:
logger.info("Applying fee on amount for {} (from {} to {}) from Trades".format(
trade, order['amount'], real_amount))
return real_amount
def handle_trade(self, trade: Trade) -> bool:

View File

@ -585,14 +585,12 @@ def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplo
trade.open_fee = 0.001
assert not freqtrade.process_maybe_execute_sell(trade)
# Test amount not modified by fee-logic
assert not log_has('Updating amount for Trade {} from 90.99181073 to 90.81'.format(trade),
caplog.record_tuples)
assert not log_has('Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(
trade), caplog.record_tuples)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
# test amount modified by fee-logic
assert not freqtrade.process_maybe_execute_sell(trade)
assert log_has('Updating amount for Trade {} from 90.99181073 to 90.81'.format(trade),
caplog.record_tuples)
trade.is_open = True
trade.open_order_id = None
@ -1319,7 +1317,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke
assert freqtrade.handle_trade(trade) is True
def test_get_real_amount_quote(default_conf, trades_for_order, mocker):
def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
"""
Test get_real_amount - fee in quote currency
"""
@ -1335,14 +1333,18 @@ def test_get_real_amount_quote(default_conf, trades_for_order, mocker):
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade) == amount - (amount * 0.001)
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades',
caplog.record_tuples)
def test_get_real_amount_stake(default_conf, trades_for_order, mocker):
def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
"""
Test get_real_amount - fees in Stake currency
"""
@ -1358,14 +1360,15 @@ def test_get_real_amount_stake(default_conf, trades_for_order, mocker):
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount does not change
assert freqtrade.get_real_amount(trade) == amount
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
def test_get_real_amount_BNB(default_conf, trades_for_order, mocker):
def test_get_real_amount_BNB(default_conf, trades_for_order, buy_order_fee, mocker):
"""
Test get_real_amount - Fees in BNB
"""
@ -1383,14 +1386,15 @@ def test_get_real_amount_BNB(default_conf, trades_for_order, mocker):
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount does not change
assert freqtrade.get_real_amount(trade) == amount
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
def test_get_real_amount_multi(default_conf, trades_for_order2, mocker):
def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, caplog, mocker):
"""
Test get_real_amount with split trades (multiple trades for this order)
"""
@ -1405,8 +1409,40 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, mocker):
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade) == amount - (amount * 0.001)
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades',
caplog.record_tuples)
def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
"""
Test get_real_amount with split trades (multiple trades for this order)
"""
limit_buy_order = deepcopy(buy_order_fee)
limit_buy_order['fee'] = {'cost': 0.004, 'currency': 'LTC'}
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch('freqtrade.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, create_engine('sqlite://'))
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996) from Order',
caplog.record_tuples)