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
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
from datetime import time
|
|
||||||
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
|
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
|
||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.exchange.common import retrier
|
from freqtrade.exchange.common import retrier
|
||||||
from freqtrade.utils import hours_to_time
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ class Binance(Exchange):
|
|||||||
"trades_pagination_arg": "fromId",
|
"trades_pagination_arg": "fromId",
|
||||||
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
|
"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:
|
def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -7,7 +7,7 @@ import http
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from datetime import datetime, time, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
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)
|
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
||||||
}
|
}
|
||||||
_ft_has: Dict = {}
|
_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:
|
def __init__(self, config: Dict[str, Any], validate: bool = True) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1555,6 +1555,21 @@ class Exchange:
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from 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(
|
def _get_funding_fee(
|
||||||
self,
|
self,
|
||||||
contract_size: float,
|
contract_size: float,
|
||||||
@ -1572,6 +1587,45 @@ class Exchange:
|
|||||||
"""
|
"""
|
||||||
raise OperationalException(f"Funding fee has not been implemented for {self.name}")
|
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:
|
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
|
||||||
return exchange_name in ccxt_exchanges(ccxt_module)
|
return exchange_name in ccxt_exchanges(ccxt_module)
|
||||||
|
@ -3,13 +3,13 @@ import logging
|
|||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
from datetime import time
|
|
||||||
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
|
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
|
||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.exchange.common import API_FETCH_ORDER_RETRY_COUNT, retrier
|
from freqtrade.exchange.common import API_FETCH_ORDER_RETRY_COUNT, retrier
|
||||||
from freqtrade.misc import safe_value_fallback2
|
from freqtrade.misc import safe_value_fallback2
|
||||||
from freqtrade.utils import hours_to_time
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class Ftx(Exchange):
|
|||||||
"stoploss_on_exchange": True,
|
"stoploss_on_exchange": True,
|
||||||
"ohlcv_candle_limit": 1500,
|
"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:
|
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -159,9 +159,7 @@ class Ftx(Exchange):
|
|||||||
contract_size: float,
|
contract_size: float,
|
||||||
mark_price: float,
|
mark_price: float,
|
||||||
funding_rate: Optional[float],
|
funding_rate: Optional[float],
|
||||||
# index_price: float,
|
) -> float:
|
||||||
# interest_rate: float)
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Calculates a single funding fee
|
Calculates a single funding fee
|
||||||
Always paid in USD on FTX # TODO: How do we account for this
|
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 mark_price: The price of the asset that the contract is based off of
|
||||||
:param funding_rate: Must be None on ftx
|
:param funding_rate: Must be None on ftx
|
||||||
"""
|
"""
|
||||||
(contract_size * mark_price) / 24
|
return (contract_size * mark_price) / 24
|
||||||
return
|
|
||||||
|
@ -3,12 +3,12 @@ import logging
|
|||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
from datetime import time
|
|
||||||
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
|
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
|
||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.exchange.common import retrier
|
from freqtrade.exchange.common import retrier
|
||||||
from freqtrade.utils import hours_to_time
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ class Kraken(Exchange):
|
|||||||
"trades_pagination": "id",
|
"trades_pagination": "id",
|
||||||
"trades_pagination_arg": "since",
|
"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:
|
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 copy
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import schedule
|
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from math import isclose
|
from math import isclose
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
|
import schedule
|
||||||
|
|
||||||
from freqtrade import __version__, constants
|
from freqtrade import __version__, constants
|
||||||
from freqtrade.configuration import validate_config_consistency
|
from freqtrade.configuration import validate_config_consistency
|
||||||
@ -251,7 +251,10 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
def update_funding_fees(self):
|
def update_funding_fees(self):
|
||||||
if self.trading_mode == TradingMode.FUTURES:
|
if self.trading_mode == TradingMode.FUTURES:
|
||||||
for trade in Trade.get_open_trades():
|
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
|
trade.funding_fees = funding_fees
|
||||||
|
|
||||||
def update_open_orders(self):
|
def update_open_orders(self):
|
||||||
@ -583,7 +586,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
||||||
open_date = datetime.utcnow()
|
open_date = datetime.utcnow()
|
||||||
if self.trading_mode == TradingMode.FUTURES:
|
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:
|
else:
|
||||||
funding_fees = 0.0
|
funding_fees = 0.0
|
||||||
|
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
# flake8: noqa: F401
|
# flake8: noqa: F401
|
||||||
from freqtrade.leverage.funding_fees import funding_fee
|
|
||||||
from freqtrade.leverage.interest import interest
|
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')
|
date_time = datetime.strptime("2021-09-01T00:00:01.000Z", '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||||
unix_time = int(date_time.strftime('%s'))
|
unix_time = int(date_time.strftime('%s'))
|
||||||
expected_fees = -0.001 # 0.14542341 + -0.14642341
|
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',
|
pair='XRP/USDT',
|
||||||
since=date_time
|
since=date_time
|
||||||
)
|
)
|
||||||
fees_from_unix_time = exchange.get_funding_fees(
|
fees_from_unix_time = exchange.get_funding_fees_from_exchange(
|
||||||
pair='XRP/USDT',
|
pair='XRP/USDT',
|
||||||
since=unix_time
|
since=unix_time
|
||||||
)
|
)
|
||||||
@ -2989,7 +2989,7 @@ def test_get_funding_fees(default_conf, mocker, exchange_name):
|
|||||||
default_conf,
|
default_conf,
|
||||||
api_mock,
|
api_mock,
|
||||||
exchange_name,
|
exchange_name,
|
||||||
"get_funding_fees",
|
"get_funding_fees_from_exchange",
|
||||||
"fetch_funding_history",
|
"fetch_funding_history",
|
||||||
pair="XRP/USDT",
|
pair="XRP/USDT",
|
||||||
since=unix_time
|
since=unix_time
|
||||||
|
@ -112,7 +112,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'interest_rate': 0.0,
|
'interest_rate': 0.0,
|
||||||
'isolated_liq': None,
|
'isolated_liq': None,
|
||||||
'is_short': False,
|
'is_short': False,
|
||||||
'funding_fees': None,
|
'funding_fees': 0.0,
|
||||||
'trading_mode': TradingMode.SPOT
|
'trading_mode': TradingMode.SPOT
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'interest_rate': 0.0,
|
'interest_rate': 0.0,
|
||||||
'isolated_liq': None,
|
'isolated_liq': None,
|
||||||
'is_short': False,
|
'is_short': False,
|
||||||
'funding_fees': None,
|
'funding_fees': 0.0,
|
||||||
'trading_mode': TradingMode.SPOT
|
'trading_mode': TradingMode.SPOT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user