Merge pull request #3430 from freqtrade/timeframe

ticker_interval -> timeframe
This commit is contained in:
Matthias
2020-06-15 13:47:26 +02:00
committed by GitHub
69 changed files with 309 additions and 230 deletions

View File

@@ -15,7 +15,7 @@ ARGS_STRATEGY = ["strategy", "strategy_path"]
ARGS_TRADE = ["db_url", "sd_notify", "dry_run"]
ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange",
ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange",
"max_open_trades", "stake_amount", "fee"]
ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions",
@@ -59,10 +59,10 @@ ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "download_trades", "exchang
ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit",
"db_url", "trade_source", "export", "exportfilename",
"timerange", "ticker_interval", "no_trades"]
"timerange", "timeframe", "no_trades"]
ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
"trade_source", "ticker_interval"]
"trade_source", "timeframe"]
ARGS_SHOW_TRADES = ["db_url", "trade_ids", "print_json"]
@@ -318,7 +318,7 @@ class Arguments:
# Add list-timeframes subcommand
list_timeframes_cmd = subparsers.add_parser(
'list-timeframes',
help='Print available ticker intervals (timeframes) for the exchange.',
help='Print available timeframes for the exchange.',
parents=[_common_parser],
)
list_timeframes_cmd.set_defaults(func=start_list_timeframes)

View File

@@ -75,8 +75,8 @@ def ask_user_config() -> Dict[str, Any]:
},
{
"type": "text",
"name": "ticker_interval",
"message": "Please insert your timeframe (ticker interval):",
"name": "timeframe",
"message": "Please insert your desired timeframe (e.g. 5m):",
"default": "5m",
},
{

View File

@@ -110,8 +110,8 @@ AVAILABLE_CLI_OPTIONS = {
action='store_true',
),
# Optimize common
"ticker_interval": Arg(
'-i', '--ticker-interval',
"timeframe": Arg(
'-i', '--timeframe', '--ticker-interval',
help='Specify ticker interval (`1m`, `5m`, `30m`, `1h`, `1d`).',
),
"timerange": Arg(

View File

@@ -102,8 +102,8 @@ def start_list_timeframes(args: Dict[str, Any]) -> None:
Print ticker intervals (timeframes) available on Exchange
"""
config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE)
# Do not use ticker_interval set in the config
config['ticker_interval'] = None
# Do not use timeframe set in the config
config['timeframe'] = None
# Init exchange
exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False)

View File

@@ -25,7 +25,6 @@ def start_test_pairlist(args: Dict[str, Any]) -> None:
results = {}
for curr in quote_currencies:
config['stake_currency'] = curr
# Do not use ticker_interval set in the config
pairlists = PairListManager(exchange, config)
pairlists.refresh_pairlist()
results[curr] = pairlists.whitelist

View File

@@ -204,9 +204,9 @@ class Configuration:
def _process_optimize_options(self, config: Dict[str, Any]) -> None:
# This will override the strategy configuration
self._args_to_config(config, argname='ticker_interval',
logstring='Parameter -i/--ticker-interval detected ... '
'Using ticker_interval: {} ...')
self._args_to_config(config, argname='timeframe',
logstring='Parameter -i/--timeframe detected ... '
'Using timeframe: {} ...')
self._args_to_config(config, argname='position_stacking',
logstring='Parameter --enable-position-stacking detected ...')
@@ -242,8 +242,8 @@ class Configuration:
self._args_to_config(config, argname='strategy_list',
logstring='Using strategy list of {} strategies', logfun=len)
self._args_to_config(config, argname='ticker_interval',
logstring='Overriding ticker interval with Command line argument')
self._args_to_config(config, argname='timeframe',
logstring='Overriding timeframe with Command line argument')
self._args_to_config(config, argname='export',
logstring='Parameter --export detected: {} ...')

View File

@@ -67,3 +67,9 @@ def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None:
"'tradable_balance_ratio' and remove 'capital_available_percentage' "
"from the edge configuration."
)
if 'ticker_interval' in config:
logger.warning(
"DEPRECATED: "
"Please use 'timeframe' instead of 'ticker_interval."
)
config['timeframe'] = config['ticker_interval']

View File

@@ -71,7 +71,7 @@ CONF_SCHEMA = {
'type': 'object',
'properties': {
'max_open_trades': {'type': ['integer', 'number'], 'minimum': -1},
'ticker_interval': {'type': 'string'},
'timeframe': {'type': 'string'},
'stake_currency': {'type': 'string'},
'stake_amount': {
'type': ['number', 'string'],
@@ -303,6 +303,7 @@ CONF_SCHEMA = {
SCHEMA_TRADE_REQUIRED = [
'exchange',
'timeframe',
'max_open_trades',
'stake_currency',
'stake_amount',

View File

@@ -103,7 +103,7 @@ def load_trades_from_db(db_url: str) -> pd.DataFrame:
"open_rate", "close_rate", "amount", "duration", "sell_reason",
"fee_open", "fee_close", "open_rate_requested", "close_rate_requested",
"stake_amount", "max_rate", "min_rate", "id", "exchange",
"stop_loss", "initial_stop_loss", "strategy", "ticker_interval"]
"stop_loss", "initial_stop_loss", "strategy", "timeframe"]
trades = pd.DataFrame([(t.pair,
t.open_date.replace(tzinfo=timezone.utc),
@@ -121,7 +121,7 @@ def load_trades_from_db(db_url: str) -> pd.DataFrame:
t.min_rate,
t.id, t.exchange,
t.stop_loss, t.initial_stop_loss,
t.strategy, t.ticker_interval
t.strategy, t.timeframe
)
for t in Trade.get_trades().all()],
columns=columns)

View File

@@ -236,12 +236,12 @@ def convert_ohlcv_format(config: Dict[str, Any], convert_from: str, convert_to:
from freqtrade.data.history.idatahandler import get_datahandler
src = get_datahandler(config['datadir'], convert_from)
trg = get_datahandler(config['datadir'], convert_to)
timeframes = config.get('timeframes', [config.get('ticker_interval')])
timeframes = config.get('timeframes', [config.get('timeframe')])
logger.info(f"Converting candle (OHLCV) for timeframe {timeframes}")
if 'pairs' not in config:
config['pairs'] = []
# Check timeframes or fall back to ticker_interval.
# Check timeframes or fall back to timeframe.
for timeframe in timeframes:
config['pairs'].extend(src.ohlcv_get_pairs(config['datadir'],
timeframe))

View File

@@ -55,7 +55,7 @@ class DataProvider:
Use False only for read-only operations (where the dataframe is not modified)
"""
if self.runmode in (RunMode.DRY_RUN, RunMode.LIVE):
return self._exchange.klines((pair, timeframe or self._config['ticker_interval']),
return self._exchange.klines((pair, timeframe or self._config['timeframe']),
copy=copy)
else:
return DataFrame()
@@ -67,7 +67,7 @@ class DataProvider:
:param timeframe: timeframe to get data for
"""
return load_pair_history(pair=pair,
timeframe=timeframe or self._config['ticker_interval'],
timeframe=timeframe or self._config['timeframe'],
datadir=self._config['datadir']
)

View File

@@ -98,14 +98,14 @@ class Edge:
datadir=self.config['datadir'],
pairs=pairs,
exchange=self.exchange,
timeframe=self.strategy.ticker_interval,
timeframe=self.strategy.timeframe,
timerange=self._timerange,
)
data = load_data(
datadir=self.config['datadir'],
pairs=pairs,
timeframe=self.strategy.ticker_interval,
timeframe=self.strategy.timeframe,
timerange=self._timerange,
startup_candles=self.strategy.startup_candle_count,
data_format=self.config.get('dataformat_ohlcv', 'json'),

View File

@@ -115,7 +115,7 @@ class Exchange:
if validate:
# Check if timeframe is available
self.validate_timeframes(config.get('ticker_interval'))
self.validate_timeframes(config.get('timeframe'))
# Initial markets load
self._load_markets()

View File

@@ -421,8 +421,8 @@ class FreqtradeBot:
# running get_signal on historical data fetched
(buy, sell) = self.strategy.get_signal(
pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(pair, self.strategy.ticker_interval))
pair, self.strategy.timeframe,
self.dataprovider.ohlcv(pair, self.strategy.timeframe))
if buy and not sell:
stake_amount = self.get_trade_stake_amount(pair)
@@ -547,7 +547,7 @@ class FreqtradeBot:
exchange=self.exchange.id,
open_order_id=order_id,
strategy=self.strategy.get_strategy_name(),
ticker_interval=timeframe_to_minutes(self.config['ticker_interval'])
timeframe=timeframe_to_minutes(self.config['timeframe'])
)
# Update fees if order is closed
@@ -698,8 +698,8 @@ class FreqtradeBot:
if (config_ask_strategy.get('use_sell_signal', True) or
config_ask_strategy.get('ignore_roi_if_buy_signal', False)):
(buy, sell) = self.strategy.get_signal(
trade.pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(trade.pair, self.strategy.ticker_interval))
trade.pair, self.strategy.timeframe,
self.dataprovider.ohlcv(trade.pair, self.strategy.timeframe))
if config_ask_strategy.get('use_order_book', False):
order_book_min = config_ask_strategy.get('order_book_min', 1)
@@ -784,7 +784,7 @@ class FreqtradeBot:
self.update_trade_state(trade, stoploss_order, sl_order=True)
# Lock pair for one candle to prevent immediate rebuys
self.strategy.lock_pair(trade.pair,
timeframe_to_next_date(self.config['ticker_interval']))
timeframe_to_next_date(self.config['timeframe']))
self._notify_sell(trade, "stoploss")
return True
@@ -1094,7 +1094,7 @@ class FreqtradeBot:
Trade.session.flush()
# Lock pair for one candle to prevent immediate rebuys
self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval']))
self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['timeframe']))
self._notify_sell(trade, order_type)

View File

@@ -95,10 +95,10 @@ class Backtesting:
self.strategylist.append(StrategyResolver.load_strategy(self.config))
validate_config_consistency(self.config)
if "ticker_interval" not in self.config:
if "timeframe" not in self.config:
raise OperationalException("Timeframe (ticker interval) needs to be set in either "
"configuration or as cli argument `--ticker-interval 5m`")
self.timeframe = str(self.config.get('ticker_interval'))
"configuration or as cli argument `--timeframe 5m`")
self.timeframe = str(self.config.get('timeframe'))
self.timeframe_min = timeframe_to_minutes(self.timeframe)
# Get maximum required startup period

View File

@@ -31,13 +31,15 @@ class IHyperOpt(ABC):
Class attributes you can use:
ticker_interval -> int: value of the ticker interval to use for the strategy
"""
ticker_interval: str
ticker_interval: str # DEPRECATED
timeframe: str
def __init__(self, config: dict) -> None:
self.config = config
# Assign ticker_interval to be used in hyperopt
IHyperOpt.ticker_interval = str(config['ticker_interval'])
IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED
IHyperOpt.timeframe = str(config['timeframe'])
@staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
@@ -218,9 +220,10 @@ class IHyperOpt(ABC):
# Why do I still need such shamanic mantras in modern python?
def __getstate__(self):
state = self.__dict__.copy()
state['ticker_interval'] = self.ticker_interval
state['timeframe'] = self.timeframe
return state
def __setstate__(self, state):
self.__dict__.update(state)
IHyperOpt.ticker_interval = state['ticker_interval']
IHyperOpt.ticker_interval = state['timeframe']
IHyperOpt.timeframe = state['timeframe']

View File

@@ -14,7 +14,7 @@ class IHyperOptLoss(ABC):
Interface for freqtrade hyperopt Loss functions.
Defines the custom loss function (`hyperopt_loss_function()` which is evaluated every epoch.)
"""
ticker_interval: str
timeframe: str
@staticmethod
@abstractmethod

View File

@@ -131,6 +131,6 @@ class PairListManager():
def create_pair_list(self, pairs: List[str], timeframe: str = None) -> ListPairsWithTimeframes:
"""
Create list of pair tuples with (pair, ticker_interval)
Create list of pair tuples with (pair, timeframe)
"""
return [(pair, timeframe or self._config['ticker_interval']) for pair in pairs]
return [(pair, timeframe or self._config['timeframe']) for pair in pairs]

View File

@@ -86,7 +86,7 @@ def check_migrate(engine) -> None:
logger.debug(f'trying {table_back_name}')
# Check for latest column
if not has_column(cols, 'sell_order_status'):
if not has_column(cols, 'timeframe'):
logger.info(f'Running database migration - backup available as {table_back_name}')
fee_open = get_column_def(cols, 'fee_open', 'fee')
@@ -107,7 +107,12 @@ def check_migrate(engine) -> None:
min_rate = get_column_def(cols, 'min_rate', 'null')
sell_reason = get_column_def(cols, 'sell_reason', 'null')
strategy = get_column_def(cols, 'strategy', 'null')
ticker_interval = get_column_def(cols, 'ticker_interval', 'null')
# If ticker-interval existed use that, else null.
if has_column(cols, 'ticker_interval'):
timeframe = get_column_def(cols, 'timeframe', 'ticker_interval')
else:
timeframe = get_column_def(cols, 'timeframe', 'null')
open_trade_price = get_column_def(cols, 'open_trade_price',
f'amount * open_rate * (1 + {fee_open})')
close_profit_abs = get_column_def(
@@ -133,7 +138,7 @@ def check_migrate(engine) -> None:
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
stoploss_order_id, stoploss_last_update,
max_rate, min_rate, sell_reason, sell_order_status, strategy,
ticker_interval, open_trade_price, close_profit_abs
timeframe, open_trade_price, close_profit_abs
)
select id, lower(exchange),
case
@@ -155,7 +160,7 @@ def check_migrate(engine) -> None:
{stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update,
{max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason,
{sell_order_status} sell_order_status,
{strategy} strategy, {ticker_interval} ticker_interval,
{strategy} strategy, {timeframe} timeframe,
{open_trade_price} open_trade_price, {close_profit_abs} close_profit_abs
from {table_back_name}
""")
@@ -232,7 +237,7 @@ class Trade(_DECL_BASE):
sell_reason = Column(String, nullable=True)
sell_order_status = Column(String, nullable=True)
strategy = Column(String, nullable=True)
ticker_interval = Column(Integer, nullable=True)
timeframe = Column(Integer, nullable=True)
def __init__(self, **kwargs):
super().__init__(**kwargs)
@@ -253,7 +258,8 @@ class Trade(_DECL_BASE):
'amount': round(self.amount, 8),
'stake_amount': round(self.stake_amount, 8),
'strategy': self.strategy,
'ticker_interval': self.ticker_interval,
'ticker_interval': self.timeframe, # DEPRECATED
'timeframe': self.timeframe,
'fee_open': self.fee_open,
'fee_open_cost': self.fee_open_cost,

View File

@@ -45,7 +45,7 @@ def init_plotscript(config):
data = load_data(
datadir=config.get("datadir"),
pairs=pairs,
timeframe=config.get('ticker_interval', '5m'),
timeframe=config.get('timeframe', '5m'),
timerange=timerange,
data_format=config.get('dataformat_ohlcv', 'json'),
)
@@ -487,7 +487,7 @@ def load_and_plot_trades(config: Dict[str, Any]):
plot_config=strategy.plot_config if hasattr(strategy, 'plot_config') else {}
)
store_plot_file(fig, filename=generate_plot_filename(pair, config['ticker_interval']),
store_plot_file(fig, filename=generate_plot_filename(pair, config['timeframe']),
directory=config['user_data_dir'] / "plot")
logger.info('End of plotting process. %s plots generated', pair_counter)
@@ -515,6 +515,6 @@ def plot_profit(config: Dict[str, Any]) -> None:
# Create an average close price of all the pairs that were involved.
# this could be useful to gauge the overall market trend
fig = generate_profit_graph(plot_elements["pairs"], plot_elements["ohlcv"],
trades, config.get('ticker_interval', '5m'))
trades, config.get('timeframe', '5m'))
store_plot_file(fig, filename='freqtrade-profit-plot.html',
directory=config['user_data_dir'] / "plot", auto_open=True)

View File

@@ -77,8 +77,9 @@ class HyperOptLossResolver(IResolver):
config, kwargs={},
extra_dir=config.get('hyperopt_path'))
# Assign ticker_interval to be used in hyperopt
hyperoptloss.__class__.ticker_interval = str(config['ticker_interval'])
# Assign timeframe to be used in hyperopt
hyperoptloss.__class__.ticker_interval = str(config['timeframe'])
hyperoptloss.__class__.timeframe = str(config['timeframe'])
if not hasattr(hyperoptloss, 'hyperopt_loss_function'):
raise OperationalException(

View File

@@ -50,11 +50,19 @@ class StrategyResolver(IResolver):
if 'ask_strategy' not in config:
config['ask_strategy'] = {}
if hasattr(strategy, 'ticker_interval') and not hasattr(strategy, 'timeframe'):
# Assign ticker_interval to timeframe to keep compatibility
if 'timeframe' not in config:
logger.warning(
"DEPRECATED: Please migrate to using 'timeframe' instead of 'ticker_interval'."
)
strategy.timeframe = strategy.ticker_interval
# Set attributes
# Check if we need to override configuration
# (Attribute name, default, subkey)
attributes = [("minimal_roi", {"0": 10.0}, None),
("ticker_interval", None, None),
("timeframe", None, None),
("stoploss", None, None),
("trailing_stop", None, None),
("trailing_stop_positive", None, None),
@@ -80,6 +88,9 @@ class StrategyResolver(IResolver):
StrategyResolver._override_attribute_helper(strategy, config,
attribute, default)
# Assign deprecated variable - to not break users code relying on this.
strategy.ticker_interval = strategy.timeframe
# Loop this list again to have output combined
for attribute, _, subkey in attributes:
if subkey and attribute in config[subkey]:

View File

@@ -101,7 +101,8 @@ class RPC:
'trailing_stop_positive': config.get('trailing_stop_positive'),
'trailing_stop_positive_offset': config.get('trailing_stop_positive_offset'),
'trailing_only_offset_is_reached': config.get('trailing_only_offset_is_reached'),
'ticker_interval': config['ticker_interval'],
'ticker_interval': config['timeframe'], # DEPRECATED
'timeframe': config['timeframe'],
'exchange': config['exchange']['name'],
'strategy': config['strategy'],
'forcebuy_enabled': config.get('forcebuy_enable', False),

View File

@@ -72,7 +72,7 @@ class RPCManager:
minimal_roi = config['minimal_roi']
stoploss = config['stoploss']
trailing_stop = config['trailing_stop']
ticker_interval = config['ticker_interval']
timeframe = config['timeframe']
exchange_name = config['exchange']['name']
strategy_name = config.get('strategy', '')
self.send_msg({
@@ -81,7 +81,7 @@ class RPCManager:
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
f'*Minimum ROI:* `{minimal_roi}`\n'
f'*{"Trailing " if trailing_stop else ""}Stoploss:* `{stoploss}`\n'
f'*Ticker Interval:* `{ticker_interval}`\n'
f'*Timeframe:* `{timeframe}`\n'
f'*Strategy:* `{strategy_name}`'
})
self.send_msg({

View File

@@ -669,7 +669,7 @@ class Telegram(RPC):
f"*Ask strategy:* ```\n{json.dumps(val['ask_strategy'])}```\n"
f"*Bid strategy:* ```\n{json.dumps(val['bid_strategy'])}```\n"
f"{sl_info}"
f"*Ticker Interval:* `{val['ticker_interval']}`\n"
f"*Timeframe:* `{val['timeframe']}`\n"
f"*Strategy:* `{val['strategy']}`\n"
f"*Current state:* `{val['state']}`"
)

View File

@@ -62,7 +62,7 @@ class IStrategy(ABC):
Attributes you can use:
minimal_roi -> Dict: Minimal ROI designed for the strategy
stoploss -> float: optimal stoploss designed for the strategy
ticker_interval -> str: value of the timeframe (ticker interval) to use with the strategy
timeframe -> str: value of the timeframe (ticker interval) to use with the strategy
"""
# Strategy interface version
# Default to version 2
@@ -85,8 +85,9 @@ class IStrategy(ABC):
trailing_stop_positive_offset: float = 0.0
trailing_only_offset_is_reached = False
# associated ticker interval
ticker_interval: str
# associated timeframe
ticker_interval: str # DEPRECATED
timeframe: str
# Optional order types
order_types: Dict = {

View File

@@ -4,7 +4,7 @@
"stake_amount": {{ stake_amount }},
"tradable_balance_ratio": 0.99,
"fiat_display_currency": "{{ fiat_display_currency }}",
"ticker_interval": "{{ ticker_interval }}",
"timeframe": "{{ timeframe }}",
"dry_run": {{ dry_run | lower }},
"cancel_open_orders_on_exit": false,
"unfilledtimeout": {

View File

@@ -51,8 +51,8 @@ class {{ strategy }}(IStrategy):
# trailing_stop_positive = 0.01
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
# Optimal ticker interval for the strategy.
ticker_interval = '5m'
# Optimal timeframe for the strategy.
timeframe = '5m'
# Run "populate_indicators()" only for new candle.
process_only_new_candles = False

View File

@@ -53,7 +53,7 @@ class SampleStrategy(IStrategy):
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
# Optimal ticker interval for the strategy.
ticker_interval = '5m'
timeframe = '5m'
# Run "populate_indicators()" only for new candle.
process_only_new_candles = False