Added funding_fee method headers to exchange, and implemented some of the methods
This commit is contained in:
parent
d559b6d6c6
commit
d54117990b
@ -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:
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,3 +1,2 @@
|
||||
# flake8: noqa: F401
|
||||
from freqtrade.leverage.funding_fees import funding_fee
|
||||
from freqtrade.leverage.interest import interest
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user