Clarify "required candle" message

This commit is contained in:
Matthias 2021-10-08 14:36:52 +02:00
parent a1be6124f2
commit 482f4418c6

View File

@ -26,9 +26,9 @@ from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFun
InvalidOrderException, OperationalException, PricingError,
RetryableOrderError, TemporaryError)
from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, BAD_EXCHANGES,
EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED,
remove_credentials, retrier, retrier_async)
from freqtrade.misc import chunks, deep_merge_dicts, safe_value_fallback2
EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, retrier,
retrier_async)
from freqtrade.misc import deep_merge_dicts, safe_value_fallback2
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
@ -54,16 +54,12 @@ class Exchange:
# Parameters to add directly to buy/sell calls (like agreeing to trading agreement)
_params: Dict = {}
# Additional headers - added to the ccxt object
_headers: Dict = {}
# Dict to specify which options each exchange implements
# This defines defaults, which can be selectively overridden by subclasses using _ft_has
# or by specifying them in the configuration.
_ft_has_default: Dict = {
"stoploss_on_exchange": False,
"order_time_in_force": ["gtc"],
"time_in_force_parameter": "timeInForce",
"ohlcv_params": {},
"ohlcv_candle_limit": 500,
"ohlcv_partial_candle": True,
@ -104,7 +100,6 @@ class Exchange:
# Holds all open sell orders for dry_run
self._dry_run_open_orders: Dict[str, Any] = {}
remove_credentials(config)
if config['dry_run']:
logger.info('Instance is running with dry_run enabled')
@ -174,7 +169,7 @@ class Exchange:
asyncio.get_event_loop().run_until_complete(self._api_async.close())
def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt,
ccxt_kwargs: Dict = {}) -> ccxt.Exchange:
ccxt_kwargs: dict = None) -> ccxt.Exchange:
"""
Initialize ccxt with given config and return valid
ccxt instance.
@ -193,10 +188,6 @@ class Exchange:
}
if ccxt_kwargs:
logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
if self._headers:
# Inject static headers after the above output to not confuse users.
ccxt_kwargs = deep_merge_dicts({'headers': self._headers}, ccxt_kwargs)
if ccxt_kwargs:
ex_config.update(ccxt_kwargs)
try:
@ -480,7 +471,7 @@ class Exchange:
if startup_candles + 5 > candle_limit:
raise OperationalException(
f"This strategy requires {startup_candles} candles to start. "
f"{self.name} only provides {candle_limit} for {timeframe}.")
f"{self.name} only provides {candle_limit - 5} for {timeframe}.")
def exchange_has(self, endpoint: str) -> bool:
"""
@ -523,7 +514,7 @@ class Exchange:
precision = self.markets[pair]['precision']['price']
missing = price % precision
if missing != 0:
price = round(price - missing + precision, 10)
price = price - missing + precision
else:
symbol_prec = self.markets[pair]['precision']['price']
big_price = price * pow(10, symbol_prec)
@ -725,8 +716,7 @@ class Exchange:
params = self._params.copy()
if time_in_force != 'gtc' and ordertype != 'market':
param = self._ft_has.get('time_in_force_parameter', '')
params.update({param: time_in_force})
params.update({'timeInForce': time_in_force})
try:
# Set the precision for amount and price(rate) as accepted by the exchange
@ -1058,7 +1048,7 @@ class Exchange:
ticker_rate = ticker[conf_strategy['price_side']]
if ticker['last'] and ticker_rate:
if side == 'buy' and ticker_rate > ticker['last']:
balance = conf_strategy.get('ask_last_balance', 0.0)
balance = conf_strategy['ask_last_balance']
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
elif side == 'sell' and ticker_rate < ticker['last']:
balance = conf_strategy.get('bid_last_balance', 0.0)
@ -1195,7 +1185,7 @@ class Exchange:
# Historic data
def get_historic_ohlcv(self, pair: str, timeframe: str,
since_ms: int, is_new_pair: bool = False) -> List:
since_ms: int) -> List:
"""
Get candle history using asyncio and returns the list of candles.
Handles all async work for this.
@ -1207,7 +1197,7 @@ class Exchange:
"""
return asyncio.get_event_loop().run_until_complete(
self._async_get_historic_ohlcv(pair=pair, timeframe=timeframe,
since_ms=since_ms, is_new_pair=is_new_pair))
since_ms=since_ms))
def get_historic_ohlcv_as_df(self, pair: str, timeframe: str,
since_ms: int) -> DataFrame:
@ -1222,12 +1212,11 @@ class Exchange:
return ohlcv_to_dataframe(ticks, timeframe, pair=pair, fill_missing=True,
drop_incomplete=self._ohlcv_partial_candle)
async def _async_get_historic_ohlcv(self, pair: str, timeframe: str,
since_ms: int, is_new_pair: bool
) -> List:
async def _async_get_historic_ohlcv(self, pair: str,
timeframe: str,
since_ms: int) -> List:
"""
Download historic ohlcv
:param is_new_pair: used by binance subclass to allow "fast" new pair downloading
"""
one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe)
@ -1240,11 +1229,10 @@ class Exchange:
pair, timeframe, since) for since in
range(since_ms, arrow.utcnow().int_timestamp * 1000, one_call)]
data: List = []
# Chunk requests into batches of 100 to avoid overwelming ccxt Throttling
for input_coro in chunks(input_coroutines, 100):
results = await asyncio.gather(*input_coroutines, return_exceptions=True)
results = await asyncio.gather(*input_coro, return_exceptions=True)
# Combine gathered results
data: List = []
for res in results:
if isinstance(res, Exception):
logger.warning("Async code raised an exception: %s", res.__class__.__name__)
@ -1255,7 +1243,7 @@ class Exchange:
data.extend(new_data)
# Sort data again after extending the result - above calls return in "async order"
data = sorted(data, key=lambda x: x[0])
logger.info(f"Downloaded data for {pair} with length {len(data)}.")
logger.info("Downloaded data for %s with length %s.", pair, len(data))
return data
def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes, *,