Added funding_fee method headers to exchange, and implemented some of the methods

This commit is contained in:
Sam Germain 2021-09-09 01:19:24 -06:00
parent d559b6d6c6
commit d54117990b
8 changed files with 78 additions and 25 deletions

View File

@ -3,12 +3,12 @@ import logging
from typing import Dict, List, Optional
import ccxt
from datetime import time
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exchange import Exchange
from freqtrade.exchange.common import retrier
from freqtrade.utils import hours_to_time
logger = logging.getLogger(__name__)
@ -23,7 +23,7 @@ class Binance(Exchange):
"trades_pagination_arg": "fromId",
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
}
funding_fee_times: List[time] = hours_to_time([0, 8, 16])
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool:
"""

View File

@ -7,7 +7,7 @@ import http
import inspect
import logging
from copy import deepcopy
from datetime import datetime, time, timezone
from datetime import datetime, timedelta, timezone
from math import ceil
from typing import Any, Dict, List, Optional, Tuple, Union
@ -69,7 +69,7 @@ class Exchange:
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
}
_ft_has: Dict = {}
funding_fee_times: List[time] = []
funding_fee_times: List[int] = [] # hours of the day
def __init__(self, config: Dict[str, Any], validate: bool = True) -> None:
"""
@ -1555,6 +1555,21 @@ class Exchange:
except ccxt.BaseError as e:
raise OperationalException(e) from e
def get_mark_price(self, pair: str, when: datetime):
"""
Get's the value of the underlying asset for a futures contract
at a specific date and time in the past
"""
# TODO-lev: implement
raise OperationalException(f"get_mark_price has not been implemented for {self.name}")
def get_funding_rate(self, pair: str, when: datetime):
"""
Get's the funding_rate for a pair at a specific date and time in the past
"""
# TODO-lev: implement
raise OperationalException(f"get_funding_rate has not been implemented for {self.name}")
def _get_funding_fee(
self,
contract_size: float,
@ -1572,6 +1587,45 @@ class Exchange:
"""
raise OperationalException(f"Funding fee has not been implemented for {self.name}")
def get_funding_fee_dates(self, open_date: datetime, close_date: datetime):
"""
Get's the date and time of every funding fee that happened between two datetimes
"""
open_date = datetime(open_date.year, open_date.month, open_date.day, open_date.hour)
close_date = datetime(close_date.year, close_date.month, close_date.day, close_date.hour)
results = []
date_iterator = open_date
while date_iterator < close_date:
date_iterator += timedelta(hours=1)
if date_iterator.hour in self.funding_fee_times:
results.append(date_iterator)
return results
def calculate_funding_fees(
self,
pair: str,
amount: float,
open_date: datetime,
close_date: datetime
) -> float:
"""
calculates the sum of all funding fees that occurred for a pair during a futures trade
:param pair: The quote/base pair of the trade
:param amount: The quantity of the trade
:param open_date: The date and time that the trade started
:param close_date: The date and time that the trade ended
"""
fees: float = 0
for date in self.get_funding_fee_dates(open_date, close_date):
funding_rate = self.get_funding_rate(pair, date)
mark_price = self.get_mark_price(pair, date)
fees += self._get_funding_fee(amount, mark_price, funding_rate)
return fees
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
return exchange_name in ccxt_exchanges(ccxt_module)

View File

@ -3,13 +3,13 @@ import logging
from typing import Any, Dict, List, Optional
import ccxt
from datetime import time
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exchange import Exchange
from freqtrade.exchange.common import API_FETCH_ORDER_RETRY_COUNT, retrier
from freqtrade.misc import safe_value_fallback2
from freqtrade.utils import hours_to_time
logger = logging.getLogger(__name__)
@ -20,7 +20,7 @@ class Ftx(Exchange):
"stoploss_on_exchange": True,
"ohlcv_candle_limit": 1500,
}
funding_fee_times: List[time] = hours_to_time(list(range(0, 23)))
funding_fee_times: List[int] = list(range(0, 23))
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
"""
@ -159,9 +159,7 @@ class Ftx(Exchange):
contract_size: float,
mark_price: float,
funding_rate: Optional[float],
# index_price: float,
# interest_rate: float)
):
) -> float:
"""
Calculates a single funding fee
Always paid in USD on FTX # TODO: How do we account for this
@ -169,5 +167,4 @@ class Ftx(Exchange):
:param mark_price: The price of the asset that the contract is based off of
:param funding_rate: Must be None on ftx
"""
(contract_size * mark_price) / 24
return
return (contract_size * mark_price) / 24

View File

@ -3,12 +3,12 @@ import logging
from typing import Any, Dict, List
import ccxt
from datetime import time
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exchange import Exchange
from freqtrade.exchange.common import retrier
from freqtrade.utils import hours_to_time
logger = logging.getLogger(__name__)
@ -22,7 +22,7 @@ class Kraken(Exchange):
"trades_pagination": "id",
"trades_pagination_arg": "since",
}
funding_fee_times: List[time] = hours_to_time([0, 4, 8, 12, 16, 20])
funding_fee_times: List[int] = [0, 4, 8, 12, 16, 20] # hours of the day
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
"""

View File

@ -4,13 +4,13 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade()
import copy
import logging
import traceback
import schedule
from datetime import datetime, timezone
from math import isclose
from threading import Lock
from typing import Any, Dict, List, Optional
import arrow
import schedule
from freqtrade import __version__, constants
from freqtrade.configuration import validate_config_consistency
@ -251,7 +251,10 @@ class FreqtradeBot(LoggingMixin):
def update_funding_fees(self):
if self.trading_mode == TradingMode.FUTURES:
for trade in Trade.get_open_trades():
funding_fees = self.exchange.get_funding_fees(trade.pair, trade.open_date)
funding_fees = self.exchange.get_funding_fees_from_exchange(
trade.pair,
trade.open_date
)
trade.funding_fees = funding_fees
def update_open_orders(self):
@ -583,7 +586,7 @@ class FreqtradeBot(LoggingMixin):
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
open_date = datetime.utcnow()
if self.trading_mode == TradingMode.FUTURES:
funding_fees = self.exchange.get_funding_fees(pair, open_date)
funding_fees = self.exchange.get_funding_fees_from_exchange(pair, open_date)
else:
funding_fees = 0.0

View File

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

View File

@ -2972,11 +2972,11 @@ def test_get_funding_fees(default_conf, mocker, exchange_name):
date_time = datetime.strptime("2021-09-01T00:00:01.000Z", '%Y-%m-%dT%H:%M:%S.%fZ')
unix_time = int(date_time.strftime('%s'))
expected_fees = -0.001 # 0.14542341 + -0.14642341
fees_from_datetime = exchange.get_funding_fees(
fees_from_datetime = exchange.get_funding_fees_from_exchange(
pair='XRP/USDT',
since=date_time
)
fees_from_unix_time = exchange.get_funding_fees(
fees_from_unix_time = exchange.get_funding_fees_from_exchange(
pair='XRP/USDT',
since=unix_time
)
@ -2989,7 +2989,7 @@ def test_get_funding_fees(default_conf, mocker, exchange_name):
default_conf,
api_mock,
exchange_name,
"get_funding_fees",
"get_funding_fees_from_exchange",
"fetch_funding_history",
pair="XRP/USDT",
since=unix_time

View File

@ -112,7 +112,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'interest_rate': 0.0,
'isolated_liq': None,
'is_short': False,
'funding_fees': None,
'funding_fees': 0.0,
'trading_mode': TradingMode.SPOT
}
@ -185,7 +185,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'interest_rate': 0.0,
'isolated_liq': None,
'is_short': False,
'funding_fees': None,
'funding_fees': 0.0,
'trading_mode': TradingMode.SPOT
}