Update _calculate_funding_fees to reuse existing async infrastructure

This commit is contained in:
Matthias 2021-12-10 19:50:58 +01:00
parent 35f9549e98
commit aabca85a5f
3 changed files with 56 additions and 37 deletions

View File

@ -1498,11 +1498,17 @@ class Exchange:
params = deepcopy(self._ft_has.get('ohlcv_params', {})) params = deepcopy(self._ft_has.get('ohlcv_params', {}))
if candle_type != CandleType.SPOT: if candle_type != CandleType.SPOT:
params.update({'price': candle_type}) params.update({'price': candle_type})
data = await self._api_async.fetch_ohlcv(pair, timeframe=timeframe, if candle_type != CandleType.FUNDING_RATE:
since=since_ms, data = await self._api_async.fetch_ohlcv(
limit=self.ohlcv_candle_limit(timeframe), pair, timeframe=timeframe, since=since_ms,
params=params) limit=self.ohlcv_candle_limit(timeframe), params=params)
else:
# Funding rate
data = await self._api_async.fetch_funding_rate_history(
pair, since=since_ms,
limit=self.ohlcv_candle_limit(timeframe))
# Convert funding rate to candle pattern
data = [[x['timestamp'], x['fundingRate'], 0, 0, 0, 0] for x in data]
# Some exchanges sort OHLCV in ASC order and others in DESC. # Some exchanges sort OHLCV in ASC order and others in DESC.
# Ex: Bittrex returns the list of OHLCV in ASC order (oldest first, newest last) # Ex: Bittrex returns the list of OHLCV in ASC order (oldest first, newest last)
# while GDAX returns the list of OHLCV in DESC order (newest first, oldest last) # while GDAX returns the list of OHLCV in DESC order (newest first, oldest last)
@ -1882,28 +1888,23 @@ class Exchange:
close_date = datetime.now(timezone.utc) close_date = datetime.now(timezone.utc)
open_timestamp = int(open_date.timestamp()) * 1000 open_timestamp = int(open_date.timestamp()) * 1000
# close_timestamp = int(close_date.timestamp()) * 1000 # close_timestamp = int(close_date.timestamp()) * 1000
funding_rate_history = self.get_funding_rate_history(
pair, mark_comb: PairWithTimeframe = (
open_timestamp pair, '1h', CandleType.from_string(self._ft_has["mark_ohlcv_price"]))
) # TODO-lev: funding_rate downloading this way is not yet possible.
mark_price_history = self._get_mark_price_history( funding_comb: PairWithTimeframe = (pair, '1h', CandleType.FUNDING_RATE)
pair, candle_histories = self.refresh_latest_ohlcv(
open_timestamp [mark_comb, funding_comb],
) since_ms=open_timestamp,
for timestamp in funding_rate_history.keys(): cache=False,
funding_rate = funding_rate_history[timestamp] drop_incomplete=False,
if timestamp in mark_price_history:
mark_price = mark_price_history[timestamp]
fees += self._get_funding_fee(
size=amount,
mark_price=mark_price,
funding_rate=funding_rate
)
else:
logger.warning(
f"Mark price for {pair} at timestamp {timestamp} not found in "
f"funding_rate_history Funding fee calculation may be incorrect"
) )
funding_rates = candle_histories[funding_comb]
mark_rates = candle_histories[mark_comb]
df = funding_rates.merge(mark_rates, on='date', how="inner", suffixes=["_fund", "_mark"])
# TODO-lev: filter for relevant timeperiod?
fees = sum(df['open_fund'] * df['open_mark'] * amount)
return fees return fees

View File

@ -195,7 +195,6 @@ class TestCCXTExchange():
assert exchange.klines(pair_tf).iloc[-1]['date'] >= timeframe_to_prev_date(timeframe, now) assert exchange.klines(pair_tf).iloc[-1]['date'] >= timeframe_to_prev_date(timeframe, now)
def test_ccxt_fetch_funding_rate_history(self, exchange_futures): def test_ccxt_fetch_funding_rate_history(self, exchange_futures):
# TODO-lev: enable this test once Futures mode is enabled.
exchange, exchangename = exchange_futures exchange, exchangename = exchange_futures
if not exchange: if not exchange:
# exchange_futures only returns values for supported exchanges # exchange_futures only returns values for supported exchanges
@ -203,15 +202,21 @@ class TestCCXTExchange():
pair = EXCHANGES[exchangename].get('futures_pair', EXCHANGES[exchangename]['pair']) pair = EXCHANGES[exchangename].get('futures_pair', EXCHANGES[exchangename]['pair'])
since = int((datetime.now(timezone.utc) - timedelta(days=5)).timestamp() * 1000) since = int((datetime.now(timezone.utc) - timedelta(days=5)).timestamp() * 1000)
pair_tf = (pair, '1h', CandleType.FUNDING_RATE)
rate = exchange.get_funding_rate_history(pair, since) funding_ohlcv = exchange.refresh_latest_ohlcv(
assert isinstance(rate, dict) [pair_tf],
since_ms=since,
drop_incomplete=False)
assert isinstance(funding_ohlcv, dict)
rate = funding_ohlcv[pair_tf]
expected_tf = exchange._ft_has['mark_ohlcv_timeframe'] expected_tf = exchange._ft_has['mark_ohlcv_timeframe']
this_hour = timeframe_to_prev_date(expected_tf) this_hour = timeframe_to_prev_date(expected_tf)
prev_tick = timeframe_to_prev_date(expected_tf, this_hour - timedelta(minutes=1)) prev_hour = timeframe_to_prev_date(expected_tf, this_hour - timedelta(minutes=1))
assert rate[int(this_hour.timestamp() * 1000)] != 0.0 assert rate[rate['date'] == this_hour].iloc[0]['open'] != 0.0
assert rate[int(prev_tick.timestamp() * 1000)] != 0.0 assert rate[rate['date'] == prev_hour].iloc[0]['open'] != 0.0
def test_ccxt_fetch_mark_price_history(self, exchange_futures): def test_ccxt_fetch_mark_price_history(self, exchange_futures):
exchange, exchangename = exchange_futures exchange, exchangename = exchange_futures
@ -237,6 +242,19 @@ class TestCCXTExchange():
assert mark_candles[mark_candles['date'] == prev_hour].iloc[0]['open'] != 0.0 assert mark_candles[mark_candles['date'] == prev_hour].iloc[0]['open'] != 0.0
assert mark_candles[mark_candles['date'] == this_hour].iloc[0]['open'] != 0.0 assert mark_candles[mark_candles['date'] == this_hour].iloc[0]['open'] != 0.0
def test_ccxt__calculate_funding_fees(self, exchange_futures):
exchange, exchangename = exchange_futures
if not exchange:
# exchange_futures only returns values for supported exchanges
return
pair = EXCHANGES[exchangename].get('futures_pair', EXCHANGES[exchangename]['pair'])
since = datetime.now(timezone.utc) - timedelta(days=5)
funding_fee = exchange._calculate_funding_fees(pair, 20, open_date=since)
assert isinstance(funding_fee, float)
# assert funding_fee > 0
# TODO: tests fetch_trades (?) # TODO: tests fetch_trades (?)
def test_ccxt_get_fee(self, exchange): def test_ccxt_get_fee(self, exchange):

View File

@ -3566,14 +3566,14 @@ def test__calculate_funding_fees(
'gateio': funding_rate_history_octohourly, 'gateio': funding_rate_history_octohourly,
}[exchange][rate_start:rate_end] }[exchange][rate_start:rate_end]
api_mock = MagicMock() api_mock = MagicMock()
api_mock.fetch_funding_rate_history = MagicMock(return_value=funding_rate_history) api_mock.fetch_funding_rate_history = get_mock_coro(return_value=funding_rate_history)
api_mock.fetch_ohlcv = MagicMock(return_value=mark_ohlcv) api_mock.fetch_ohlcv = get_mock_coro(return_value=mark_ohlcv)
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True}) type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange)
funding_fees = exchange._calculate_funding_fees('ADA/USDT', amount, d1, d2) funding_fees = exchange._calculate_funding_fees('ADA/USDT', amount, d1, d2)
assert funding_fees == expected_fees assert pytest.approx(funding_fees, expected_fees)
@ pytest.mark.parametrize('exchange,expected_fees', [ @ pytest.mark.parametrize('exchange,expected_fees', [
@ -3590,8 +3590,8 @@ def test__calculate_funding_fees_datetime_called(
expected_fees expected_fees
): ):
api_mock = MagicMock() api_mock = MagicMock()
api_mock.fetch_ohlcv = MagicMock(return_value=mark_ohlcv) api_mock.fetch_ohlcv = get_mock_coro(return_value=mark_ohlcv)
api_mock.fetch_funding_rate_history = MagicMock(return_value=funding_rate_history_octohourly) api_mock.fetch_funding_rate_history = get_mock_coro(return_value=funding_rate_history_octohourly)
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True}) type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True})