Merge pull request #7379 from freqtrade/fix/keyerror_fundingfee
Fix keyerror fundingfee update
This commit is contained in:
commit
883abe5b4f
@ -2509,8 +2509,13 @@ class Exchange:
|
|||||||
cache=False,
|
cache=False,
|
||||||
drop_incomplete=False,
|
drop_incomplete=False,
|
||||||
)
|
)
|
||||||
funding_rates = candle_histories[funding_comb]
|
try:
|
||||||
mark_rates = candle_histories[mark_comb]
|
# we can't assume we always get histories - for example during exchange downtimes
|
||||||
|
funding_rates = candle_histories[funding_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']:
|
||||||
|
@ -281,16 +281,17 @@ 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()
|
||||||
for trade in trades:
|
try:
|
||||||
funding_fees = self.exchange.get_funding_fees(
|
for trade in trades:
|
||||||
pair=trade.pair,
|
funding_fees = self.exchange.get_funding_fees(
|
||||||
amount=trade.amount,
|
pair=trade.pair,
|
||||||
is_short=trade.is_short,
|
amount=trade.amount,
|
||||||
open_date=trade.date_last_filled_utc
|
is_short=trade.is_short,
|
||||||
)
|
open_date=trade.date_last_filled_utc
|
||||||
trade.funding_fees = funding_fees
|
)
|
||||||
else:
|
trade.funding_fees = funding_fees
|
||||||
return 0.0
|
except ExchangeError:
|
||||||
|
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:
|
f"{name} signal found: about create a new trade for {pair} with stake_amount: "
|
||||||
logger.info(
|
f"{stake_amount} ...")
|
||||||
f"{name} signal found: about create a new trade for {pair} with stake_amount: "
|
logger.info(msg)
|
||||||
f"{stake_amount} ...")
|
|
||||||
|
|
||||||
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 = self.exchange.get_funding_fees(
|
funding_fees = 0.0
|
||||||
pair=pair, amount=amount, is_short=is_short, open_date=open_date)
|
try:
|
||||||
|
funding_fees = self.exchange.get_funding_fees(
|
||||||
|
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
|
||||||
"""
|
"""
|
||||||
trade.funding_fees = self.exchange.get_funding_fees(
|
try:
|
||||||
pair=trade.pair,
|
trade.funding_fees = self.exchange.get_funding_fees(
|
||||||
amount=trade.amount,
|
pair=trade.pair,
|
||||||
is_short=trade.is_short,
|
amount=trade.amount,
|
||||||
open_date=trade.date_last_filled_utc,
|
is_short=trade.is_short,
|
||||||
)
|
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 (
|
||||||
|
@ -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),
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user