diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 0c470cb24..bed07ca89 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -1,6 +1,6 @@ """ Binance exchange subclass """ import logging -from typing import Dict +from typing import Dict, Optional import ccxt @@ -89,3 +89,17 @@ 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 ecf3302d8..0040fa6b9 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1516,6 +1516,20 @@ 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 is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool: return exchange_name in ccxt_exchanges(ccxt_module) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 6cd549d60..77b864ac7 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 +from typing import Any, Dict, Optional import ccxt @@ -152,3 +152,18 @@ 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/leverage/funding_fee.py b/freqtrade/leverage/funding_fee.py index 738fa1344..209019075 100644 --- a/freqtrade/leverage/funding_fee.py +++ b/freqtrade/leverage/funding_fee.py @@ -3,6 +3,7 @@ from typing import List import schedule +from freqtrade.exchange import Exchange from freqtrade.persistence import Trade @@ -16,10 +17,14 @@ class FundingFee: "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() @@ -28,27 +33,30 @@ class FundingFee: else: # crosses midnight return check_time >= begin_time or check_time <= end_time - def _apply_funding_fees(self, num_of: int = 1): - if num_of == 0: - return + def _apply_current_funding_fees(self): + funding_rates = self.exchange.fetch_funding_rates() + for trade in self.trades: - trade.adjust_funding_fee(self._calculate(trade.amount) * num_of) + funding_rate = funding_rates[trade.pair] + self._apply_fee_to_trade(funding_rate, trade) - def _calculate(self, amount): - # TODO-futures: implement - # TODO-futures: Check how other exchages do it and adjust accordingly - # https://www.binance.com/en/support/faq/360033525031 - # mark_price = - # contract_size = maybe trade.amount - # funding_rate = # https://www.binance.com/en/futures/funding-history/0 - # nominal_value = mark_price * contract_size - # adjustment = nominal_value * funding_rate - # return adjustment + def _apply_fee_to_trade(self, funding_rate: dict, trade: Trade): - # FTX - paid in USD(always) - # position size * TWAP of((future - index) / index) / 24 - # https: // help.ftx.com/hc/en-us/articles/360027946571-Funding - return + 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