Okx - conditional candle-length
This commit is contained in:
parent
64668b11da
commit
111b04c9e6
@ -309,12 +309,15 @@ class Exchange:
|
||||
if self.log_responses:
|
||||
logger.info(f"API {endpoint}: {response}")
|
||||
|
||||
def ohlcv_candle_limit(self, timeframe: str) -> int:
|
||||
def ohlcv_candle_limit(
|
||||
self, timeframe: str, candle_type: CandleType, since_ms: Optional[int]) -> int:
|
||||
"""
|
||||
Exchange ohlcv candle limit
|
||||
Uses ohlcv_candle_limit_per_timeframe if the exchange has different limits
|
||||
per timeframe (e.g. bittrex), otherwise falls back to ohlcv_candle_limit
|
||||
:param timeframe: Timeframe to check
|
||||
:param candle_type: Candle-type
|
||||
:param since_ms: Candle-type
|
||||
:return: Candle limit as integer
|
||||
"""
|
||||
return int(self._ft_has.get('ohlcv_candle_limit_per_timeframe', {}).get(
|
||||
@ -616,7 +619,7 @@ class Exchange:
|
||||
Checks if required startup_candles is more than ohlcv_candle_limit().
|
||||
Requires a grace-period of 5 candles - so a startup-period up to 494 is allowed by default.
|
||||
"""
|
||||
candle_limit = self.ohlcv_candle_limit(timeframe)
|
||||
candle_limit = self.ohlcv_candle_limit(timeframe, self._config['candle_type_def'], None)
|
||||
# Require one more candle - to account for the still open candle.
|
||||
candle_count = startup_candles + 1
|
||||
# Allow 5 calls to the exchange per pair
|
||||
@ -1708,7 +1711,8 @@ class Exchange:
|
||||
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
||||
"""
|
||||
|
||||
one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe)
|
||||
one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(
|
||||
timeframe, candle_type, since_ms)
|
||||
logger.debug(
|
||||
"one_call: %s msecs (%s)",
|
||||
one_call,
|
||||
@ -1744,7 +1748,8 @@ class Exchange:
|
||||
if (not since_ms
|
||||
and (self._ft_has["ohlcv_require_since"] or self.required_candle_call_count > 1)):
|
||||
# Multiple calls for one pair - to get more history
|
||||
one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe)
|
||||
one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(
|
||||
timeframe, candle_type, since_ms)
|
||||
move_to = one_call * self.required_candle_call_count
|
||||
now = timeframe_to_next_date(timeframe)
|
||||
since_ms = int((now - timedelta(seconds=move_to // 1000)).timestamp() * 1000)
|
||||
@ -1862,7 +1867,9 @@ class Exchange:
|
||||
pair, timeframe, since_ms, s
|
||||
)
|
||||
params = deepcopy(self._ft_has.get('ohlcv_params', {}))
|
||||
candle_limit = self.ohlcv_candle_limit(timeframe)
|
||||
candle_limit = self.ohlcv_candle_limit(
|
||||
timeframe, candle_type=candle_type, since_ms=since_ms)
|
||||
|
||||
if candle_type != CandleType.SPOT:
|
||||
params.update({'price': candle_type})
|
||||
if candle_type != CandleType.FUNDING_RATE:
|
||||
|
@ -1,13 +1,16 @@
|
||||
import logging
|
||||
from typing import Dict, List, Tuple
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
import ccxt
|
||||
|
||||
from freqtrade.constants import BuySell
|
||||
from freqtrade.enums import MarginMode, TradingMode
|
||||
from freqtrade.enums.candletype import CandleType
|
||||
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.exchange.common import retrier
|
||||
from freqtrade.exchange.exchange import timeframe_to_minutes
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -20,7 +23,7 @@ class Okx(Exchange):
|
||||
"""
|
||||
|
||||
_ft_has: Dict = {
|
||||
"ohlcv_candle_limit": 100,
|
||||
"ohlcv_candle_limit": 300, # Warning, special case with data prior to X months
|
||||
"mark_ohlcv_timeframe": "4h",
|
||||
"funding_fee_timeframe": "8h",
|
||||
}
|
||||
@ -37,6 +40,27 @@ class Okx(Exchange):
|
||||
|
||||
net_only = True
|
||||
|
||||
def ohlcv_candle_limit(
|
||||
self, timeframe: str, candle_type: CandleType, since_ms: Optional[int]) -> int:
|
||||
"""
|
||||
Exchange ohlcv candle limit
|
||||
Uses ohlcv_candle_limit_per_timeframe if the exchange has different limits
|
||||
per timeframe (e.g. bittrex), otherwise falls back to ohlcv_candle_limit
|
||||
:param timeframe: Timeframe to check
|
||||
:param candle_type: Candle-type
|
||||
:param since_ms: Candle-type
|
||||
:return: Candle limit as integer
|
||||
"""
|
||||
now = datetime.now(timezone.utc)
|
||||
offset_mins = timeframe_to_minutes(timeframe) * self._ft_has['ohlcv_candle_limit']
|
||||
if since_ms and since_ms < ((now - timedelta(minutes=offset_mins)).timestamp() * 1000):
|
||||
return 100
|
||||
if candle_type not in (CandleType.FUTURES, CandleType.SPOT):
|
||||
return 100
|
||||
|
||||
return int(self._ft_has.get('ohlcv_candle_limit_per_timeframe', {}).get(
|
||||
timeframe, self._ft_has.get('ohlcv_candle_limit')))
|
||||
|
||||
@retrier
|
||||
def additional_exchange_init(self) -> None:
|
||||
"""
|
||||
|
@ -13,6 +13,7 @@ import pytest
|
||||
|
||||
from freqtrade.enums import CandleType
|
||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date
|
||||
from freqtrade.exchange.exchange import timeframe_to_msecs
|
||||
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||
from tests.conftest import get_default_conf_usdt
|
||||
|
||||
@ -236,6 +237,38 @@ class TestCCXTExchange():
|
||||
now = datetime.now(timezone.utc) - timedelta(minutes=(timeframe_to_minutes(timeframe) * 2))
|
||||
assert exchange.klines(pair_tf).iloc[-1]['date'] >= timeframe_to_prev_date(timeframe, now)
|
||||
|
||||
def test_ccxt__async_get_candle_history(self, exchange):
|
||||
exchange, exchangename = exchange
|
||||
# For some weired reason, this test returns random lengths for bittrex.
|
||||
if not exchange._ft_has['ohlcv_has_history'] or exchangename == 'bittrex':
|
||||
return
|
||||
pair = EXCHANGES[exchangename]['pair']
|
||||
timeframe = EXCHANGES[exchangename]['timeframe']
|
||||
candle_type = CandleType.SPOT
|
||||
timeframe_ms = timeframe_to_msecs(timeframe)
|
||||
now = timeframe_to_prev_date(
|
||||
timeframe, datetime.now(timezone.utc))
|
||||
for offset in (360, 120, 30, 10, 5, 2):
|
||||
since = now - timedelta(days=offset)
|
||||
since_ms = int(since.timestamp() * 1000)
|
||||
|
||||
res = exchange.loop.run_until_complete(exchange._async_get_candle_history(
|
||||
pair=pair,
|
||||
timeframe=timeframe,
|
||||
since_ms=since_ms,
|
||||
candle_type=candle_type
|
||||
)
|
||||
)
|
||||
assert res
|
||||
assert res[0] == pair
|
||||
assert res[1] == timeframe
|
||||
assert res[2] == candle_type
|
||||
candles = res[3]
|
||||
candle_count = exchange.ohlcv_candle_limit(timeframe, candle_type, since_ms) * 0.9
|
||||
candle_count1 = (now.timestamp() * 1000 - since_ms) // timeframe_ms
|
||||
assert len(candles) >= min(candle_count, candle_count1)
|
||||
assert candles[0][0] == since_ms or (since_ms + timeframe_ms)
|
||||
|
||||
def test_ccxt_fetch_funding_rate_history(self, exchange_futures):
|
||||
exchange, exchangename = exchange_futures
|
||||
if not exchange:
|
||||
|
Loading…
Reference in New Issue
Block a user