get rid of TICKER_INTERVAL_MINUTES dict, use ccxt's parse_timeframe() instead
This commit is contained in:
parent
75c522e082
commit
2aa1b43f01
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user