Merge pull request #7379 from freqtrade/fix/keyerror_fundingfee

Fix keyerror fundingfee update
This commit is contained in:
Matthias 2022-09-08 12:06:52 +02:00 committed by GitHub
commit 883abe5b4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 35 deletions

View File

@ -2509,8 +2509,13 @@ class Exchange:
cache=False, cache=False,
drop_incomplete=False, drop_incomplete=False,
) )
try:
# we can't assume we always get histories - for example during exchange downtimes
funding_rates = candle_histories[funding_comb] funding_rates = candle_histories[funding_comb]
mark_rates = candle_histories[mark_comb] mark_rates = candle_histories[mark_comb]
except KeyError:
raise ExchangeError("Could not find funding rates.") from None
funding_mark_rates = self.combine_funding_and_mark( funding_mark_rates = self.combine_funding_and_mark(
funding_rates=funding_rates, mark_rates=mark_rates) funding_rates=funding_rates, mark_rates=mark_rates)
@ -2590,6 +2595,8 @@ class Exchange:
:param is_short: trade direction :param is_short: trade direction
:param amount: Trade amount :param amount: Trade amount
:param open_date: Open date of the trade :param open_date: Open date of the trade
:return: funding fee since open_date
:raies: ExchangeError if something goes wrong.
""" """
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
if self._config['dry_run']: if self._config['dry_run']:

View File

@ -281,6 +281,7 @@ class FreqtradeBot(LoggingMixin):
def update_funding_fees(self): def update_funding_fees(self):
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
trades = Trade.get_open_trades() trades = Trade.get_open_trades()
try:
for trade in trades: for trade in trades:
funding_fees = self.exchange.get_funding_fees( funding_fees = self.exchange.get_funding_fees(
pair=trade.pair, pair=trade.pair,
@ -289,8 +290,8 @@ class FreqtradeBot(LoggingMixin):
open_date=trade.date_last_filled_utc open_date=trade.date_last_filled_utc
) )
trade.funding_fees = funding_fees trade.funding_fees = funding_fees
else: except ExchangeError:
return 0.0 logger.warning("Could not update funding fees for open trades.")
def startup_backpopulate_precision(self): def startup_backpopulate_precision(self):
@ -671,14 +672,12 @@ class FreqtradeBot(LoggingMixin):
if not stake_amount: if not stake_amount:
return False return False
if pos_adjust: msg = (f"Position adjust: about to create a new order for {pair} with stake: "
logger.info(f"Position adjust: about to create a new order for {pair} with stake: " f"{stake_amount} for {trade}" if pos_adjust
f"{stake_amount} for {trade}") else
else:
logger.info(
f"{name} signal found: about create a new trade for {pair} with stake_amount: " f"{name} signal found: about create a new trade for {pair} with stake_amount: "
f"{stake_amount} ...") f"{stake_amount} ...")
logger.info(msg)
amount = (stake_amount / enter_limit_requested) * leverage amount = (stake_amount / enter_limit_requested) * leverage
order_type = ordertype or self.strategy.order_types['entry'] order_type = ordertype or self.strategy.order_types['entry']
@ -741,8 +740,13 @@ class FreqtradeBot(LoggingMixin):
# This is a new trade # This is a new trade
if trade is None: if trade is None:
funding_fees = 0.0
try:
funding_fees = self.exchange.get_funding_fees( funding_fees = self.exchange.get_funding_fees(
pair=pair, amount=amount, is_short=is_short, open_date=open_date) pair=pair, amount=amount, is_short=is_short, open_date=open_date)
except ExchangeError:
logger.warning("Could not find funding fee.")
trade = Trade( trade = Trade(
pair=pair, pair=pair,
base_currency=base_currency, base_currency=base_currency,
@ -1493,12 +1497,16 @@ class FreqtradeBot(LoggingMixin):
:param exit_check: CheckTuple with signal and reason :param exit_check: CheckTuple with signal and reason
:return: True if it succeeds False :return: True if it succeeds False
""" """
try:
trade.funding_fees = self.exchange.get_funding_fees( trade.funding_fees = self.exchange.get_funding_fees(
pair=trade.pair, pair=trade.pair,
amount=trade.amount, amount=trade.amount,
is_short=trade.is_short, is_short=trade.is_short,
open_date=trade.date_last_filled_utc, open_date=trade.date_last_filled_utc,
) )
except ExchangeError:
logger.warning("Could not update funding fee.")
exit_type = 'exit' exit_type = 'exit'
exit_reason = exit_tag or exit_check.exit_reason exit_reason = exit_tag or exit_check.exit_reason
if exit_check.exit_type in ( if exit_check.exit_type in (

View File

@ -11,8 +11,9 @@ import pytest
from pandas import DataFrame from pandas import DataFrame
from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.enums import CandleType, MarginMode, TradingMode
from freqtrade.exceptions import (DDosProtection, DependencyException, InvalidOrderException, from freqtrade.exceptions import (DDosProtection, DependencyException, ExchangeError,
OperationalException, PricingError, TemporaryError) InvalidOrderException, OperationalException, PricingError,
TemporaryError)
from freqtrade.exchange import (Binance, Bittrex, Exchange, Kraken, amount_to_precision, from freqtrade.exchange import (Binance, Bittrex, Exchange, Kraken, amount_to_precision,
date_minus_candles, market_is_active, price_to_precision, date_minus_candles, market_is_active, price_to_precision,
timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date, timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date,
@ -4179,17 +4180,24 @@ def test__fetch_and_calculate_funding_fees(
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True}) type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) ex = get_patched_exchange(mocker, default_conf, api_mock, id=exchange)
mocker.patch('freqtrade.exchange.Exchange.timeframes', PropertyMock( mocker.patch('freqtrade.exchange.Exchange.timeframes', PropertyMock(
return_value=['1h', '4h', '8h'])) return_value=['1h', '4h', '8h']))
funding_fees = exchange._fetch_and_calculate_funding_fees( funding_fees = ex._fetch_and_calculate_funding_fees(
pair='ADA/USDT', amount=amount, is_short=True, open_date=d1, close_date=d2) pair='ADA/USDT', amount=amount, is_short=True, open_date=d1, close_date=d2)
assert pytest.approx(funding_fees) == expected_fees assert pytest.approx(funding_fees) == expected_fees
# Fees for Longs are inverted # Fees for Longs are inverted
funding_fees = exchange._fetch_and_calculate_funding_fees( funding_fees = ex._fetch_and_calculate_funding_fees(
pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2) pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2)
assert pytest.approx(funding_fees) == -expected_fees assert pytest.approx(funding_fees) == -expected_fees
# Return empty "refresh_latest"
mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", return_value={})
ex = get_patched_exchange(mocker, default_conf, api_mock, id=exchange)
with pytest.raises(ExchangeError, match="Could not find funding rates."):
ex._fetch_and_calculate_funding_fees(
pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2)
@pytest.mark.parametrize('exchange,expected_fees', [ @pytest.mark.parametrize('exchange,expected_fees', [
('binance', -0.0009140999999999999), ('binance', -0.0009140999999999999),

View File

@ -506,7 +506,7 @@ def test_create_trades_multiple_trades(
def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker,
limit_buy_order_usdt_open) -> None: limit_buy_order_usdt_open, caplog) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
default_conf_usdt['max_open_trades'] = 4 default_conf_usdt['max_open_trades'] = 4
@ -515,6 +515,7 @@ def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker,
fetch_ticker=ticker_usdt, fetch_ticker=ticker_usdt,
create_order=MagicMock(return_value=limit_buy_order_usdt_open), create_order=MagicMock(return_value=limit_buy_order_usdt_open),
get_fee=fee, get_fee=fee,
get_funding_fees=MagicMock(side_effect=ExchangeError()),
) )
freqtrade = FreqtradeBot(default_conf_usdt) freqtrade = FreqtradeBot(default_conf_usdt)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
@ -522,6 +523,7 @@ def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker,
# Create 2 existing trades # Create 2 existing trades
freqtrade.execute_entry('ETH/USDT', default_conf_usdt['stake_amount']) freqtrade.execute_entry('ETH/USDT', default_conf_usdt['stake_amount'])
freqtrade.execute_entry('NEO/BTC', default_conf_usdt['stake_amount']) freqtrade.execute_entry('NEO/BTC', default_conf_usdt['stake_amount'])
assert log_has("Could not find funding fee.", caplog)
assert len(Trade.get_open_trades()) == 2 assert len(Trade.get_open_trades()) == 2
# Change order_id for new orders # Change order_id for new orders
@ -3666,7 +3668,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(
(True, 29.70297029, 2.2, 2.3, -8.63762376, -0.1443212, 'loss'), (True, 29.70297029, 2.2, 2.3, -8.63762376, -0.1443212, 'loss'),
]) ])
def test_execute_trade_exit_market_order( def test_execute_trade_exit_market_order(
default_conf_usdt, ticker_usdt, fee, is_short, current_rate, amount, default_conf_usdt, ticker_usdt, fee, is_short, current_rate, amount, caplog,
limit, profit_amount, profit_ratio, profit_or_loss, ticker_usdt_sell_up, mocker limit, profit_amount, profit_ratio, profit_or_loss, ticker_usdt_sell_up, mocker
) -> None: ) -> None:
""" """
@ -3694,6 +3696,7 @@ def test_execute_trade_exit_market_order(
fetch_ticker=ticker_usdt, fetch_ticker=ticker_usdt,
get_fee=fee, get_fee=fee,
_is_dry_limit_order_filled=MagicMock(return_value=True), _is_dry_limit_order_filled=MagicMock(return_value=True),
get_funding_fees=MagicMock(side_effect=ExchangeError()),
) )
patch_whitelist(mocker, default_conf_usdt) patch_whitelist(mocker, default_conf_usdt)
freqtrade = FreqtradeBot(default_conf_usdt) freqtrade = FreqtradeBot(default_conf_usdt)
@ -3719,6 +3722,7 @@ def test_execute_trade_exit_market_order(
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'], limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
exit_check=ExitCheckTuple(exit_type=ExitType.ROI) exit_check=ExitCheckTuple(exit_type=ExitType.ROI)
) )
assert log_has("Could not update funding fee.", caplog)
assert not trade.is_open assert not trade.is_open
assert pytest.approx(trade.close_profit) == profit_ratio assert pytest.approx(trade.close_profit) == profit_ratio
@ -5430,6 +5434,16 @@ def test_update_funding_fees(
)) ))
def test_update_funding_fees_error(mocker, default_conf, caplog):
mocker.patch('freqtrade.exchange.Exchange.get_funding_fees', side_effect=ExchangeError())
default_conf['trading_mode'] = 'futures'
default_conf['margin_mode'] = 'isolated'
freqtrade = get_patched_freqtradebot(mocker, default_conf)
freqtrade.update_funding_fees()
log_has("Could not update funding fees for open trades.", caplog)
def test_position_adjust(mocker, default_conf_usdt, fee) -> None: def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)