Changed funding fee implementation

This commit is contained in:
Sam Germain
2021-08-25 22:09:32 -06:00
parent 194bb24a55
commit b854350e8d
8 changed files with 30 additions and 146 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -1 +1,2 @@
# flake8: noqa: F401
from freqtrade.leverage.interest import interest

View File

@@ -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)

View File

@@ -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)