Added tests and docstring to exchange funding_fee methods, removed utils
This commit is contained in:
parent
d54117990b
commit
dfb9937436
@ -1,5 +1,6 @@
|
||||
""" Binance exchange subclass """
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import ccxt
|
||||
@ -91,6 +92,13 @@ class Binance(Exchange):
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
def _get_funding_rate(self, pair: str, when: datetime) -> Optional[float]:
|
||||
"""
|
||||
Get's the funding_rate for a pair at a specific date and time in the past
|
||||
"""
|
||||
# TODO-lev: implement
|
||||
raise OperationalException("_get_funding_rate has not been implement on binance")
|
||||
|
||||
def _get_funding_fee(
|
||||
self,
|
||||
contract_size: float,
|
||||
|
@ -1555,7 +1555,7 @@ class Exchange:
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
def get_mark_price(self, pair: str, when: datetime):
|
||||
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
|
||||
@ -1563,7 +1563,7 @@ class Exchange:
|
||||
# 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):
|
||||
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
|
||||
"""
|
||||
@ -1587,7 +1587,7 @@ 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):
|
||||
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
|
||||
"""
|
||||
@ -1619,9 +1619,9 @@ class Exchange:
|
||||
"""
|
||||
|
||||
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)
|
||||
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
|
||||
|
@ -3,7 +3,7 @@ import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import ccxt
|
||||
|
||||
from datetime import datetime
|
||||
from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
|
||||
OperationalException, TemporaryError)
|
||||
from freqtrade.exchange import Exchange
|
||||
@ -154,6 +154,10 @@ class Ftx(Exchange):
|
||||
return safe_value_fallback2(order, order, 'id_stop', 'id')
|
||||
return order['id']
|
||||
|
||||
def _get_funding_rate(self, pair: str, when: datetime) -> Optional[float]:
|
||||
"""FTX doesn't use this"""
|
||||
return None
|
||||
|
||||
def _get_funding_fee(
|
||||
self,
|
||||
contract_size: float,
|
||||
@ -162,9 +166,9 @@ class Ftx(Exchange):
|
||||
) -> float:
|
||||
"""
|
||||
Calculates a single funding fee
|
||||
Always paid in USD on FTX # TODO: How do we account for this
|
||||
:param contract_size: The amount/quanity
|
||||
:param mark_price: The price of the asset that the contract is based off of
|
||||
:param funding_rate: Must be None on ftx
|
||||
Always paid in USD on FTX # TODO: How do we account for this
|
||||
: param contract_size: The amount/quanity
|
||||
: param mark_price: The price of the asset that the contract is based off of
|
||||
: param funding_rate: Must be None on ftx
|
||||
"""
|
||||
return (contract_size * mark_price) / 24
|
||||
|
75
freqtrade/leverage/funding_fees.py
Normal file
75
freqtrade/leverage/funding_fees.py
Normal file
@ -0,0 +1,75 @@
|
||||
from datetime import datetime, time
|
||||
from typing import Optional
|
||||
|
||||
from freqtrade.exceptions import OperationalException
|
||||
|
||||
|
||||
def funding_fees(
|
||||
exchange_name: str,
|
||||
pair: str,
|
||||
contract_size: float,
|
||||
open_date: datetime,
|
||||
close_date: datetime,
|
||||
funding_times: [time]
|
||||
# index_price: float,
|
||||
# interest_rate: float
|
||||
):
|
||||
"""
|
||||
Equation to calculate funding_fees on futures trades
|
||||
|
||||
:param exchange_name: The exchanged being trading on
|
||||
:param borrowed: The amount of currency being borrowed
|
||||
:param rate: The rate of interest
|
||||
:param hours: The time in hours that the currency has been borrowed for
|
||||
|
||||
Raises:
|
||||
OperationalException: Raised if freqtrade does
|
||||
not support margin trading for this exchange
|
||||
|
||||
Returns: The amount of interest owed (currency matches borrowed)
|
||||
"""
|
||||
exchange_name = exchange_name.lower()
|
||||
# fees = 0
|
||||
if exchange_name == "binance":
|
||||
for timeslot in funding_times:
|
||||
# for each day in close_date - open_date
|
||||
# mark_price = mark_price at this time
|
||||
# rate = rate at this time
|
||||
# fees = fees + funding_fee(exchange_name, contract_size, mark_price, rate)
|
||||
# return fees
|
||||
return
|
||||
elif exchange_name == "kraken":
|
||||
raise OperationalException("Funding_fees has not been implemented for Kraken")
|
||||
elif exchange_name == "ftx":
|
||||
# for timeslot in every hour since open_date:
|
||||
# mark_price = mark_price at this time
|
||||
# fees = fees + funding_fee(exchange_name, contract_size, mark_price, rate)
|
||||
return
|
||||
else:
|
||||
raise OperationalException(f"Leverage not available on {exchange_name} with freqtrade")
|
||||
|
||||
|
||||
def funding_fee(
|
||||
exchange_name: str,
|
||||
contract_size: float,
|
||||
mark_price: float,
|
||||
rate: Optional[float],
|
||||
# index_price: float,
|
||||
# interest_rate: float
|
||||
):
|
||||
"""
|
||||
Calculates a single funding fee
|
||||
"""
|
||||
if exchange_name == "binance":
|
||||
assert isinstance(rate, float)
|
||||
nominal_value = mark_price * contract_size
|
||||
adjustment = nominal_value * rate
|
||||
return adjustment
|
||||
elif exchange_name == "kraken":
|
||||
raise OperationalException("Funding fee has not been implemented for kraken")
|
||||
elif exchange_name == "ftx":
|
||||
"""
|
||||
Always paid in USD on FTX # TODO: How do we account for this
|
||||
"""
|
||||
(contract_size * mark_price) / 24
|
||||
return
|
@ -1,2 +0,0 @@
|
||||
# flake8: noqa: F401
|
||||
from freqtrade.utils.hours_to_time import hours_to_time
|
@ -1,11 +0,0 @@
|
||||
from datetime import datetime, time
|
||||
from typing import List
|
||||
|
||||
|
||||
def hours_to_time(hours: List[int]) -> List[time]:
|
||||
'''
|
||||
:param hours: a list of hours as a time of day (e.g. [1, 16] is 01:00 and 16:00 o'clock)
|
||||
:return: a list of datetime time objects that correspond to the hours in hours
|
||||
'''
|
||||
# TODO-lev: These must be utc time
|
||||
return [datetime.strptime(str(t), '%H').time() for t in hours]
|
@ -105,3 +105,11 @@ def test_stoploss_adjust_binance(mocker, default_conf):
|
||||
# Test with invalid order case
|
||||
order['type'] = 'stop_loss'
|
||||
assert not exchange.stoploss_adjust(1501, order)
|
||||
|
||||
|
||||
def test_get_funding_rate():
|
||||
return
|
||||
|
||||
|
||||
def test__get_funding_fee():
|
||||
return
|
||||
|
@ -2994,3 +2994,15 @@ def test_get_funding_fees(default_conf, mocker, exchange_name):
|
||||
pair="XRP/USDT",
|
||||
since=unix_time
|
||||
)
|
||||
|
||||
|
||||
def test_get_mark_price():
|
||||
return
|
||||
|
||||
|
||||
def test_get_funding_fee_dates():
|
||||
return
|
||||
|
||||
|
||||
def test_calculate_funding_fees():
|
||||
return
|
||||
|
@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timedelta
|
||||
from random import randint
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
@ -191,3 +192,18 @@ def test_get_order_id(mocker, default_conf):
|
||||
}
|
||||
}
|
||||
assert exchange.get_order_id_conditional(order) == '1111'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pair,when", [
|
||||
('XRP/USDT', datetime.utcnow()),
|
||||
('ADA/BTC', datetime.utcnow()),
|
||||
('XRP/USDT', datetime.utcnow() - timedelta(hours=30)),
|
||||
])
|
||||
def test__get_funding_rate(default_conf, mocker, pair, when):
|
||||
api_mock = MagicMock()
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="ftx")
|
||||
assert exchange._get_funding_rate(pair, when) is None
|
||||
|
||||
|
||||
def test__get_funding_fee():
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user