get rid of TICKER_INTERVAL_MINUTES dict, use ccxt's parse_timeframe() instead
This commit is contained in:
		| @@ -6,7 +6,7 @@ bot constants | |||||||
| DEFAULT_CONFIG = 'config.json' | DEFAULT_CONFIG = 'config.json' | ||||||
| DYNAMIC_WHITELIST = 20  # pairs | DYNAMIC_WHITELIST = 20  # pairs | ||||||
| PROCESS_THROTTLE_SECS = 5  # sec | PROCESS_THROTTLE_SECS = 5  # sec | ||||||
| TICKER_INTERVAL = 5  # min | DEFAULT_TICKER_INTERVAL = 5  # min | ||||||
| HYPEROPT_EPOCH = 100  # epochs | HYPEROPT_EPOCH = 100  # epochs | ||||||
| RETRY_TIMEOUT = 30  # sec | RETRY_TIMEOUT = 30  # sec | ||||||
| DEFAULT_STRATEGY = 'DefaultStrategy' | DEFAULT_STRATEGY = 'DefaultStrategy' | ||||||
| @@ -22,22 +22,11 @@ ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] | |||||||
| AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList'] | AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList'] | ||||||
| DRY_RUN_WALLET = 999.9 | DRY_RUN_WALLET = 999.9 | ||||||
|  |  | ||||||
| TICKER_INTERVAL_MINUTES = { | TICKER_INTERVALS = [ | ||||||
|     '1m': 1, |     '1m', '3m', '5m', '15m', '30m', | ||||||
|     '3m': 3, |     '1h', '2h', '4h', '6h', '8h', '12h', | ||||||
|     '5m': 5, |     '1d', '3d', '1w', | ||||||
|     '15m': 15, | ] | ||||||
|     '30m': 30, |  | ||||||
|     '1h': 60, |  | ||||||
|     '2h': 120, |  | ||||||
|     '4h': 240, |  | ||||||
|     '6h': 360, |  | ||||||
|     '8h': 480, |  | ||||||
|     '12h': 720, |  | ||||||
|     '1d': 1440, |  | ||||||
|     '3d': 4320, |  | ||||||
|     '1w': 10080, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| SUPPORTED_FIAT = [ | SUPPORTED_FIAT = [ | ||||||
|     "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", |     "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", | ||||||
| @@ -52,7 +41,7 @@ CONF_SCHEMA = { | |||||||
|     'type': 'object', |     'type': 'object', | ||||||
|     'properties': { |     'properties': { | ||||||
|         'max_open_trades': {'type': 'integer', 'minimum': -1}, |         'max_open_trades': {'type': 'integer', 'minimum': -1}, | ||||||
|         'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())}, |         'ticker_interval': {'type': 'string', 'enum': TICKER_INTERVALS}, | ||||||
|         'stake_currency': {'type': 'string', 'enum': ['BTC', 'XBT', 'ETH', 'USDT', 'EUR', 'USD']}, |         'stake_currency': {'type': 'string', 'enum': ['BTC', 'XBT', 'ETH', 'USDT', 'EUR', 'USD']}, | ||||||
|         'stake_amount': { |         'stake_amount': { | ||||||
|             "type": ["number", "string"], |             "type": ["number", "string"], | ||||||
|   | |||||||
| @@ -4,8 +4,8 @@ Functions to convert data from one format to another | |||||||
| import logging | import logging | ||||||
| import pandas as pd | import pandas as pd | ||||||
| from pandas import DataFrame, to_datetime | from pandas import DataFrame, to_datetime | ||||||
|  | from freqtrade.misc import timeframe_to_minutes | ||||||
|  |  | ||||||
| from freqtrade.constants import TICKER_INTERVAL_MINUTES |  | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
| @@ -65,9 +65,9 @@ def ohlcv_fill_up_missing_data(dataframe: DataFrame, ticker_interval: str) -> Da | |||||||
|         'close': 'last', |         'close': 'last', | ||||||
|         'volume': 'sum' |         'volume': 'sum' | ||||||
|     } |     } | ||||||
|     tick_mins = TICKER_INTERVAL_MINUTES[ticker_interval] |     ticker_minutes = timeframe_to_minutes(ticker_interval) | ||||||
|     # Resample to create "NAN" values |     # Resample to create "NAN" values | ||||||
|     df = dataframe.resample(f'{tick_mins}min', on='date').agg(ohlc_dict) |     df = dataframe.resample(f'{ticker_minutes}min', on='date').agg(ohlc_dict) | ||||||
|  |  | ||||||
|     # Forwardfill close for missing columns |     # Forwardfill close for missing columns | ||||||
|     df['close'] = df['close'].fillna(method='ffill') |     df['close'] = df['close'].fillna(method='ffill') | ||||||
|   | |||||||
| @@ -12,10 +12,12 @@ from typing import Optional, List, Dict, Tuple, Any | |||||||
| import arrow | import arrow | ||||||
| from pandas import DataFrame | from pandas import DataFrame | ||||||
|  |  | ||||||
| from freqtrade import misc, constants, OperationalException | from freqtrade import misc, OperationalException | ||||||
|  | from freqtrade.arguments import TimeRange | ||||||
| from freqtrade.data.converter import parse_ticker_dataframe | from freqtrade.data.converter import parse_ticker_dataframe | ||||||
| from freqtrade.exchange import Exchange | from freqtrade.exchange import Exchange | ||||||
| from freqtrade.arguments import TimeRange | from freqtrade.misc import timeframe_to_minutes | ||||||
|  |  | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
| @@ -163,7 +165,7 @@ def load_cached_data_for_updating(filename: Path, tick_interval: str, | |||||||
|         if timerange.starttype == 'date': |         if timerange.starttype == 'date': | ||||||
|             since_ms = timerange.startts * 1000 |             since_ms = timerange.startts * 1000 | ||||||
|         elif timerange.stoptype == 'line': |         elif timerange.stoptype == 'line': | ||||||
|             num_minutes = timerange.stopts * constants.TICKER_INTERVAL_MINUTES[tick_interval] |             num_minutes = timerange.stopts * timeframe_to_minutes(tick_interval) | ||||||
|             since_ms = arrow.utcnow().shift(minutes=num_minutes).timestamp * 1000 |             since_ms = arrow.utcnow().shift(minutes=num_minutes).timestamp * 1000 | ||||||
|  |  | ||||||
|     # read the cached file |     # read the cached file | ||||||
|   | |||||||
| @@ -15,9 +15,12 @@ from pandas import DataFrame | |||||||
|  |  | ||||||
| from freqtrade import constants, DependencyException, OperationalException, TemporaryError | from freqtrade import constants, DependencyException, OperationalException, TemporaryError | ||||||
| from freqtrade.data.converter import parse_ticker_dataframe | from freqtrade.data.converter import parse_ticker_dataframe | ||||||
|  | from freqtrade.misc import timeframe_to_seconds, timeframe_to_msecs | ||||||
|  |  | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
| API_RETRY_COUNT = 4 | API_RETRY_COUNT = 4 | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -502,8 +505,8 @@ class Exchange(object): | |||||||
|         # Assume exchange returns 500 candles |         # Assume exchange returns 500 candles | ||||||
|         _LIMIT = 500 |         _LIMIT = 500 | ||||||
|  |  | ||||||
|         one_call = constants.TICKER_INTERVAL_MINUTES[tick_interval] * 60 * _LIMIT * 1000 |         one_call = timeframe_to_msecs(tick_interval) * _LIMIT | ||||||
|         logger.debug("one_call: %s", one_call) |         logger.debug("one_call: %s msecs", one_call) | ||||||
|         input_coroutines = [self._async_get_candle_history( |         input_coroutines = [self._async_get_candle_history( | ||||||
|             pair, tick_interval, since) for since in |             pair, tick_interval, since) for since in | ||||||
|             range(since_ms, arrow.utcnow().timestamp * 1000, one_call)] |             range(since_ms, arrow.utcnow().timestamp * 1000, one_call)] | ||||||
| @@ -557,7 +560,7 @@ class Exchange(object): | |||||||
|  |  | ||||||
|     def _now_is_time_to_refresh(self, pair: str, ticker_interval: str) -> bool: |     def _now_is_time_to_refresh(self, pair: str, ticker_interval: str) -> bool: | ||||||
|         # Calculating ticker interval in seconds |         # Calculating ticker interval in seconds | ||||||
|         interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 |         interval_in_sec = timeframe_to_seconds(ticker_interval) | ||||||
|  |  | ||||||
|         return not ((self._pairs_last_refresh_time.get((pair, ticker_interval), 0) |         return not ((self._pairs_last_refresh_time.get((pair, ticker_interval), 0) | ||||||
|                      + interval_in_sec) >= arrow.utcnow().timestamp) |                      + interval_in_sec) >= arrow.utcnow().timestamp) | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ from freqtrade import (DependencyException, OperationalException, | |||||||
| from freqtrade.data.converter import order_book_to_dataframe | from freqtrade.data.converter import order_book_to_dataframe | ||||||
| from freqtrade.data.dataprovider import DataProvider | from freqtrade.data.dataprovider import DataProvider | ||||||
| from freqtrade.edge import Edge | from freqtrade.edge import Edge | ||||||
|  | from freqtrade.misc import timeframe_to_minutes | ||||||
| from freqtrade.persistence import Trade | from freqtrade.persistence import Trade | ||||||
| from freqtrade.rpc import RPCManager, RPCMessageType | from freqtrade.rpc import RPCManager, RPCMessageType | ||||||
| from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver | from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver | ||||||
| @@ -397,7 +398,7 @@ class FreqtradeBot(object): | |||||||
|             exchange=self.exchange.id, |             exchange=self.exchange.id, | ||||||
|             open_order_id=order_id, |             open_order_id=order_id, | ||||||
|             strategy=self.strategy.get_strategy_name(), |             strategy=self.strategy.get_strategy_name(), | ||||||
|             ticker_interval=constants.TICKER_INTERVAL_MINUTES[self.config['ticker_interval']] |             ticker_interval=timeframe_to_minutes(self.config['ticker_interval']) | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         # Update fees if order is closed |         # Update fees if order is closed | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import re | |||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from typing import Dict | from typing import Dict | ||||||
|  |  | ||||||
|  | from ccxt import Exchange | ||||||
| import numpy as np | import numpy as np | ||||||
| from pandas import DataFrame | from pandas import DataFrame | ||||||
| import rapidjson | import rapidjson | ||||||
| @@ -131,3 +132,15 @@ def deep_merge_dicts(source, destination): | |||||||
|             destination[key] = value |             destination[key] = value | ||||||
|  |  | ||||||
|     return destination |     return destination | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def timeframe_to_seconds(ticker_interval: str) -> int: | ||||||
|  |     return Exchange.parse_timeframe(ticker_interval) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def timeframe_to_minutes(ticker_interval: str) -> int: | ||||||
|  |     return Exchange.parse_timeframe(ticker_interval) // 60 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def timeframe_to_msecs(ticker_interval: str) -> int: | ||||||
|  |     return Exchange.parse_timeframe(ticker_interval) * 1000 | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ from freqtrade.arguments import Arguments | |||||||
| from freqtrade.configuration import Configuration | from freqtrade.configuration import Configuration | ||||||
| from freqtrade.data import history | from freqtrade.data import history | ||||||
| from freqtrade.data.dataprovider import DataProvider | from freqtrade.data.dataprovider import DataProvider | ||||||
| from freqtrade.misc import file_dump_json | from freqtrade.misc import file_dump_json, timeframe_to_minutes | ||||||
| from freqtrade.persistence import Trade | from freqtrade.persistence import Trade | ||||||
| from freqtrade.resolvers import ExchangeResolver, StrategyResolver | from freqtrade.resolvers import ExchangeResolver, StrategyResolver | ||||||
| from freqtrade.state import RunMode | from freqtrade.state import RunMode | ||||||
| @@ -77,7 +77,7 @@ class Backtesting(object): | |||||||
|         if self.config.get('strategy_list', None): |         if self.config.get('strategy_list', None): | ||||||
|             # Force one interval |             # Force one interval | ||||||
|             self.ticker_interval = str(self.config.get('ticker_interval')) |             self.ticker_interval = str(self.config.get('ticker_interval')) | ||||||
|             self.ticker_interval_mins = constants.TICKER_INTERVAL_MINUTES[self.ticker_interval] |             self.ticker_interval_mins = timeframe_to_minutes(self.ticker_interval) | ||||||
|             for strat in list(self.config['strategy_list']): |             for strat in list(self.config['strategy_list']): | ||||||
|                 stratconf = deepcopy(self.config) |                 stratconf = deepcopy(self.config) | ||||||
|                 stratconf['strategy'] = strat |                 stratconf['strategy'] = strat | ||||||
| @@ -96,7 +96,7 @@ class Backtesting(object): | |||||||
|         self.strategy = strategy |         self.strategy = strategy | ||||||
|  |  | ||||||
|         self.ticker_interval = self.config.get('ticker_interval') |         self.ticker_interval = self.config.get('ticker_interval') | ||||||
|         self.ticker_interval_mins = constants.TICKER_INTERVAL_MINUTES[self.ticker_interval] |         self.ticker_interval_mins = timeframe_to_minutes(self.ticker_interval) | ||||||
|         self.tickerdata_to_dataframe = strategy.tickerdata_to_dataframe |         self.tickerdata_to_dataframe = strategy.tickerdata_to_dataframe | ||||||
|         self.advise_buy = strategy.advise_buy |         self.advise_buy = strategy.advise_buy | ||||||
|         self.advise_sell = strategy.advise_sell |         self.advise_sell = strategy.advise_sell | ||||||
| @@ -421,7 +421,7 @@ class Backtesting(object): | |||||||
|             min_date, max_date = optimize.get_timeframe(data) |             min_date, max_date = optimize.get_timeframe(data) | ||||||
|             # Validate dataframe for missing values (mainly at start and end, as fillup is called) |             # Validate dataframe for missing values (mainly at start and end, as fillup is called) | ||||||
|             optimize.validate_backtest_data(data, min_date, max_date, |             optimize.validate_backtest_data(data, min_date, max_date, | ||||||
|                                             constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]) |                                             timeframe_to_minutes(self.ticker_interval)) | ||||||
|             logger.info( |             logger.info( | ||||||
|                 'Measuring data from %s up to %s (%s days)..', |                 'Measuring data from %s up to %s (%s days)..', | ||||||
|                 min_date.isoformat(), |                 min_date.isoformat(), | ||||||
|   | |||||||
| @@ -12,8 +12,8 @@ import warnings | |||||||
| import arrow | import arrow | ||||||
| from pandas import DataFrame | from pandas import DataFrame | ||||||
|  |  | ||||||
| from freqtrade import constants |  | ||||||
| from freqtrade.data.dataprovider import DataProvider | from freqtrade.data.dataprovider import DataProvider | ||||||
|  | from freqtrade.misc import timeframe_to_minutes | ||||||
| from freqtrade.persistence import Trade | from freqtrade.persistence import Trade | ||||||
| from freqtrade.wallets import Wallets | from freqtrade.wallets import Wallets | ||||||
|  |  | ||||||
| @@ -221,7 +221,7 @@ class IStrategy(ABC): | |||||||
|  |  | ||||||
|         # Check if dataframe is out of date |         # Check if dataframe is out of date | ||||||
|         signal_date = arrow.get(latest['date']) |         signal_date = arrow.get(latest['date']) | ||||||
|         interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval] |         interval_minutes = timeframe_to_minutes(interval) | ||||||
|         offset = self.config.get('exchange', {}).get('outdated_offset', 5) |         offset = self.config.get('exchange', {}).get('outdated_offset', 5) | ||||||
|         if signal_date < (arrow.utcnow().shift(minutes=-(interval_minutes * 2 + offset))): |         if signal_date < (arrow.utcnow().shift(minutes=-(interval_minutes * 2 + offset))): | ||||||
|             logger.warning( |             logger.warning( | ||||||
|   | |||||||
| @@ -3,11 +3,11 @@ from typing import NamedTuple, List | |||||||
| import arrow | import arrow | ||||||
| from pandas import DataFrame | from pandas import DataFrame | ||||||
|  |  | ||||||
|  | from freqtrade.misc import timeframe_to_minutes | ||||||
| from freqtrade.strategy.interface import SellType | from freqtrade.strategy.interface import SellType | ||||||
| from freqtrade.constants import TICKER_INTERVAL_MINUTES |  | ||||||
|  |  | ||||||
| ticker_start_time = arrow.get(2018, 10, 3) | ticker_start_time = arrow.get(2018, 10, 3) | ||||||
| tests_ticker_interval = "1h" | tests_ticker_interval = '1h' | ||||||
|  |  | ||||||
|  |  | ||||||
| class BTrade(NamedTuple): | class BTrade(NamedTuple): | ||||||
| @@ -32,7 +32,7 @@ class BTContainer(NamedTuple): | |||||||
|  |  | ||||||
|  |  | ||||||
| def _get_frame_time_from_offset(offset): | def _get_frame_time_from_offset(offset): | ||||||
|     return ticker_start_time.shift(minutes=(offset * TICKER_INTERVAL_MINUTES[tests_ticker_interval]) |     return ticker_start_time.shift(minutes=(offset * timeframe_to_minutes(tests_ticker_interval)) | ||||||
|                                    ).datetime.replace(tzinfo=None) |                                    ).datetime.replace(tzinfo=None) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| # pragma pylint: disable=missing-docstring, protected-access, C0103 | # pragma pylint: disable=missing-docstring, protected-access, C0103 | ||||||
| from freqtrade import optimize, constants | from freqtrade import optimize | ||||||
| from freqtrade.arguments import TimeRange | from freqtrade.arguments import TimeRange | ||||||
| from freqtrade.data import history | from freqtrade.data import history | ||||||
|  | from freqtrade.misc import timeframe_to_minutes | ||||||
| from freqtrade.strategy.default_strategy import DefaultStrategy | from freqtrade.strategy.default_strategy import DefaultStrategy | ||||||
| from freqtrade.tests.conftest import log_has, patch_exchange | from freqtrade.tests.conftest import log_has, patch_exchange | ||||||
|  |  | ||||||
| @@ -37,7 +38,7 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None: | |||||||
|     min_date, max_date = optimize.get_timeframe(data) |     min_date, max_date = optimize.get_timeframe(data) | ||||||
|     caplog.clear() |     caplog.clear() | ||||||
|     assert optimize.validate_backtest_data(data, min_date, max_date, |     assert optimize.validate_backtest_data(data, min_date, max_date, | ||||||
|                                            constants.TICKER_INTERVAL_MINUTES["1m"]) |                                            timeframe_to_minutes('1m')) | ||||||
|     assert len(caplog.record_tuples) == 1 |     assert len(caplog.record_tuples) == 1 | ||||||
|     assert log_has( |     assert log_has( | ||||||
|         "UNITTEST/BTC has missing frames: expected 14396, got 13680, that's 716 missing values", |         "UNITTEST/BTC has missing frames: expected 14396, got 13680, that's 716 missing values", | ||||||
| @@ -61,5 +62,5 @@ def test_validate_backtest_data(default_conf, mocker, caplog) -> None: | |||||||
|     min_date, max_date = optimize.get_timeframe(data) |     min_date, max_date = optimize.get_timeframe(data) | ||||||
|     caplog.clear() |     caplog.clear() | ||||||
|     assert not optimize.validate_backtest_data(data, min_date, max_date, |     assert not optimize.validate_backtest_data(data, min_date, max_date, | ||||||
|                                                constants.TICKER_INTERVAL_MINUTES["5m"]) |                                                timeframe_to_minutes('5m')) | ||||||
|     assert len(caplog.record_tuples) == 0 |     assert len(caplog.record_tuples) == 0 | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ from freqtrade import constants, misc | |||||||
| from freqtrade.arguments import Arguments | from freqtrade.arguments import Arguments | ||||||
| from freqtrade.configuration import Configuration | from freqtrade.configuration import Configuration | ||||||
| from freqtrade.data import history | from freqtrade.data import history | ||||||
|  | from freqtrade.misc import timeframe_to_seconds | ||||||
| from freqtrade.resolvers import StrategyResolver | from freqtrade.resolvers import StrategyResolver | ||||||
| from freqtrade.state import RunMode | from freqtrade.state import RunMode | ||||||
|  |  | ||||||
| @@ -193,8 +194,8 @@ def define_index(min_date: int, max_date: int, interval: str) -> int: | |||||||
|     """ |     """ | ||||||
|     Return the index of a specific date |     Return the index of a specific date | ||||||
|     """ |     """ | ||||||
|     interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval] |     interval_seconds = timeframe_to_seconds(interval) | ||||||
|     return int((max_date - min_date) / (interval_minutes * 60)) |     return int((max_date - min_date) / interval_seconds) | ||||||
|  |  | ||||||
|  |  | ||||||
| def plot_parse_args(args: List[str]) -> Namespace: | def plot_parse_args(args: List[str]) -> Namespace: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user