Changed funding fee implementation
This commit is contained in:
parent
194bb24a55
commit
b854350e8d
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -1 +1,2 @@
|
||||
# flake8: noqa: F401
|
||||
from freqtrade.leverage.interest import interest
|
||||
|
@ -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)
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user