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