From 2aa1b43f013a29ee2a398b660cd23709eb57eed1 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 4 Apr 2019 20:56:40 +0300 Subject: [PATCH] get rid of TICKER_INTERVAL_MINUTES dict, use ccxt's parse_timeframe() instead --- freqtrade/constants.py | 25 +++++++---------------- freqtrade/data/converter.py | 6 +++--- freqtrade/data/history.py | 8 +++++--- freqtrade/exchange/exchange.py | 9 +++++--- freqtrade/freqtradebot.py | 3 ++- freqtrade/misc.py | 13 ++++++++++++ freqtrade/optimize/backtesting.py | 8 ++++---- freqtrade/strategy/interface.py | 4 ++-- freqtrade/tests/optimize/__init__.py | 6 +++--- freqtrade/tests/optimize/test_optimize.py | 7 ++++--- scripts/plot_profit.py | 5 +++-- 11 files changed, 52 insertions(+), 42 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 02062acc4..5243eeb4a 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -6,7 +6,7 @@ bot constants DEFAULT_CONFIG = 'config.json' DYNAMIC_WHITELIST = 20 # pairs PROCESS_THROTTLE_SECS = 5 # sec -TICKER_INTERVAL = 5 # min +DEFAULT_TICKER_INTERVAL = 5 # min HYPEROPT_EPOCH = 100 # epochs RETRY_TIMEOUT = 30 # sec DEFAULT_STRATEGY = 'DefaultStrategy' @@ -22,22 +22,11 @@ ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList'] DRY_RUN_WALLET = 999.9 -TICKER_INTERVAL_MINUTES = { - '1m': 1, - '3m': 3, - '5m': 5, - '15m': 15, - '30m': 30, - '1h': 60, - '2h': 120, - '4h': 240, - '6h': 360, - '8h': 480, - '12h': 720, - '1d': 1440, - '3d': 4320, - '1w': 10080, -} +TICKER_INTERVALS = [ + '1m', '3m', '5m', '15m', '30m', + '1h', '2h', '4h', '6h', '8h', '12h', + '1d', '3d', '1w', +] SUPPORTED_FIAT = [ "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", @@ -52,7 +41,7 @@ CONF_SCHEMA = { 'type': 'object', 'properties': { '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_amount': { "type": ["number", "string"], diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index c32338bbe..28749293b 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -4,8 +4,8 @@ Functions to convert data from one format to another import logging import pandas as pd from pandas import DataFrame, to_datetime +from freqtrade.misc import timeframe_to_minutes -from freqtrade.constants import TICKER_INTERVAL_MINUTES logger = logging.getLogger(__name__) @@ -65,9 +65,9 @@ def ohlcv_fill_up_missing_data(dataframe: DataFrame, ticker_interval: str) -> Da 'close': 'last', 'volume': 'sum' } - tick_mins = TICKER_INTERVAL_MINUTES[ticker_interval] + ticker_minutes = timeframe_to_minutes(ticker_interval) # 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 df['close'] = df['close'].fillna(method='ffill') diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 7d89f7ad6..0ecc632e4 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -12,10 +12,12 @@ from typing import Optional, List, Dict, Tuple, Any import arrow 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.exchange import Exchange -from freqtrade.arguments import TimeRange +from freqtrade.misc import timeframe_to_minutes + logger = logging.getLogger(__name__) @@ -163,7 +165,7 @@ def load_cached_data_for_updating(filename: Path, tick_interval: str, if timerange.starttype == 'date': since_ms = timerange.startts * 1000 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 # read the cached file diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 011be58e5..f18a71a50 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -15,9 +15,12 @@ from pandas import DataFrame from freqtrade import constants, DependencyException, OperationalException, TemporaryError from freqtrade.data.converter import parse_ticker_dataframe +from freqtrade.misc import timeframe_to_seconds, timeframe_to_msecs + logger = logging.getLogger(__name__) + API_RETRY_COUNT = 4 @@ -502,8 +505,8 @@ class Exchange(object): # Assume exchange returns 500 candles _LIMIT = 500 - one_call = constants.TICKER_INTERVAL_MINUTES[tick_interval] * 60 * _LIMIT * 1000 - logger.debug("one_call: %s", one_call) + one_call = timeframe_to_msecs(tick_interval) * _LIMIT + logger.debug("one_call: %s msecs", one_call) input_coroutines = [self._async_get_candle_history( pair, tick_interval, since) for since in 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: # 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) + interval_in_sec) >= arrow.utcnow().timestamp) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index acf47b065..e9f636ec7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -16,6 +16,7 @@ from freqtrade import (DependencyException, OperationalException, from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge +from freqtrade.misc import timeframe_to_minutes from freqtrade.persistence import Trade from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver @@ -397,7 +398,7 @@ class FreqtradeBot(object): exchange=self.exchange.id, open_order_id=order_id, 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 diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 38f758669..9aeaef069 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -8,6 +8,7 @@ import re from datetime import datetime from typing import Dict +from ccxt import Exchange import numpy as np from pandas import DataFrame import rapidjson @@ -131,3 +132,15 @@ def deep_merge_dicts(source, destination): destination[key] = value 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 diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 293511fc0..e0360cdef 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -19,7 +19,7 @@ from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.data import history 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.resolvers import ExchangeResolver, StrategyResolver from freqtrade.state import RunMode @@ -77,7 +77,7 @@ class Backtesting(object): if self.config.get('strategy_list', None): # Force one 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']): stratconf = deepcopy(self.config) stratconf['strategy'] = strat @@ -96,7 +96,7 @@ class Backtesting(object): self.strategy = strategy 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.advise_buy = strategy.advise_buy self.advise_sell = strategy.advise_sell @@ -421,7 +421,7 @@ class Backtesting(object): min_date, max_date = optimize.get_timeframe(data) # Validate dataframe for missing values (mainly at start and end, as fillup is called) optimize.validate_backtest_data(data, min_date, max_date, - constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]) + timeframe_to_minutes(self.ticker_interval)) logger.info( 'Measuring data from %s up to %s (%s days)..', min_date.isoformat(), diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index fcb27d7bd..646bd2a94 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -12,8 +12,8 @@ import warnings import arrow from pandas import DataFrame -from freqtrade import constants from freqtrade.data.dataprovider import DataProvider +from freqtrade.misc import timeframe_to_minutes from freqtrade.persistence import Trade from freqtrade.wallets import Wallets @@ -221,7 +221,7 @@ class IStrategy(ABC): # Check if dataframe is out of 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) if signal_date < (arrow.utcnow().shift(minutes=-(interval_minutes * 2 + offset))): logger.warning( diff --git a/freqtrade/tests/optimize/__init__.py b/freqtrade/tests/optimize/__init__.py index 075938a61..9203ec19c 100644 --- a/freqtrade/tests/optimize/__init__.py +++ b/freqtrade/tests/optimize/__init__.py @@ -3,11 +3,11 @@ from typing import NamedTuple, List import arrow from pandas import DataFrame +from freqtrade.misc import timeframe_to_minutes from freqtrade.strategy.interface import SellType -from freqtrade.constants import TICKER_INTERVAL_MINUTES ticker_start_time = arrow.get(2018, 10, 3) -tests_ticker_interval = "1h" +tests_ticker_interval = '1h' class BTrade(NamedTuple): @@ -32,7 +32,7 @@ class BTContainer(NamedTuple): 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) diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index 99cd24c26..088743038 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -1,7 +1,8 @@ # pragma pylint: disable=missing-docstring, protected-access, C0103 -from freqtrade import optimize, constants +from freqtrade import optimize from freqtrade.arguments import TimeRange from freqtrade.data import history +from freqtrade.misc import timeframe_to_minutes from freqtrade.strategy.default_strategy import DefaultStrategy 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) caplog.clear() 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 log_has( "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) caplog.clear() 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 diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index 394f02116..8330e2a88 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -28,6 +28,7 @@ from freqtrade import constants, misc from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.data import history +from freqtrade.misc import timeframe_to_seconds from freqtrade.resolvers import StrategyResolver 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 """ - interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval] - return int((max_date - min_date) / (interval_minutes * 60)) + interval_seconds = timeframe_to_seconds(interval) + return int((max_date - min_date) / interval_seconds) def plot_parse_args(args: List[str]) -> Namespace: