Okx - conditional candle-length
This commit is contained in:
parent
64668b11da
commit
111b04c9e6
@ -309,12 +309,15 @@ class Exchange:
|
|||||||
if self.log_responses:
|
if self.log_responses:
|
||||||
logger.info(f"API {endpoint}: {response}")
|
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
|
Exchange ohlcv candle limit
|
||||||
Uses ohlcv_candle_limit_per_timeframe if the exchange has different limits
|
Uses ohlcv_candle_limit_per_timeframe if the exchange has different limits
|
||||||
per timeframe (e.g. bittrex), otherwise falls back to ohlcv_candle_limit
|
per timeframe (e.g. bittrex), otherwise falls back to ohlcv_candle_limit
|
||||||
:param timeframe: Timeframe to check
|
:param timeframe: Timeframe to check
|
||||||
|
:param candle_type: Candle-type
|
||||||
|
:param since_ms: Candle-type
|
||||||
:return: Candle limit as integer
|
:return: Candle limit as integer
|
||||||
"""
|
"""
|
||||||
return int(self._ft_has.get('ohlcv_candle_limit_per_timeframe', {}).get(
|
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().
|
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.
|
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.
|
# Require one more candle - to account for the still open candle.
|
||||||
candle_count = startup_candles + 1
|
candle_count = startup_candles + 1
|
||||||
# Allow 5 calls to the exchange per pair
|
# 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!)
|
: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(
|
logger.debug(
|
||||||
"one_call: %s msecs (%s)",
|
"one_call: %s msecs (%s)",
|
||||||
one_call,
|
one_call,
|
||||||
@ -1744,7 +1748,8 @@ class Exchange:
|
|||||||
if (not since_ms
|
if (not since_ms
|
||||||
and (self._ft_has["ohlcv_require_since"] or self.required_candle_call_count > 1)):
|
and (self._ft_has["ohlcv_require_since"] or self.required_candle_call_count > 1)):
|
||||||
# Multiple calls for one pair - to get more history
|
# 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
|
move_to = one_call * self.required_candle_call_count
|
||||||
now = timeframe_to_next_date(timeframe)
|
now = timeframe_to_next_date(timeframe)
|
||||||
since_ms = int((now - timedelta(seconds=move_to // 1000)).timestamp() * 1000)
|
since_ms = int((now - timedelta(seconds=move_to // 1000)).timestamp() * 1000)
|
||||||
@ -1862,7 +1867,9 @@ class Exchange:
|
|||||||
pair, timeframe, since_ms, s
|
pair, timeframe, since_ms, s
|
||||||
)
|
)
|
||||||
params = deepcopy(self._ft_has.get('ohlcv_params', {}))
|
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:
|
if candle_type != CandleType.SPOT:
|
||||||
params.update({'price': candle_type})
|
params.update({'price': candle_type})
|
||||||
if candle_type != CandleType.FUNDING_RATE:
|
if candle_type != CandleType.FUNDING_RATE:
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Dict, List, Tuple
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
|
|
||||||
from freqtrade.constants import BuySell
|
from freqtrade.constants import BuySell
|
||||||
from freqtrade.enums import MarginMode, TradingMode
|
from freqtrade.enums import MarginMode, TradingMode
|
||||||
|
from freqtrade.enums.candletype import CandleType
|
||||||
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
from freqtrade.exceptions import DDosProtection, 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.exchange.exchange import timeframe_to_minutes
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -20,7 +23,7 @@ class Okx(Exchange):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"ohlcv_candle_limit": 100,
|
"ohlcv_candle_limit": 300, # Warning, special case with data prior to X months
|
||||||
"mark_ohlcv_timeframe": "4h",
|
"mark_ohlcv_timeframe": "4h",
|
||||||
"funding_fee_timeframe": "8h",
|
"funding_fee_timeframe": "8h",
|
||||||
}
|
}
|
||||||
@ -37,6 +40,27 @@ class Okx(Exchange):
|
|||||||
|
|
||||||
net_only = True
|
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
|
@retrier
|
||||||
def additional_exchange_init(self) -> None:
|
def additional_exchange_init(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -13,6 +13,7 @@ import pytest
|
|||||||
|
|
||||||
from freqtrade.enums import CandleType
|
from freqtrade.enums import CandleType
|
||||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date
|
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 freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||||
from tests.conftest import get_default_conf_usdt
|
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))
|
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)
|
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):
|
def test_ccxt_fetch_funding_rate_history(self, exchange_futures):
|
||||||
exchange, exchangename = exchange_futures
|
exchange, exchangename = exchange_futures
|
||||||
if not exchange:
|
if not exchange:
|
||||||
|
Loading…
Reference in New Issue
Block a user