From b854350e8d49e4b9bfd239c0a5e7ff612ac5076b Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 25 Aug 2021 22:09:32 -0600 Subject: [PATCH] Changed funding fee implementation --- freqtrade/exchange/binance.py | 16 +----- freqtrade/exchange/exchange.py | 29 +++++----- freqtrade/exchange/ftx.py | 17 +----- freqtrade/freqtradebot.py | 7 ++- freqtrade/leverage/__init__.py | 1 + freqtrade/leverage/funding_fee.py | 88 ------------------------------- freqtrade/optimize/backtesting.py | 2 +- tests/rpc/test_rpc.py | 16 ++---- 8 files changed, 30 insertions(+), 146 deletions(-) delete mode 100644 freqtrade/leverage/funding_fee.py diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index bed07ca89..0c470cb24 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -1,6 +1,6 @@ """ Binance exchange subclass """ import logging -from typing import Dict, Optional +from typing import Dict import ccxt @@ -89,17 +89,3 @@ class Binance(Exchange): f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: raise OperationalException(e) from e - - # https://www.binance.com/en/support/faq/360033525031 - def get_funding_fee( - self, - contract_size: float, - mark_price: float, - rate: Optional[float], - # index_price: float, - # interest_rate: float - ): - assert isinstance(rate, float) - nominal_value = mark_price * contract_size - adjustment = nominal_value * rate - return adjustment diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 0040fa6b9..168dcd575 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1516,19 +1516,22 @@ class Exchange: self._async_get_trade_history(pair=pair, since=since, until=until, from_id=from_id)) - def fetch_funding_rates(self): - return self._api.fetch_funding_rates() - - # https://www.binance.com/en/support/faq/360033525031 - def get_funding_fee( - self, - contract_size: float, - mark_price: float, - rate: Optional[float], - # index_price: float, - # interest_rate: float - ): - raise OperationalException(f"{self.name} has not implemented get_funding_rate") + def get_funding_fees(self, pair: str, since: datetime): + try: + funding_history = self._api.fetch_funding_history( + pair=pair, + since=since + ) + # TODO: sum all the funding fees in funding_history together + funding_fees = funding_history + return funding_fees + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + f'Could not get funding fees due to {e.__class__.__name__}. Message: {e}') from e + except ccxt.BaseError as e: + raise OperationalException(e) from e def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool: diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 77b864ac7..6cd549d60 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -1,6 +1,6 @@ """ FTX exchange subclass """ import logging -from typing import Any, Dict, Optional +from typing import Any, Dict import ccxt @@ -152,18 +152,3 @@ class Ftx(Exchange): if order['type'] == 'stop': return safe_value_fallback2(order, order, 'id_stop', 'id') return order['id'] - - # https://help.ftx.com/hc/en-us/articles/360027946571-Funding - def get_funding_fee( - self, - contract_size: float, - mark_price: float, - rate: Optional[float], - # index_price: float, - # interest_rate: float - ): - """ - Always paid in USD on FTX # TODO: How do we account for this - """ - (contract_size * mark_price) / 24 - return diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4659a634c..7b0a521bf 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -103,7 +103,7 @@ class FreqtradeBot(LoggingMixin): self._sell_lock = Lock() LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe)) - self.trading_mode = TradingMode.SPOT + self.trading_mode = self.config['trading_mode'] if self.trading_mode == TradingMode.FUTURES: self.funding_fee = FundingFee() self.funding_fee.start() @@ -243,6 +243,10 @@ class FreqtradeBot(LoggingMixin): open_trades = len(Trade.get_open_trades()) return max(0, self.config['max_open_trades'] - open_trades) + def get_funding_fees(): + if self.trading_mode == TradingMode.FUTURES: + return + def update_open_orders(self): """ Updates open orders based on order list kept in the database. @@ -258,7 +262,6 @@ class FreqtradeBot(LoggingMixin): try: fo = self.exchange.fetch_order_or_stoploss_order(order.order_id, order.ft_pair, order.ft_order_side == 'stoploss') - self.update_trade_state(order.trade, order.order_id, fo) except ExchangeError as e: diff --git a/freqtrade/leverage/__init__.py b/freqtrade/leverage/__init__.py index 9186b160e..ae78f4722 100644 --- a/freqtrade/leverage/__init__.py +++ b/freqtrade/leverage/__init__.py @@ -1 +1,2 @@ # flake8: noqa: F401 +from freqtrade.leverage.interest import interest diff --git a/freqtrade/leverage/funding_fee.py b/freqtrade/leverage/funding_fee.py deleted file mode 100644 index 209019075..000000000 --- a/freqtrade/leverage/funding_fee.py +++ /dev/null @@ -1,88 +0,0 @@ -from datetime import datetime, timedelta -from typing import List - -import schedule - -from freqtrade.exchange import Exchange -from freqtrade.persistence import Trade - - -class FundingFee: - - trades: List[Trade] - # Binance - begin_times = [ - # TODO-lev: Make these UTC time - "23:59:45", - "07:59:45", - "15:59:45", - ] - exchange: Exchange - - # FTX - # begin_times = every hour - - def __init__(self, exchange: Exchange): - self.exchange = exchange - - def _is_time_between(self, begin_time, end_time): - # If check time is not given, default to current UTC time - check_time = datetime.utcnow().time() - if begin_time < end_time: - return check_time >= begin_time and check_time <= end_time - else: # crosses midnight - return check_time >= begin_time or check_time <= end_time - - def _apply_current_funding_fees(self): - funding_rates = self.exchange.fetch_funding_rates() - - for trade in self.trades: - funding_rate = funding_rates[trade.pair] - self._apply_fee_to_trade(funding_rate, trade) - - def _apply_fee_to_trade(self, funding_rate: dict, trade: Trade): - - amount = trade.amount - mark_price = funding_rate['markPrice'] - rate = funding_rate['fundingRate'] - # index_price = funding_rate['indexPrice'] - # interest_rate = funding_rate['interestRate'] - - funding_fee = self.exchange.get_funding_fee( - amount, - mark_price, - rate, - # interest_rate - # index_price, - ) - - trade.adjust_funding_fee(funding_fee) - - def initial_funding_fee(self, amount) -> float: - # A funding fee interval is applied immediately if within 30s of an iterval - # May only exist on binance - for begin_string in self.begin_times: - begin_time = datetime.strptime(begin_string, "%H:%M:%S") - end_time = (begin_time + timedelta(seconds=30)) - if self._is_time_between(begin_time.time(), end_time.time()): - return self._calculate(amount) - return 0.0 - - def start(self): - for interval in self.begin_times: - schedule.every().day.at(interval).do(self._apply_funding_fees()) - - # https://stackoverflow.com/a/30393162/6331353 - # TODO-futures: Put schedule.run_pending() somewhere in the bot_loop - - def reboot(self): - # TODO-futures Find out how many begin_times have passed since last funding_fee added - amount_missed = 0 - self.apply_funding_fees(num_of=amount_missed) - self.start() - - def add_new_trade(self, trade): - self.trades.append(trade) - - def remove_trade(self, trade): - self.trades.remove(trade) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 99d4c60d0..084142646 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -386,7 +386,7 @@ class Backtesting: detail_data = detail_data.loc[ (detail_data['date'] >= sell_candle_time) & (detail_data['date'] < sell_candle_end) - ] + ] if len(detail_data) == 0: # Fall back to "regular" data if no detail data was found for this candle return self._get_sell_trade_entry_for_candle(trade, sell_row) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index d649581a6..56e64db69 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -8,7 +8,7 @@ import pytest from numpy import isnan from freqtrade.edge import PairInfo -from freqtrade.enums import State, TradingMode +from freqtrade.enums import State from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError from freqtrade.persistence import Trade from freqtrade.persistence.pairlock_middleware import PairLocks @@ -108,13 +108,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'stoploss_entry_dist_ratio': -0.10448878, 'open_order': None, 'exchange': 'binance', - 'trading_mode': TradingMode.SPOT, - 'isolated_liq': None, - 'is_short': False, 'leverage': 1.0, 'interest_rate': 0.0, - 'funding_fee': None, - 'last_funding_adjustment': None, + 'isolated_liq': None, + 'is_short': False, } mocker.patch('freqtrade.exchange.Exchange.get_rate', @@ -182,13 +179,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'stoploss_entry_dist_ratio': -0.10448878, 'open_order': None, 'exchange': 'binance', - 'trading_mode': TradingMode.SPOT, - 'isolated_liq': None, - 'is_short': False, 'leverage': 1.0, 'interest_rate': 0.0, - 'funding_fee': None, - 'last_funding_adjustment': None, + 'isolated_liq': None, + 'is_short': False, }