From b2025597aa3f97f9f6a6b14e0402534dc8e2cdcc Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 20:15:48 +0200 Subject: [PATCH 01/54] Build-commands should write timeframe instead of ticker interval --- freqtrade/commands/build_config_commands.py | 4 ++-- freqtrade/templates/base_config.json.j2 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index 87098f53c..0c98b2e55 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -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", }, { diff --git a/freqtrade/templates/base_config.json.j2 b/freqtrade/templates/base_config.json.j2 index 6d3174347..e47c32309 100644 --- a/freqtrade/templates/base_config.json.j2 +++ b/freqtrade/templates/base_config.json.j2 @@ -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": { From 009ea0639f90535a0f350311231c217d1664deab Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 20:33:26 +0200 Subject: [PATCH 02/54] Exchange some occurances of ticker_interval --- freqtrade/commands/arguments.py | 6 ++--- freqtrade/commands/cli_options.py | 4 +-- freqtrade/commands/list_commands.py | 4 +-- freqtrade/constants.py | 2 ++ tests/conftest.py | 2 +- tests/exchange/test_exchange.py | 8 +++--- tests/optimize/test_backtest_detail.py | 2 +- tests/optimize/test_backtesting.py | 34 +++++++++++++------------- tests/optimize/test_edge_cli.py | 8 +++--- tests/optimize/test_hyperopt.py | 8 +++--- 10 files changed, 40 insertions(+), 38 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 1b7bbfeb5..36e3dedf0 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -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"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index ee9208c33..3ed2f81d1 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -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( diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index e5131f9b2..b29aabe25 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -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) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 1984d4866..511c2993d 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -72,6 +72,7 @@ CONF_SCHEMA = { '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 +304,7 @@ CONF_SCHEMA = { SCHEMA_TRADE_REQUIRED = [ 'exchange', + 'timeframe', 'max_open_trades', 'stake_currency', 'stake_amount', diff --git a/tests/conftest.py b/tests/conftest.py index 971f7a5fa..f62b18f98 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -247,7 +247,7 @@ def default_conf(testdatadir): "stake_currency": "BTC", "stake_amount": 0.001, "fiat_display_currency": "USD", - "ticker_interval": '5m', + "timeframe": '5m', "dry_run": True, "cancel_open_orders_on_exit": False, "minimal_roi": { diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 25aecba5c..6df94f356 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1350,7 +1350,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_ # exchange = Exchange(default_conf) await async_ccxt_exception(mocker, default_conf, MagicMock(), "_async_get_candle_history", "fetch_ohlcv", - pair='ABCD/BTC', timeframe=default_conf['ticker_interval']) + pair='ABCD/BTC', timeframe=default_conf['timeframe']) api_mock = MagicMock() with pytest.raises(OperationalException, @@ -1480,7 +1480,7 @@ async def test___async_get_candle_history_sort(default_conf, mocker, exchange_na exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) sort_mock = mocker.patch('freqtrade.exchange.exchange.sorted', MagicMock(side_effect=sort_data)) # Test the OHLCV data sort - res = await exchange._async_get_candle_history('ETH/BTC', default_conf['ticker_interval']) + res = await exchange._async_get_candle_history('ETH/BTC', default_conf['timeframe']) assert res[0] == 'ETH/BTC' res_ohlcv = res[2] @@ -1517,9 +1517,9 @@ async def test___async_get_candle_history_sort(default_conf, mocker, exchange_na # Reset sort mock sort_mock = mocker.patch('freqtrade.exchange.sorted', MagicMock(side_effect=sort_data)) # Test the OHLCV data sort - res = await exchange._async_get_candle_history('ETH/BTC', default_conf['ticker_interval']) + res = await exchange._async_get_candle_history('ETH/BTC', default_conf['timeframe']) assert res[0] == 'ETH/BTC' - assert res[1] == default_conf['ticker_interval'] + assert res[1] == default_conf['timeframe'] res_ohlcv = res[2] # Sorted not called again - data is already in order assert sort_mock.call_count == 0 diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index e7bc76c1d..9b3043086 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -360,7 +360,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: """ default_conf["stoploss"] = data.stop_loss default_conf["minimal_roi"] = data.roi - default_conf["ticker_interval"] = tests_timeframe + default_conf["timeframe"] = tests_timeframe default_conf["trailing_stop"] = data.trailing_stop default_conf["trailing_only_offset_is_reached"] = data.trailing_only_offset_is_reached # Only add this to configuration If it's necessary diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index ace82d28b..407604d9c 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -81,7 +81,7 @@ def load_data_test(what, testdatadir): def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None: patch_exchange(mocker) - config['ticker_interval'] = '1m' + config['timeframe'] = '1m' backtesting = Backtesting(config) data = load_data_test(contour, testdatadir) @@ -165,7 +165,7 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca assert 'pair_whitelist' in config['exchange'] assert 'datadir' in config assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'ticker_interval' in config + assert 'timeframe' in config assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog) assert 'position_stacking' not in config @@ -189,7 +189,7 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', '/foo/bar', - '--ticker-interval', '1m', + '--timeframe', '1m', '--enable-position-stacking', '--disable-max-market-positions', '--timerange', ':100', @@ -208,8 +208,8 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> assert config['runmode'] == RunMode.BACKTEST assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + assert 'timeframe' in config + assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', caplog) assert 'position_stacking' in config @@ -288,7 +288,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None: def test_backtesting_init_no_ticker_interval(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - del default_conf['ticker_interval'] + del default_conf['timeframe'] default_conf['strategy_list'] = ['DefaultStrategy', 'SampleStrategy'] @@ -337,7 +337,7 @@ def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None: mocker.patch('freqtrade.pairlist.pairlistmanager.PairListManager.whitelist', PropertyMock(return_value=['UNITTEST/BTC'])) - default_conf['ticker_interval'] = '1m' + default_conf['timeframe'] = '1m' default_conf['datadir'] = testdatadir default_conf['export'] = None default_conf['timerange'] = '-1510694220' @@ -367,7 +367,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) -> mocker.patch('freqtrade.pairlist.pairlistmanager.PairListManager.whitelist', PropertyMock(return_value=['UNITTEST/BTC'])) - default_conf['ticker_interval'] = "1m" + default_conf['timeframe'] = "1m" default_conf['datadir'] = testdatadir default_conf['export'] = None default_conf['timerange'] = '20180101-20180102' @@ -387,7 +387,7 @@ def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) -> mocker.patch('freqtrade.pairlist.pairlistmanager.PairListManager.whitelist', PropertyMock(return_value=[])) - default_conf['ticker_interval'] = "1m" + default_conf['timeframe'] = "1m" default_conf['datadir'] = testdatadir default_conf['export'] = None default_conf['timerange'] = '20180101-20180102' @@ -534,7 +534,7 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC', datadir=testdatadir) - default_conf['ticker_interval'] = '1m' + default_conf['timeframe'] = '1m' backtesting = Backtesting(default_conf) backtesting.strategy.advise_buy = _trend_alternate # Override backtesting.strategy.advise_sell = _trend_alternate # Override @@ -573,7 +573,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) # Remove data for one pair from the beginning of the data data[pair] = data[pair][tres:].reset_index() - default_conf['ticker_interval'] = '5m' + default_conf['timeframe'] = '5m' backtesting = Backtesting(default_conf) backtesting.strategy.advise_buy = _trend_alternate_hold # Override @@ -623,7 +623,7 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir): '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', str(testdatadir), - '--ticker-interval', '1m', + '--timeframe', '1m', '--timerange', '1510694220-1510700340', '--enable-position-stacking', '--disable-max-market-positions' @@ -632,7 +632,7 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir): start_backtesting(args) # check the logs, that will contain the backtest result exists = [ - 'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + 'Parameter -i/--timeframe detected ... Using timeframe: 1m ...', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Parameter --timerange detected: 1510694220-1510700340 ...', f'Using data directory: {testdatadir} ...', @@ -676,7 +676,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): '--config', 'config.json', '--datadir', str(testdatadir), '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), - '--ticker-interval', '1m', + '--timeframe', '1m', '--timerange', '1510694220-1510700340', '--enable-position-stacking', '--disable-max-market-positions', @@ -695,7 +695,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): # check the logs, that will contain the backtest result exists = [ - 'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + 'Parameter -i/--timeframe detected ... Using timeframe: 1m ...', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Parameter --timerange detected: 1510694220-1510700340 ...', f'Using data directory: {testdatadir} ...', @@ -765,7 +765,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat '--config', 'config.json', '--datadir', str(testdatadir), '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), - '--ticker-interval', '1m', + '--timeframe', '1m', '--timerange', '1510694220-1510700340', '--enable-position-stacking', '--disable-max-market-positions', @@ -778,7 +778,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat # check the logs, that will contain the backtest result exists = [ - 'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + 'Parameter -i/--timeframe detected ... Using timeframe: 1m ...', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Parameter --timerange detected: 1510694220-1510700340 ...', f'Using data directory: {testdatadir} ...', diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py index a5e468542..acec51f66 100644 --- a/tests/optimize/test_edge_cli.py +++ b/tests/optimize/test_edge_cli.py @@ -29,7 +29,7 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca assert 'pair_whitelist' in config['exchange'] assert 'datadir' in config assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'ticker_interval' in config + assert 'timeframe' in config assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog) assert 'timerange' not in config @@ -48,7 +48,7 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', '/foo/bar', - '--ticker-interval', '1m', + '--timeframe', '1m', '--timerange', ':100', '--stoplosses=-0.01,-0.10,-0.001' ] @@ -62,8 +62,8 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N assert 'datadir' in config assert config['runmode'] == RunMode.EDGE assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + assert 'timeframe' in config + assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', caplog) assert 'timerange' in config diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 90e047954..f5b3b8909 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -94,7 +94,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca assert 'pair_whitelist' in config['exchange'] assert 'datadir' in config assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'ticker_interval' in config + assert 'timeframe' in config assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog) assert 'position_stacking' not in config @@ -136,8 +136,8 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo assert config['runmode'] == RunMode.HYPEROPT assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + assert 'timeframe' in config + assert log_has('Parameter -i/--ticker-interval detected ... Using timeframe: 1m ...', caplog) assert 'position_stacking' in config @@ -544,7 +544,7 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None: ) patch_exchange(mocker) # Co-test loading timeframe from strategy - del default_conf['ticker_interval'] + del default_conf['timeframe'] default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, From 898def7f6ca3a58163ad7811dca75c547bb13730 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 20:39:01 +0200 Subject: [PATCH 03/54] Remove ticker_interval from exchange --- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 7ef0d7750..3f1cdc568 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -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() diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 6df94f356..0d924882f 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -578,7 +578,7 @@ def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog): ('5m'), ("1m"), ("15m"), ("1h") ]) def test_validate_timeframes(default_conf, mocker, timeframe): - default_conf["ticker_interval"] = timeframe + default_conf["timeframe"] = timeframe api_mock = MagicMock() id_mock = PropertyMock(return_value='test_exchange') type(api_mock).id = id_mock @@ -596,7 +596,7 @@ def test_validate_timeframes(default_conf, mocker, timeframe): def test_validate_timeframes_failed(default_conf, mocker): - default_conf["ticker_interval"] = "3m" + default_conf["timeframe"] = "3m" api_mock = MagicMock() id_mock = PropertyMock(return_value='test_exchange') type(api_mock).id = id_mock @@ -613,7 +613,7 @@ def test_validate_timeframes_failed(default_conf, mocker): with pytest.raises(OperationalException, match=r"Invalid timeframe '3m'. This exchange supports.*"): Exchange(default_conf) - default_conf["ticker_interval"] = "15s" + default_conf["timeframe"] = "15s" with pytest.raises(OperationalException, match=r"Timeframes < 1m are currently not supported by Freqtrade."): @@ -621,7 +621,7 @@ def test_validate_timeframes_failed(default_conf, mocker): def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker): - default_conf["ticker_interval"] = "3m" + default_conf["timeframe"] = "3m" api_mock = MagicMock() id_mock = PropertyMock(return_value='test_exchange') type(api_mock).id = id_mock @@ -641,7 +641,7 @@ def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker): def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker): - default_conf["ticker_interval"] = "3m" + default_conf["timeframe"] = "3m" api_mock = MagicMock() id_mock = PropertyMock(return_value='test_exchange') type(api_mock).id = id_mock @@ -662,7 +662,7 @@ def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker): def test_validate_timeframes_not_in_config(default_conf, mocker): - del default_conf["ticker_interval"] + del default_conf["timeframe"] api_mock = MagicMock() id_mock = PropertyMock(return_value='test_exchange') type(api_mock).id = id_mock From b2c241e607317baf147806824c838952632e5722 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 20:43:20 +0200 Subject: [PATCH 04/54] Replace ticker_interval in all rpc files --- freqtrade/rpc/rpc.py | 3 ++- freqtrade/rpc/rpc_manager.py | 4 ++-- freqtrade/rpc/telegram.py | 2 +- tests/rpc/test_rpc.py | 2 ++ tests/rpc/test_rpc_apiserver.py | 2 ++ 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 6addf18ba..e7d4a3be8 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -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), diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index 670275991..2cb44fec8 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -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({ diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 7eef18dfc..17354cdb0 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -639,7 +639,7 @@ class Telegram(RPC): f"*Max open Trades:* `{val['max_open_trades']}`\n" f"*Minimum ROI:* `{val['minimal_roi']}`\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']}`" ) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 7b7824310..4950c4ea7 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -66,6 +66,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'max_rate': ANY, 'strategy': ANY, 'ticker_interval': ANY, + 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, 'close_date_hum': None, @@ -120,6 +121,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'max_rate': ANY, 'strategy': ANY, 'ticker_interval': ANY, + 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, 'close_date_hum': None, diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index c7f937b0c..bccd12e18 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -544,6 +544,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'sell_order_status': None, 'strategy': 'DefaultStrategy', 'ticker_interval': 5, + 'timeframe': 5, 'exchange': 'bittrex', }] @@ -659,6 +660,7 @@ def test_api_forcebuy(botclient, mocker, fee): 'sell_order_status': None, 'strategy': None, 'ticker_interval': None, + 'timeframe': None, 'exchange': 'bittrex', } From 950f358982b881d0f8f7b5d4e665506380fe8f90 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 20:47:27 +0200 Subject: [PATCH 05/54] Replace occurances in test files --- tests/commands/test_build_config.py | 4 +- tests/config_test_comments.json | 2 +- tests/data/test_dataprovider.py | 40 +++++++++---------- tests/data/test_history.py | 6 +-- tests/optimize/test_hyperopt.py | 4 +- tests/strategy/test_interface.py | 12 +++--- tests/strategy/test_strategy.py | 8 ++-- tests/test_arguments.py | 2 +- tests/test_configuration.py | 2 +- tests/test_freqtradebot.py | 2 +- tests/test_main.py | 12 +++--- tests/test_persistence.py | 2 + tests/test_plotting.py | 2 +- tests/testdata/backtest-result_test copy.json | 7 ++++ 14 files changed, 57 insertions(+), 48 deletions(-) create mode 100644 tests/testdata/backtest-result_test copy.json diff --git a/tests/commands/test_build_config.py b/tests/commands/test_build_config.py index d4ebe1de2..69b277e3b 100644 --- a/tests/commands/test_build_config.py +++ b/tests/commands/test_build_config.py @@ -44,7 +44,7 @@ def test_start_new_config(mocker, caplog, exchange): 'stake_currency': 'USDT', 'stake_amount': 100, 'fiat_display_currency': 'EUR', - 'ticker_interval': '15m', + 'timeframe': '15m', 'dry_run': True, 'exchange_name': exchange, 'exchange_key': 'sampleKey', @@ -68,7 +68,7 @@ def test_start_new_config(mocker, caplog, exchange): result = rapidjson.loads(wt_mock.call_args_list[0][0][0], parse_mode=rapidjson.PM_COMMENTS | rapidjson.PM_TRAILING_COMMAS) assert result['exchange']['name'] == exchange - assert result['ticker_interval'] == '15m' + assert result['timeframe'] == '15m' def test_start_new_config_exists(mocker, caplog): diff --git a/tests/config_test_comments.json b/tests/config_test_comments.json index 8f41b08fa..b224d7602 100644 --- a/tests/config_test_comments.json +++ b/tests/config_test_comments.json @@ -9,7 +9,7 @@ "fiat_display_currency": "USD", // C++-style comment "amount_reserve_percent" : 0.05, // And more, tabs before this comment "dry_run": false, - "ticker_interval": "5m", + "timeframe": "5m", "trailing_stop": false, "trailing_stop_positive": 0.005, "trailing_stop_positive_offset": 0.0051, diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index c2d6e82f1..718060c5e 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -12,7 +12,7 @@ from tests.conftest import get_patched_exchange def test_ohlcv(mocker, default_conf, ohlcv_history): default_conf["runmode"] = RunMode.DRY_RUN - timeframe = default_conf["ticker_interval"] + timeframe = default_conf["timeframe"] exchange = get_patched_exchange(mocker, default_conf) exchange._klines[("XRP/BTC", timeframe)] = ohlcv_history exchange._klines[("UNITTEST/BTC", timeframe)] = ohlcv_history @@ -53,47 +53,47 @@ def test_historic_ohlcv(mocker, default_conf, ohlcv_history): def test_get_pair_dataframe(mocker, default_conf, ohlcv_history): default_conf["runmode"] = RunMode.DRY_RUN - ticker_interval = default_conf["ticker_interval"] + timeframe = default_conf["timeframe"] exchange = get_patched_exchange(mocker, default_conf) - exchange._klines[("XRP/BTC", ticker_interval)] = ohlcv_history - exchange._klines[("UNITTEST/BTC", ticker_interval)] = ohlcv_history + exchange._klines[("XRP/BTC", timeframe)] = ohlcv_history + exchange._klines[("UNITTEST/BTC", timeframe)] = ohlcv_history dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.DRY_RUN - assert ohlcv_history.equals(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval)) - assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval), DataFrame) - assert dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval) is not ohlcv_history - assert not dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval).empty - assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty + assert ohlcv_history.equals(dp.get_pair_dataframe("UNITTEST/BTC", timeframe)) + assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", timeframe), DataFrame) + assert dp.get_pair_dataframe("UNITTEST/BTC", timeframe) is not ohlcv_history + assert not dp.get_pair_dataframe("UNITTEST/BTC", timeframe).empty + assert dp.get_pair_dataframe("NONESENSE/AAA", timeframe).empty # Test with and without parameter - assert dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval)\ + assert dp.get_pair_dataframe("UNITTEST/BTC", timeframe)\ .equals(dp.get_pair_dataframe("UNITTEST/BTC")) default_conf["runmode"] = RunMode.LIVE dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.LIVE - assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval), DataFrame) - assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty + assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", timeframe), DataFrame) + assert dp.get_pair_dataframe("NONESENSE/AAA", timeframe).empty historymock = MagicMock(return_value=ohlcv_history) mocker.patch("freqtrade.data.dataprovider.load_pair_history", historymock) default_conf["runmode"] = RunMode.BACKTEST dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.BACKTEST - assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval), DataFrame) - # assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty + assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", timeframe), DataFrame) + # assert dp.get_pair_dataframe("NONESENSE/AAA", timeframe).empty def test_available_pairs(mocker, default_conf, ohlcv_history): exchange = get_patched_exchange(mocker, default_conf) - ticker_interval = default_conf["ticker_interval"] - exchange._klines[("XRP/BTC", ticker_interval)] = ohlcv_history - exchange._klines[("UNITTEST/BTC", ticker_interval)] = ohlcv_history + timeframe = default_conf["timeframe"] + exchange._klines[("XRP/BTC", timeframe)] = ohlcv_history + exchange._klines[("UNITTEST/BTC", timeframe)] = ohlcv_history dp = DataProvider(default_conf, exchange) assert len(dp.available_pairs) == 2 - assert dp.available_pairs == [("XRP/BTC", ticker_interval), ("UNITTEST/BTC", ticker_interval), ] + assert dp.available_pairs == [("XRP/BTC", timeframe), ("UNITTEST/BTC", timeframe), ] def test_refresh(mocker, default_conf, ohlcv_history): @@ -101,8 +101,8 @@ def test_refresh(mocker, default_conf, ohlcv_history): mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", refresh_mock) exchange = get_patched_exchange(mocker, default_conf, id="binance") - ticker_interval = default_conf["ticker_interval"] - pairs = [("XRP/BTC", ticker_interval), ("UNITTEST/BTC", ticker_interval)] + timeframe = default_conf["timeframe"] + pairs = [("XRP/BTC", timeframe), ("UNITTEST/BTC", timeframe)] pairs_non_trad = [("ETH/USDT", ticker_interval), ("BTC/TUSD", "1h")] diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 6fd4d9569..c52163bbc 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -354,7 +354,7 @@ def test_init(default_conf, mocker) -> None: assert {} == load_data( datadir=Path(''), pairs=[], - timeframe=default_conf['ticker_interval'] + timeframe=default_conf['timeframe'] ) @@ -363,13 +363,13 @@ def test_init_with_refresh(default_conf, mocker) -> None: refresh_data( datadir=Path(''), pairs=[], - timeframe=default_conf['ticker_interval'], + timeframe=default_conf['timeframe'], exchange=exchange ) assert {} == load_data( datadir=Path(''), pairs=[], - timeframe=default_conf['ticker_interval'] + timeframe=default_conf['timeframe'] ) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index f5b3b8909..4f5b3983a 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -117,7 +117,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo '--config', 'config.json', '--hyperopt', 'DefaultHyperOpt', '--datadir', '/foo/bar', - '--ticker-interval', '1m', + '--timeframe', '1m', '--timerange', ':100', '--enable-position-stacking', '--disable-max-market-positions', @@ -137,7 +137,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) assert 'timeframe' in config - assert log_has('Parameter -i/--ticker-interval detected ... Using timeframe: 1m ...', + assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', caplog) assert 'position_stacking' in config diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index e5539099b..59b4d5902 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -54,12 +54,12 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], + assert (False, False) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() - assert (False, False) == _STRATEGY.get_signal('bar', default_conf['ticker_interval'], + assert (False, False) == _STRATEGY.get_signal('bar', default_conf['timeframe'], []) assert log_has('Empty candle (OHLCV) data for pair bar', caplog) @@ -70,7 +70,7 @@ def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ohlcv_his _STRATEGY, '_analyze_ticker_internal', side_effect=ValueError('xyz') ) - assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], + assert (False, False) == _STRATEGY.get_signal('foo', default_conf['timeframe'], ohlcv_history) assert log_has_re(r'Strategy caused the following exception: xyz.*', caplog) @@ -83,7 +83,7 @@ def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ohlcv_history) ) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], + assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], ohlcv_history) assert log_has('Empty dataframe for pair xyz', caplog) @@ -104,7 +104,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): return_value=mocked_history ) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], + assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], ohlcv_history) assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) @@ -124,7 +124,7 @@ def test_assert_df_raise(default_conf, mocker, caplog, ohlcv_history): _STRATEGY, 'assert_df', side_effect=StrategyError('Dataframe returned...') ) - assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], + assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], ohlcv_history) assert log_has('Unable to analyze candle (OHLCV) data for pair xyz: Dataframe returned...', caplog) diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 13ca68bf0..5cb59cad0 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -106,7 +106,7 @@ def test_strategy(result, default_conf): assert default_conf['stoploss'] == -0.10 assert strategy.ticker_interval == '5m' - assert default_conf['ticker_interval'] == '5m' + assert default_conf['timeframe'] == '5m' df_indicators = strategy.advise_indicators(result, metadata=metadata) assert 'adx' in df_indicators @@ -176,19 +176,19 @@ def test_strategy_override_trailing_stop_positive(caplog, default_conf): caplog) -def test_strategy_override_ticker_interval(caplog, default_conf): +def test_strategy_override_timeframe(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ 'strategy': 'DefaultStrategy', - 'ticker_interval': 60, + 'timeframe': 60, 'stake_currency': 'ETH' }) strategy = StrategyResolver.load_strategy(default_conf) assert strategy.ticker_interval == 60 assert strategy.stake_currency == 'ETH' - assert log_has("Override strategy 'ticker_interval' with value in config file: 60.", + assert log_has("Override strategy 'timeframe' with value in config file: 60.", caplog) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 0052a61d0..457683598 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -131,7 +131,7 @@ def test_parse_args_backtesting_custom() -> None: assert call_args["verbosity"] == 0 assert call_args["command"] == 'backtesting' assert call_args["func"] is not None - assert call_args["ticker_interval"] == '1m' + assert call_args["timeframe"] == '1m' assert type(call_args["strategy_list"]) is list assert len(call_args["strategy_list"]) == 2 diff --git a/tests/test_configuration.py b/tests/test_configuration.py index edcbe4516..05074c258 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -87,7 +87,7 @@ def test_load_config_file_error_range(default_conf, mocker, caplog) -> None: assert isinstance(x, str) assert (x == '{"max_open_trades": 1, "stake_currency": "BTC", ' '"stake_amount": .001, "fiat_display_currency": "USD", ' - '"ticker_interval": "5m", "dry_run": true, ') + '"timeframe": "5m", "dry_run": true, ') def test__args_to_config(caplog): diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 5e951b585..442917bb6 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -924,7 +924,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: assert refresh_mock.call_count == 1 assert ("BTC/ETH", "1m") in refresh_mock.call_args[0][0] assert ("ETH/USDT", "1h") in refresh_mock.call_args[0][0] - assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0] + assert ("ETH/BTC", default_conf["timeframe"]) in refresh_mock.call_args[0][0] @pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", [ diff --git a/tests/test_main.py b/tests/test_main.py index 11d0ede3a..5700df1ae 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -35,12 +35,12 @@ def test_parse_args_backtesting(mocker) -> None: main(['backtesting']) assert backtesting_mock.call_count == 1 call_args = backtesting_mock.call_args[0][0] - assert call_args["config"] == ['config.json'] - assert call_args["verbosity"] == 0 - assert call_args["command"] == 'backtesting' - assert call_args["func"] is not None - assert callable(call_args["func"]) - assert call_args["ticker_interval"] is None + assert call_args['config'] == ['config.json'] + assert call_args['verbosity'] == 0 + assert call_args['command'] == 'backtesting' + assert call_args['func'] is not None + assert callable(call_args['func']) + assert call_args['timeframe'] is None def test_main_start_hyperopt(mocker) -> None: diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 3b1041fd8..c15936cf6 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -772,6 +772,7 @@ def test_to_json(default_conf, fee): 'max_rate': None, 'strategy': None, 'ticker_interval': None, + 'timeframe': None, 'exchange': 'bittrex', } @@ -829,6 +830,7 @@ def test_to_json(default_conf, fee): 'sell_order_status': None, 'strategy': None, 'ticker_interval': None, + 'timeframe': None, 'exchange': 'bittrex', } diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 5bb113784..af872d0c1 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -47,7 +47,7 @@ def generate_empty_figure(): def test_init_plotscript(default_conf, mocker, testdatadir): default_conf['timerange'] = "20180110-20180112" default_conf['trade_source'] = "file" - default_conf['ticker_interval'] = "5m" + default_conf['timeframe'] = "5m" default_conf["datadir"] = testdatadir default_conf['exportfilename'] = testdatadir / "backtest-result_test.json" ret = init_plotscript(default_conf) diff --git a/tests/testdata/backtest-result_test copy.json b/tests/testdata/backtest-result_test copy.json new file mode 100644 index 000000000..0395830d4 --- /dev/null +++ b/tests/testdata/backtest-result_test copy.json @@ -0,0 +1,7 @@ +{ + "ASDF": {, + "trades": [], + "metrics":[], + } +} +[["TRX/BTC",0.03990025,1515568500.0,1515568800.0,27,5,9.64e-05,0.00010074887218045112,false,"roi"],["ADA/BTC",0.03990025,1515568500.0,1515569400.0,27,15,4.756e-05,4.9705563909774425e-05,false,"roi"],["XLM/BTC",0.03990025,1515569100.0,1515569700.0,29,10,3.339e-05,3.489631578947368e-05,false,"roi"],["TRX/BTC",0.03990025,1515569100.0,1515570000.0,29,15,9.696e-05,0.00010133413533834584,false,"roi"],["ETH/BTC",-0.0,1515569700.0,1515573300.0,31,60,0.0943,0.09477268170426063,false,"roi"],["XMR/BTC",0.00997506,1515570000.0,1515571800.0,32,30,0.02719607,0.02760503345864661,false,"roi"],["ZEC/BTC",0.0,1515572100.0,1515578100.0,39,100,0.04634952,0.046581848421052625,false,"roi"],["NXT/BTC",-0.0,1515595500.0,1515599400.0,117,65,3.066e-05,3.081368421052631e-05,false,"roi"],["LTC/BTC",0.0,1515602100.0,1515604500.0,139,40,0.0168999,0.016984611278195488,false,"roi"],["ETH/BTC",-0.0,1515602400.0,1515604800.0,140,40,0.09132568,0.0917834528320802,false,"roi"],["ETH/BTC",-0.0,1515610200.0,1515613500.0,166,55,0.08898003,0.08942604518796991,false,"roi"],["ETH/BTC",0.0,1515622500.0,1515625200.0,207,45,0.08560008,0.08602915308270676,false,"roi"],["ETC/BTC",0.00997506,1515624600.0,1515626400.0,214,30,0.00249083,0.0025282860902255634,false,"roi"],["NXT/BTC",-0.0,1515626100.0,1515629700.0,219,60,3.022e-05,3.037147869674185e-05,false,"roi"],["ETC/BTC",0.01995012,1515627600.0,1515629100.0,224,25,0.002437,0.0024980776942355883,false,"roi"],["ZEC/BTC",0.00997506,1515628800.0,1515630900.0,228,35,0.04771803,0.04843559436090225,false,"roi"],["XLM/BTC",-0.10448878,1515642000.0,1515644700.0,272,45,3.651e-05,3.2859000000000005e-05,false,"stop_loss"],["ETH/BTC",0.00997506,1515642900.0,1515644700.0,275,30,0.08824105,0.08956798308270676,false,"roi"],["ETC/BTC",-0.0,1515643200.0,1515646200.0,276,50,0.00243,0.002442180451127819,false,"roi"],["ZEC/BTC",0.01995012,1515645000.0,1515646500.0,282,25,0.04545064,0.046589753784461146,false,"roi"],["XLM/BTC",0.01995012,1515645000.0,1515646200.0,282,20,3.372e-05,3.456511278195488e-05,false,"roi"],["XMR/BTC",0.01995012,1515646500.0,1515647700.0,287,20,0.02644,0.02710265664160401,false,"roi"],["ETH/BTC",-0.0,1515669600.0,1515672000.0,364,40,0.08812,0.08856170426065162,false,"roi"],["XMR/BTC",-0.0,1515670500.0,1515672900.0,367,40,0.02683577,0.026970285137844607,false,"roi"],["ADA/BTC",0.01995012,1515679200.0,1515680700.0,396,25,4.919e-05,5.04228320802005e-05,false,"roi"],["ETH/BTC",-0.0,1515698700.0,1515702900.0,461,70,0.08784896,0.08828930566416039,false,"roi"],["ADA/BTC",-0.0,1515710100.0,1515713400.0,499,55,5.105e-05,5.130588972431077e-05,false,"roi"],["XLM/BTC",0.00997506,1515711300.0,1515713100.0,503,30,3.96e-05,4.019548872180451e-05,false,"roi"],["NXT/BTC",-0.0,1515711300.0,1515713700.0,503,40,2.885e-05,2.899461152882205e-05,false,"roi"],["XMR/BTC",0.00997506,1515713400.0,1515715500.0,510,35,0.02645,0.026847744360902256,false,"roi"],["ZEC/BTC",-0.0,1515714900.0,1515719700.0,515,80,0.048,0.04824060150375939,false,"roi"],["XLM/BTC",0.01995012,1515791700.0,1515793200.0,771,25,4.692e-05,4.809593984962405e-05,false,"roi"],["ETC/BTC",-0.0,1515804900.0,1515824400.0,815,325,0.00256966,0.0025825405012531327,false,"roi"],["ADA/BTC",0.0,1515840900.0,1515843300.0,935,40,6.262e-05,6.293388471177944e-05,false,"roi"],["XLM/BTC",0.0,1515848700.0,1516025400.0,961,2945,4.73e-05,4.753709273182957e-05,false,"roi"],["ADA/BTC",-0.0,1515850200.0,1515854700.0,966,75,6.063e-05,6.0933909774436085e-05,false,"roi"],["TRX/BTC",-0.0,1515850800.0,1515886200.0,968,590,0.00011082,0.00011137548872180449,false,"roi"],["ADA/BTC",-0.0,1515856500.0,1515858900.0,987,40,5.93e-05,5.9597243107769415e-05,false,"roi"],["ZEC/BTC",-0.0,1515861000.0,1515863400.0,1002,40,0.04850003,0.04874313791979949,false,"roi"],["ETH/BTC",-0.0,1515881100.0,1515911100.0,1069,500,0.09825019,0.09874267215538847,false,"roi"],["ADA/BTC",0.0,1515889200.0,1515970500.0,1096,1355,6.018e-05,6.048165413533834e-05,false,"roi"],["ETH/BTC",-0.0,1515933900.0,1515936300.0,1245,40,0.09758999,0.0980791628822055,false,"roi"],["ETC/BTC",0.00997506,1515943800.0,1515945600.0,1278,30,0.00311,0.0031567669172932328,false,"roi"],["ETC/BTC",-0.0,1515962700.0,1515968100.0,1341,90,0.00312401,0.003139669197994987,false,"roi"],["LTC/BTC",0.0,1515972900.0,1515976200.0,1375,55,0.0174679,0.017555458395989976,false,"roi"],["DASH/BTC",-0.0,1515973500.0,1515975900.0,1377,40,0.07346846,0.07383672295739348,false,"roi"],["ETH/BTC",-0.0,1515983100.0,1515985500.0,1409,40,0.097994,0.09848519799498745,false,"roi"],["ETH/BTC",-0.0,1516000800.0,1516003200.0,1468,40,0.09659,0.09707416040100249,false,"roi"],["TRX/BTC",0.00997506,1516004400.0,1516006500.0,1480,35,9.987e-05,0.00010137180451127818,false,"roi"],["ETH/BTC",0.0,1516018200.0,1516071000.0,1526,880,0.0948969,0.09537257368421052,false,"roi"],["DASH/BTC",-0.0,1516025400.0,1516038000.0,1550,210,0.071,0.07135588972431077,false,"roi"],["ZEC/BTC",-0.0,1516026600.0,1516029000.0,1554,40,0.04600501,0.046235611553884705,false,"roi"],["TRX/BTC",-0.0,1516039800.0,1516044300.0,1598,75,9.438e-05,9.485308270676691e-05,false,"roi"],["XMR/BTC",-0.0,1516041300.0,1516043700.0,1603,40,0.03040001,0.030552391002506264,false,"roi"],["ADA/BTC",-0.10448878,1516047900.0,1516091100.0,1625,720,5.837e-05,5.2533e-05,false,"stop_loss"],["ZEC/BTC",-0.0,1516048800.0,1516053600.0,1628,80,0.046036,0.04626675689223057,false,"roi"],["ETC/BTC",-0.0,1516062600.0,1516065000.0,1674,40,0.0028685,0.0028828784461152877,false,"roi"],["DASH/BTC",0.0,1516065300.0,1516070100.0,1683,80,0.06731755,0.0676549813283208,false,"roi"],["ETH/BTC",0.0,1516088700.0,1516092000.0,1761,55,0.09217614,0.09263817578947368,false,"roi"],["LTC/BTC",0.01995012,1516091700.0,1516092900.0,1771,20,0.0165,0.016913533834586467,false,"roi"],["TRX/BTC",0.03990025,1516091700.0,1516092000.0,1771,5,7.953e-05,8.311781954887218e-05,false,"roi"],["ZEC/BTC",-0.0,1516092300.0,1516096200.0,1773,65,0.045202,0.04542857644110275,false,"roi"],["ADA/BTC",0.00997506,1516094100.0,1516095900.0,1779,30,5.248e-05,5.326917293233082e-05,false,"roi"],["XMR/BTC",0.0,1516094100.0,1516096500.0,1779,40,0.02892318,0.02906815834586466,false,"roi"],["ADA/BTC",0.01995012,1516096200.0,1516097400.0,1786,20,5.158e-05,5.287273182957392e-05,false,"roi"],["ZEC/BTC",0.00997506,1516097100.0,1516099200.0,1789,35,0.04357584,0.044231115789473675,false,"roi"],["XMR/BTC",0.00997506,1516097100.0,1516098900.0,1789,30,0.02828232,0.02870761804511278,false,"roi"],["ADA/BTC",0.00997506,1516110300.0,1516112400.0,1833,35,5.362e-05,5.4426315789473676e-05,false,"roi"],["ADA/BTC",-0.0,1516123800.0,1516127100.0,1878,55,5.302e-05,5.328576441102756e-05,false,"roi"],["ETH/BTC",0.00997506,1516126500.0,1516128300.0,1887,30,0.09129999,0.09267292218045112,false,"roi"],["XLM/BTC",0.01995012,1516126500.0,1516127700.0,1887,20,3.808e-05,3.903438596491228e-05,false,"roi"],["XMR/BTC",0.00997506,1516129200.0,1516131000.0,1896,30,0.02811012,0.028532828571428567,false,"roi"],["ETC/BTC",-0.10448878,1516137900.0,1516141500.0,1925,60,0.00258379,0.002325411,false,"stop_loss"],["NXT/BTC",-0.10448878,1516137900.0,1516142700.0,1925,80,2.559e-05,2.3031e-05,false,"stop_loss"],["TRX/BTC",-0.10448878,1516138500.0,1516141500.0,1927,50,7.62e-05,6.858e-05,false,"stop_loss"],["LTC/BTC",0.03990025,1516141800.0,1516142400.0,1938,10,0.0151,0.015781203007518795,false,"roi"],["ETC/BTC",0.03990025,1516141800.0,1516142100.0,1938,5,0.00229844,0.002402129022556391,false,"roi"],["ETC/BTC",0.03990025,1516142400.0,1516142700.0,1940,5,0.00235676,0.00246308,false,"roi"],["DASH/BTC",0.01995012,1516142700.0,1516143900.0,1941,20,0.0630692,0.06464988170426066,false,"roi"],["NXT/BTC",0.03990025,1516143000.0,1516143300.0,1942,5,2.2e-05,2.2992481203007514e-05,false,"roi"],["ADA/BTC",0.00997506,1516159800.0,1516161600.0,1998,30,4.974e-05,5.048796992481203e-05,false,"roi"],["TRX/BTC",0.01995012,1516161300.0,1516162500.0,2003,20,7.108e-05,7.28614536340852e-05,false,"roi"],["ZEC/BTC",-0.0,1516181700.0,1516184100.0,2071,40,0.04327,0.04348689223057644,false,"roi"],["ADA/BTC",-0.0,1516184400.0,1516208400.0,2080,400,4.997e-05,5.022047619047618e-05,false,"roi"],["DASH/BTC",-0.0,1516185000.0,1516188300.0,2082,55,0.06836818,0.06871087764411027,false,"roi"],["XLM/BTC",-0.0,1516185000.0,1516187400.0,2082,40,3.63e-05,3.648195488721804e-05,false,"roi"],["XMR/BTC",-0.0,1516192200.0,1516226700.0,2106,575,0.0281,0.02824085213032581,false,"roi"],["ETH/BTC",-0.0,1516192500.0,1516208100.0,2107,260,0.08651001,0.08694364413533832,false,"roi"],["ADA/BTC",-0.0,1516251600.0,1516254900.0,2304,55,5.633e-05,5.6612355889724306e-05,false,"roi"],["DASH/BTC",0.00997506,1516252800.0,1516254900.0,2308,35,0.06988494,0.07093584135338346,false,"roi"],["ADA/BTC",-0.0,1516260900.0,1516263300.0,2335,40,5.545e-05,5.572794486215538e-05,false,"roi"],["LTC/BTC",-0.0,1516266000.0,1516268400.0,2352,40,0.01633527,0.016417151052631574,false,"roi"],["ETC/BTC",-0.0,1516293600.0,1516296000.0,2444,40,0.00269734,0.0027108605012531326,false,"roi"],["XLM/BTC",0.01995012,1516298700.0,1516300200.0,2461,25,4.475e-05,4.587155388471177e-05,false,"roi"],["NXT/BTC",0.00997506,1516299900.0,1516301700.0,2465,30,2.79e-05,2.8319548872180444e-05,false,"roi"],["ZEC/BTC",0.0,1516306200.0,1516308600.0,2486,40,0.04439326,0.04461578260651629,false,"roi"],["XLM/BTC",0.0,1516311000.0,1516322100.0,2502,185,4.49e-05,4.51250626566416e-05,false,"roi"],["XMR/BTC",-0.0,1516312500.0,1516338300.0,2507,430,0.02855,0.028693107769423555,false,"roi"],["ADA/BTC",0.0,1516313400.0,1516315800.0,2510,40,5.796e-05,5.8250526315789473e-05,false,"roi"],["ZEC/BTC",0.0,1516319400.0,1516321800.0,2530,40,0.04340323,0.04362079005012531,false,"roi"],["ZEC/BTC",0.0,1516380300.0,1516383300.0,2733,50,0.04454455,0.04476783095238095,false,"roi"],["ADA/BTC",-0.0,1516382100.0,1516391700.0,2739,160,5.62e-05,5.648170426065162e-05,false,"roi"],["XLM/BTC",-0.0,1516382400.0,1516392900.0,2740,175,4.339e-05,4.360749373433584e-05,false,"roi"],["TRX/BTC",0.0,1516423500.0,1516469700.0,2877,770,0.0001009,0.00010140576441102757,false,"roi"],["ETC/BTC",-0.0,1516423800.0,1516461300.0,2878,625,0.00270505,0.002718609147869674,false,"roi"],["XMR/BTC",-0.0,1516423800.0,1516431600.0,2878,130,0.03000002,0.030150396040100245,false,"roi"],["ADA/BTC",-0.0,1516438800.0,1516441200.0,2928,40,5.46e-05,5.4873684210526304e-05,false,"roi"],["XMR/BTC",-0.10448878,1516472700.0,1516852200.0,3041,6325,0.03082222,0.027739998000000002,false,"stop_loss"],["ETH/BTC",-0.0,1516487100.0,1516490100.0,3089,50,0.08969999,0.09014961401002504,false,"roi"],["LTC/BTC",0.0,1516503000.0,1516545000.0,3142,700,0.01632501,0.01640683962406015,false,"roi"],["DASH/BTC",-0.0,1516530000.0,1516532400.0,3232,40,0.070538,0.07089157393483708,false,"roi"],["ADA/BTC",-0.0,1516549800.0,1516560300.0,3298,175,5.301e-05,5.3275714285714276e-05,false,"roi"],["XLM/BTC",0.0,1516551600.0,1516554000.0,3304,40,3.955e-05,3.9748245614035085e-05,false,"roi"],["ETC/BTC",0.00997506,1516569300.0,1516571100.0,3363,30,0.00258505,0.002623922932330827,false,"roi"],["XLM/BTC",-0.0,1516569300.0,1516571700.0,3363,40,3.903e-05,3.922563909774435e-05,false,"roi"],["ADA/BTC",-0.0,1516581300.0,1516617300.0,3403,600,5.236e-05,5.262245614035087e-05,false,"roi"],["TRX/BTC",0.0,1516584600.0,1516587000.0,3414,40,9.028e-05,9.073253132832079e-05,false,"roi"],["ETC/BTC",-0.0,1516623900.0,1516631700.0,3545,130,0.002687,0.002700468671679198,false,"roi"],["XLM/BTC",-0.0,1516626900.0,1516629300.0,3555,40,4.168e-05,4.1888922305764405e-05,false,"roi"],["TRX/BTC",0.00997506,1516629600.0,1516631400.0,3564,30,8.821e-05,8.953646616541353e-05,false,"roi"],["ADA/BTC",-0.0,1516636500.0,1516639200.0,3587,45,5.172e-05,5.1979248120300745e-05,false,"roi"],["NXT/BTC",0.01995012,1516637100.0,1516638300.0,3589,20,3.026e-05,3.101839598997494e-05,false,"roi"],["DASH/BTC",0.0,1516650600.0,1516666200.0,3634,260,0.07064,0.07099408521303258,false,"roi"],["LTC/BTC",0.0,1516656300.0,1516658700.0,3653,40,0.01644483,0.01652726022556391,false,"roi"],["XLM/BTC",0.00997506,1516665900.0,1516667700.0,3685,30,4.331e-05,4.3961278195488714e-05,false,"roi"],["NXT/BTC",0.01995012,1516672200.0,1516673700.0,3706,25,3.2e-05,3.2802005012531326e-05,false,"roi"],["ETH/BTC",0.0,1516681500.0,1516684500.0,3737,50,0.09167706,0.09213659413533835,false,"roi"],["DASH/BTC",0.0,1516692900.0,1516698000.0,3775,85,0.0692498,0.06959691679197995,false,"roi"],["NXT/BTC",0.0,1516704600.0,1516712700.0,3814,135,3.182e-05,3.197949874686716e-05,false,"roi"],["ZEC/BTC",-0.0,1516705500.0,1516723500.0,3817,300,0.04088,0.04108491228070175,false,"roi"],["ADA/BTC",-0.0,1516719300.0,1516721700.0,3863,40,5.15e-05,5.175814536340851e-05,false,"roi"],["ETH/BTC",0.0,1516725300.0,1516752300.0,3883,450,0.09071698,0.09117170170426064,false,"roi"],["NXT/BTC",-0.0,1516728300.0,1516733100.0,3893,80,3.128e-05,3.1436791979949865e-05,false,"roi"],["TRX/BTC",-0.0,1516738500.0,1516744800.0,3927,105,9.555e-05,9.602894736842104e-05,false,"roi"],["ZEC/BTC",-0.0,1516746600.0,1516749000.0,3954,40,0.04080001,0.041004521328320796,false,"roi"],["ADA/BTC",-0.0,1516751400.0,1516764900.0,3970,225,5.163e-05,5.1888796992481196e-05,false,"roi"],["ZEC/BTC",0.0,1516753200.0,1516758600.0,3976,90,0.04040781,0.04061035541353383,false,"roi"],["ADA/BTC",-0.0,1516776300.0,1516778700.0,4053,40,5.132e-05,5.157724310776942e-05,false,"roi"],["ADA/BTC",0.03990025,1516803300.0,1516803900.0,4143,10,5.198e-05,5.432496240601503e-05,false,"roi"],["NXT/BTC",-0.0,1516805400.0,1516811700.0,4150,105,3.054e-05,3.069308270676692e-05,false,"roi"],["TRX/BTC",0.0,1516806600.0,1516810500.0,4154,65,9.263e-05,9.309431077694235e-05,false,"roi"],["ADA/BTC",-0.0,1516833600.0,1516836300.0,4244,45,5.514e-05,5.5416390977443596e-05,false,"roi"],["XLM/BTC",0.0,1516841400.0,1516843800.0,4270,40,4.921e-05,4.9456666666666664e-05,false,"roi"],["ETC/BTC",0.0,1516868100.0,1516882500.0,4359,240,0.0026,0.002613032581453634,false,"roi"],["XMR/BTC",-0.0,1516875900.0,1516896900.0,4385,350,0.02799871,0.028139054411027563,false,"roi"],["ZEC/BTC",-0.0,1516878000.0,1516880700.0,4392,45,0.04078902,0.0409934762406015,false,"roi"],["NXT/BTC",-0.0,1516885500.0,1516887900.0,4417,40,2.89e-05,2.904486215538847e-05,false,"roi"],["ZEC/BTC",-0.0,1516886400.0,1516889100.0,4420,45,0.041103,0.041309030075187964,false,"roi"],["XLM/BTC",0.00997506,1516895100.0,1516896900.0,4449,30,5.428e-05,5.5096240601503756e-05,false,"roi"],["XLM/BTC",-0.0,1516902300.0,1516922100.0,4473,330,5.414e-05,5.441137844611528e-05,false,"roi"],["ZEC/BTC",-0.0,1516914900.0,1516917300.0,4515,40,0.04140777,0.0416153277443609,false,"roi"],["ETC/BTC",0.0,1516932300.0,1516934700.0,4573,40,0.00254309,0.002555837318295739,false,"roi"],["ADA/BTC",-0.0,1516935300.0,1516979400.0,4583,735,5.607e-05,5.6351052631578935e-05,false,"roi"],["ETC/BTC",0.0,1516947000.0,1516958700.0,4622,195,0.00253806,0.0025507821052631577,false,"roi"],["ZEC/BTC",-0.0,1516951500.0,1516960500.0,4637,150,0.0415,0.04170802005012531,false,"roi"],["XLM/BTC",0.00997506,1516960500.0,1516962300.0,4667,30,5.321e-05,5.401015037593984e-05,false,"roi"],["XMR/BTC",-0.0,1516982700.0,1516985100.0,4741,40,0.02772046,0.02785940967418546,false,"roi"],["ETH/BTC",0.0,1517009700.0,1517012100.0,4831,40,0.09461341,0.09508766268170425,false,"roi"],["XLM/BTC",-0.0,1517013300.0,1517016600.0,4843,55,5.615e-05,5.643145363408521e-05,false,"roi"],["ADA/BTC",-0.07877175,1517013900.0,1517287500.0,4845,4560,5.556e-05,5.144e-05,true,"force_sell"],["DASH/BTC",-0.0,1517020200.0,1517052300.0,4866,535,0.06900001,0.06934587471177944,false,"roi"],["ETH/BTC",-0.0,1517034300.0,1517036700.0,4913,40,0.09449985,0.09497353345864659,false,"roi"],["ZEC/BTC",-0.04815133,1517046000.0,1517287200.0,4952,4020,0.0410697,0.03928809,true,"force_sell"],["XMR/BTC",-0.0,1517053500.0,1517056200.0,4977,45,0.0285,0.02864285714285714,false,"roi"],["XMR/BTC",-0.0,1517056500.0,1517066700.0,4987,170,0.02866372,0.02880739779448621,false,"roi"],["ETH/BTC",-0.0,1517068200.0,1517071800.0,5026,60,0.095381,0.09585910025062655,false,"roi"],["DASH/BTC",-0.0,1517072700.0,1517075100.0,5041,40,0.06759092,0.06792972160401002,false,"roi"],["ETC/BTC",-0.0,1517096400.0,1517101500.0,5120,85,0.00258501,0.002597967443609022,false,"roi"],["DASH/BTC",-0.0,1517106300.0,1517127000.0,5153,345,0.06698502,0.0673207845112782,false,"roi"],["DASH/BTC",-0.0,1517135100.0,1517157000.0,5249,365,0.0677177,0.06805713709273183,false,"roi"],["XLM/BTC",0.0,1517171700.0,1517175300.0,5371,60,5.215e-05,5.2411403508771925e-05,false,"roi"],["ETC/BTC",0.00997506,1517176800.0,1517178600.0,5388,30,0.00273809,0.002779264285714285,false,"roi"],["ETC/BTC",0.00997506,1517184000.0,1517185800.0,5412,30,0.00274632,0.002787618045112782,false,"roi"],["LTC/BTC",0.0,1517192100.0,1517194800.0,5439,45,0.01622478,0.016306107218045113,false,"roi"],["DASH/BTC",-0.0,1517195100.0,1517197500.0,5449,40,0.069,0.06934586466165413,false,"roi"],["TRX/BTC",-0.0,1517203200.0,1517208900.0,5476,95,8.755e-05,8.798884711779448e-05,false,"roi"],["DASH/BTC",-0.0,1517209200.0,1517253900.0,5496,745,0.06825763,0.06859977350877192,false,"roi"],["DASH/BTC",-0.0,1517255100.0,1517257500.0,5649,40,0.06713892,0.06747545593984962,false,"roi"],["TRX/BTC",-0.0199116,1517268600.0,1517287500.0,5694,315,8.934e-05,8.8e-05,true,"force_sell"]] From 18913db99267a699a89b9fdddc6509980cff6775 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 20:47:36 +0200 Subject: [PATCH 06/54] Replace ticker_interval with timeframe in sample configs --- config.json.example | 2 +- config_binance.json.example | 2 +- config_full.json.example | 2 +- config_kraken.json.example | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config.json.example b/config.json.example index d37a6b336..545fcf1d4 100644 --- a/config.json.example +++ b/config.json.example @@ -4,7 +4,7 @@ "stake_amount": 0.05, "tradable_balance_ratio": 0.99, "fiat_display_currency": "USD", - "ticker_interval": "5m", + "timeframe": "5m", "dry_run": false, "cancel_open_orders_on_exit": false, "trailing_stop": false, diff --git a/config_binance.json.example b/config_binance.json.example index 5d7b6b656..98dada647 100644 --- a/config_binance.json.example +++ b/config_binance.json.example @@ -4,7 +4,7 @@ "stake_amount": 0.05, "tradable_balance_ratio": 0.99, "fiat_display_currency": "USD", - "ticker_interval": "5m", + "timeframe": "5m", "dry_run": true, "cancel_open_orders_on_exit": false, "trailing_stop": false, diff --git a/config_full.json.example b/config_full.json.example index 481742817..66b0d6a0d 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -9,7 +9,7 @@ "last_stake_amount_min_ratio": 0.5, "dry_run": false, "cancel_open_orders_on_exit": false, - "ticker_interval": "5m", + "timeframe": "5m", "trailing_stop": false, "trailing_stop_positive": 0.005, "trailing_stop_positive_offset": 0.0051, diff --git a/config_kraken.json.example b/config_kraken.json.example index 54fbf4a00..602e2ccf3 100644 --- a/config_kraken.json.example +++ b/config_kraken.json.example @@ -4,7 +4,7 @@ "stake_amount": 10, "tradable_balance_ratio": 0.99, "fiat_display_currency": "EUR", - "ticker_interval": "5m", + "timeframe": "5m", "dry_run": true, "cancel_open_orders_on_exit": false, "trailing_stop": false, From cadc50ce9b7dbedf50478cbd9431e5bdffa4ac8c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 20:49:40 +0200 Subject: [PATCH 07/54] Replace more occurances of ticker_interval with timeframe --- freqtrade/commands/pairlist_commands.py | 2 +- freqtrade/data/converter.py | 4 ++-- freqtrade/data/dataprovider.py | 4 ++-- freqtrade/freqtradebot.py | 6 +++--- freqtrade/optimize/backtesting.py | 6 +++--- freqtrade/optimize/hyperopt_interface.py | 8 +++++--- freqtrade/pairlist/pairlistmanager.py | 4 ++-- freqtrade/plot/plotting.py | 6 +++--- freqtrade/resolvers/hyperopt_resolver.py | 5 +++-- freqtrade/resolvers/strategy_resolver.py | 2 +- freqtrade/strategy/interface.py | 2 +- freqtrade/templates/base_strategy.py.j2 | 4 ++-- tests/strategy/test_strategy.py | 2 +- 13 files changed, 29 insertions(+), 26 deletions(-) diff --git a/freqtrade/commands/pairlist_commands.py b/freqtrade/commands/pairlist_commands.py index bf0b217a5..dffe0c82e 100644 --- a/freqtrade/commands/pairlist_commands.py +++ b/freqtrade/commands/pairlist_commands.py @@ -25,7 +25,7 @@ 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 + # Do not use timeframe set in the config pairlists = PairListManager(exchange, config) pairlists.refresh_pairlist() results[curr] = pairlists.whitelist diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 0ef7955a4..cfc7bc903 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -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)) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index a01344364..058ca42da 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -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'] ) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d4afa1d60..627971c31 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -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']) + ticker_interval=timeframe_to_minutes(self.config['timeframe']) ) # Update fees if order is closed @@ -780,7 +780,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 @@ -1090,7 +1090,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) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 3bf211d99..9a48338f1 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -94,10 +94,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 diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index b3cedef2c..20209d8a9 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -37,7 +37,8 @@ class IHyperOpt(ABC): self.config = config # Assign ticker_interval to be used in hyperopt - IHyperOpt.ticker_interval = str(config['ticker_interval']) + IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECTED + IHyperOpt.timeframe = str(config['timeframe']) @staticmethod def buy_strategy_generator(params: Dict[str, Any]) -> Callable: @@ -218,9 +219,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'] diff --git a/freqtrade/pairlist/pairlistmanager.py b/freqtrade/pairlist/pairlistmanager.py index f532f2cd0..81e52768e 100644 --- a/freqtrade/pairlist/pairlistmanager.py +++ b/freqtrade/pairlist/pairlistmanager.py @@ -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] diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index f1d114e2b..f9e526967 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -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) diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index ddf461252..633363134 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -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( diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index abd6a4195..99cf2d785 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -54,7 +54,7 @@ class StrategyResolver(IResolver): # 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), diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index ed2344a53..17c4aa5ca 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -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 diff --git a/freqtrade/templates/base_strategy.py.j2 b/freqtrade/templates/base_strategy.py.j2 index c37164568..ce2c6d5c0 100644 --- a/freqtrade/templates/base_strategy.py.j2 +++ b/freqtrade/templates/base_strategy.py.j2 @@ -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 diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 5cb59cad0..59ce8c5b8 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -186,7 +186,7 @@ def test_strategy_override_timeframe(caplog, default_conf): }) strategy = StrategyResolver.load_strategy(default_conf) - assert strategy.ticker_interval == 60 + assert strategy.timeframe == 60 assert strategy.stake_currency == 'ETH' assert log_has("Override strategy 'timeframe' with value in config file: 60.", caplog) From 388573800c673e1fe383fd83938c85c06fa49db2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 20:52:33 +0200 Subject: [PATCH 08/54] Update configuration messages --- freqtrade/configuration/configuration.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 7edd9bca1..139e42084 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -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: {} ...') From 947903a4acac05fbda301e0dc90040488a772b28 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 09:36:04 +0200 Subject: [PATCH 09/54] Use timeframe from within strategy --- freqtrade/edge/edge_positioning.py | 4 ++-- freqtrade/freqtradebot.py | 8 ++++---- tests/edge/test_edge.py | 10 +++++----- tests/strategy/strats/default_strategy.py | 2 +- tests/strategy/test_default_strategy.py | 1 + tests/strategy/test_strategy.py | 1 + tests/test_configuration.py | 12 ++++++------ 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/freqtrade/edge/edge_positioning.py b/freqtrade/edge/edge_positioning.py index c19d4552a..dd4ea35bb 100644 --- a/freqtrade/edge/edge_positioning.py +++ b/freqtrade/edge/edge_positioning.py @@ -100,14 +100,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'), diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 627971c31..c52fe18d1 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -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) @@ -696,8 +696,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): # logger.debug('Order book %s',orderBook) diff --git a/tests/edge/test_edge.py b/tests/edge/test_edge.py index 163ceff4b..cf9cb6fe1 100644 --- a/tests/edge/test_edge.py +++ b/tests/edge/test_edge.py @@ -27,7 +27,7 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, #################################################################### tests_start_time = arrow.get(2018, 10, 3) -ticker_interval_in_minute = 60 +timeframe_in_minute = 60 _ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7} # Helpers for this test file @@ -49,7 +49,7 @@ def _build_dataframe(buy_ohlc_sell_matrice): 'date': tests_start_time.shift( minutes=( ohlc[0] * - ticker_interval_in_minute)).timestamp * + timeframe_in_minute)).timestamp * 1000, 'buy': ohlc[1], 'open': ohlc[2], @@ -70,7 +70,7 @@ def _build_dataframe(buy_ohlc_sell_matrice): def _time_on_candle(number): return np.datetime64(tests_start_time.shift( - minutes=(number * ticker_interval_in_minute)).timestamp * 1000, 'ms') + minutes=(number * timeframe_in_minute)).timestamp * 1000, 'ms') # End helper functions @@ -262,7 +262,7 @@ def mocked_load_data(datadir, pairs=[], timeframe='0m', NEOBTC = [ [ - tests_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000, + tests_start_time.shift(minutes=(x * timeframe_in_minute)).timestamp * 1000, math.sin(x * hz) / 1000 + base, math.sin(x * hz) / 1000 + base + 0.0001, math.sin(x * hz) / 1000 + base - 0.0001, @@ -274,7 +274,7 @@ def mocked_load_data(datadir, pairs=[], timeframe='0m', base = 0.002 LTCBTC = [ [ - tests_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000, + tests_start_time.shift(minutes=(x * timeframe_in_minute)).timestamp * 1000, math.sin(x * hz) / 1000 + base, math.sin(x * hz) / 1000 + base + 0.0001, math.sin(x * hz) / 1000 + base - 0.0001, diff --git a/tests/strategy/strats/default_strategy.py b/tests/strategy/strats/default_strategy.py index 7ea55d3f9..98842ff7c 100644 --- a/tests/strategy/strats/default_strategy.py +++ b/tests/strategy/strats/default_strategy.py @@ -29,7 +29,7 @@ class DefaultStrategy(IStrategy): stoploss = -0.10 # Optimal ticker interval for the strategy - ticker_interval = '5m' + timeframe = '5m' # Optional order type mapping order_types = { diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py index 0b8ea9f85..315f80440 100644 --- a/tests/strategy/test_default_strategy.py +++ b/tests/strategy/test_default_strategy.py @@ -19,6 +19,7 @@ def test_default_strategy(result): assert type(strategy.minimal_roi) is dict assert type(strategy.stoploss) is float assert type(strategy.ticker_interval) is str + assert type(strategy.timeframe) is str indicators = strategy.populate_indicators(result, metadata) assert type(indicators) is DataFrame assert type(strategy.populate_buy_trend(indicators, metadata)) is DataFrame diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 59ce8c5b8..1bb45f28c 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -105,6 +105,7 @@ def test_strategy(result, default_conf): assert strategy.stoploss == -0.10 assert default_conf['stoploss'] == -0.10 + assert strategy.timeframe == '5m' assert strategy.ticker_interval == '5m' assert default_conf['timeframe'] == '5m' diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 05074c258..8e79f4297 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -401,8 +401,8 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert 'datadir' in config assert 'user_data_dir' in config assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'ticker_interval' in config - assert not log_has('Parameter -i/--ticker-interval detected ...', caplog) + assert 'timeframe' in config + assert not log_has('Parameter -i/--timeframe detected ...', caplog) assert 'position_stacking' not in config assert not log_has('Parameter --enable-position-stacking detected ...', caplog) @@ -448,8 +448,8 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non assert log_has('Using user-data directory: {} ...'.format(Path("/tmp/freqtrade")), caplog) assert 'user_data_dir' in config - assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + assert 'timeframe' in config + assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', caplog) assert 'position_stacking' in config @@ -494,8 +494,8 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non assert 'pair_whitelist' in config['exchange'] assert 'datadir' in config assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + assert 'timeframe' in config + assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', caplog) assert 'strategy_list' in config From 3e895ae74aa3d754a0d8a2bc477c1c6da50b7baa Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 09:41:42 +0200 Subject: [PATCH 10/54] Some more replacements of ticker_interval --- freqtrade/optimize/hyperopt_loss_interface.py | 1 + freqtrade/persistence.py | 1 + tests/data/test_dataprovider.py | 2 +- tests/test_configuration.py | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/hyperopt_loss_interface.py b/freqtrade/optimize/hyperopt_loss_interface.py index 879a9f0e9..c2607a9a8 100644 --- a/freqtrade/optimize/hyperopt_loss_interface.py +++ b/freqtrade/optimize/hyperopt_loss_interface.py @@ -15,6 +15,7 @@ class IHyperOptLoss(ABC): Defines the custom loss function (`hyperopt_loss_function()` which is evaluated every epoch.) """ ticker_interval: str + timeframe: str @staticmethod @abstractmethod diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 111ccfe2a..363ce35ad 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -288,6 +288,7 @@ class Trade(_DECL_BASE): 'max_rate': self.max_rate, 'strategy': self.strategy, 'ticker_interval': self.ticker_interval, + 'timeframe': self.ticker_interval, 'open_order_id': self.open_order_id, 'exchange': self.exchange, } diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 718060c5e..def3ad535 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -104,7 +104,7 @@ def test_refresh(mocker, default_conf, ohlcv_history): timeframe = default_conf["timeframe"] pairs = [("XRP/BTC", timeframe), ("UNITTEST/BTC", timeframe)] - pairs_non_trad = [("ETH/USDT", ticker_interval), ("BTC/TUSD", "1h")] + pairs_non_trad = [("ETH/USDT", timeframe), ("BTC/TUSD", "1h")] dp = DataProvider(default_conf, exchange) dp.refresh(pairs) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 8e79f4297..1ed97f728 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -87,7 +87,7 @@ def test_load_config_file_error_range(default_conf, mocker, caplog) -> None: assert isinstance(x, str) assert (x == '{"max_open_trades": 1, "stake_currency": "BTC", ' '"stake_amount": .001, "fiat_display_currency": "USD", ' - '"timeframe": "5m", "dry_run": true, ') + '"timeframe": "5m", "dry_run": true, "cance') def test__args_to_config(caplog): From 09fe3c6f5e1f9f0a42882782e3caac6607e34600 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 09:50:56 +0200 Subject: [PATCH 11/54] create compatibility code --- freqtrade/configuration/deprecated_settings.py | 6 ++++++ freqtrade/constants.py | 1 - freqtrade/resolvers/strategy_resolver.py | 11 +++++++++++ freqtrade/strategy/interface.py | 4 ++-- tests/strategy/test_default_strategy.py | 1 - 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index 3999ea422..2d5dba9ca 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -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'] diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 511c2993d..3afb4b0f1 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -71,7 +71,6 @@ 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': { diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 99cf2d785..26bce01ca 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -50,6 +50,14 @@ 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) @@ -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]: diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 17c4aa5ca..7bf750249 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -85,8 +85,8 @@ class IStrategy(ABC): trailing_stop_positive_offset: float = 0.0 trailing_only_offset_is_reached = False - # associated ticker interval - ticker_interval: str + # associated timeframe + timeframe: str # Optional order types order_types: Dict = { diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py index 315f80440..df7e35197 100644 --- a/tests/strategy/test_default_strategy.py +++ b/tests/strategy/test_default_strategy.py @@ -18,7 +18,6 @@ def test_default_strategy(result): metadata = {'pair': 'ETH/BTC'} assert type(strategy.minimal_roi) is dict assert type(strategy.stoploss) is float - assert type(strategy.ticker_interval) is str assert type(strategy.timeframe) is str indicators = strategy.populate_indicators(result, metadata) assert type(indicators) is DataFrame From af0f29e6b7b6647ce8e651d35b4a558fee09b578 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 10:02:24 +0200 Subject: [PATCH 12/54] Update persistence to use timeframe --- docs/sql_cheatsheet.md | 2 +- freqtrade/freqtradebot.py | 2 +- freqtrade/persistence.py | 19 ++++++++++++------- tests/test_persistence.py | 7 ++++--- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index 895a0536a..b261904d7 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -70,7 +70,7 @@ CREATE TABLE trades min_rate FLOAT, sell_reason VARCHAR, strategy VARCHAR, - ticker_interval INTEGER, + timeframe INTEGER, PRIMARY KEY (id), CHECK (is_open IN (0, 1)) ); diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c52fe18d1..a8483051e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -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['timeframe']) + timeframe=timeframe_to_minutes(self.config['timeframe']) ) # Update fees if order is closed diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 363ce35ad..f0b97be1e 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -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) @@ -287,8 +292,8 @@ class Trade(_DECL_BASE): 'min_rate': self.min_rate, 'max_rate': self.max_rate, 'strategy': self.strategy, - 'ticker_interval': self.ticker_interval, - 'timeframe': self.ticker_interval, + 'ticker_interval': self.timeframe, + 'timeframe': self.timeframe, 'open_order_id': self.open_order_id, 'exchange': self.exchange, } diff --git a/tests/test_persistence.py b/tests/test_persistence.py index c15936cf6..bc5b315a1 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -469,6 +469,7 @@ def test_migrate_old(mocker, default_conf, fee): assert trade.fee_open_currency is None assert trade.fee_close_cost is None assert trade.fee_close_currency is None + assert trade.timeframe is None trade = Trade.query.filter(Trade.id == 2).first() assert trade.close_rate is not None @@ -512,11 +513,11 @@ def test_migrate_new(mocker, default_conf, fee, caplog): );""" insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee, open_rate, stake_amount, amount, open_date, - stop_loss, initial_stop_loss, max_rate) + stop_loss, initial_stop_loss, max_rate, ticker_interval) VALUES ('binance', 'ETC/BTC', 1, {fee}, 0.00258580, {stake}, {amount}, '2019-11-28 12:44:24.000000', - 0.0, 0.0, 0.0) + 0.0, 0.0, 0.0, '5m') """.format(fee=fee.return_value, stake=default_conf.get("stake_amount"), amount=amount @@ -554,7 +555,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): assert trade.initial_stop_loss == 0.0 assert trade.sell_reason is None assert trade.strategy is None - assert trade.ticker_interval is None + assert trade.timeframe == '5m' assert trade.stoploss_order_id is None assert trade.stoploss_last_update is None assert log_has("trying trades_bak1", caplog) From f9bb1a7f22ee086042e37d563ee67d90a12577a1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 10:02:55 +0200 Subject: [PATCH 13/54] Update more occurances of ticker_interval --- freqtrade/data/btanalysis.py | 4 ++-- freqtrade/optimize/hyperopt_loss_interface.py | 1 - freqtrade/templates/sample_strategy.py | 2 +- tests/optimize/test_backtesting.py | 4 ++-- tests/optimize/test_hyperopt.py | 3 ++- tests/strategy/test_default_strategy.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index f98135c27..5948d933c 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -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) diff --git a/freqtrade/optimize/hyperopt_loss_interface.py b/freqtrade/optimize/hyperopt_loss_interface.py index c2607a9a8..48407a8a8 100644 --- a/freqtrade/optimize/hyperopt_loss_interface.py +++ b/freqtrade/optimize/hyperopt_loss_interface.py @@ -14,7 +14,6 @@ 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 diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index f78489173..e269848d2 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -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 diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 407604d9c..342d7689f 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -286,7 +286,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None: assert not backtesting.strategy.order_types["stoploss_on_exchange"] -def test_backtesting_init_no_ticker_interval(mocker, default_conf, caplog) -> None: +def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None: patch_exchange(mocker) del default_conf['timeframe'] default_conf['strategy_list'] = ['DefaultStrategy', @@ -453,7 +453,7 @@ def test_backtest(default_conf, fee, mocker, testdatadir) -> None: t["close_rate"], 6) < round(ln.iloc[0]["high"], 6)) -def test_backtest_1min_ticker_interval(default_conf, fee, mocker, testdatadir) -> None: +def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None: default_conf['ask_strategy']['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) patch_exchange(mocker) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 4f5b3983a..564725709 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -197,7 +197,8 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None: "Using populate_sell_trend from the strategy.", caplog) assert log_has("Hyperopt class does not provide populate_buy_trend() method. " "Using populate_buy_trend from the strategy.", caplog) - assert hasattr(x, "ticker_interval") + assert hasattr(x, "ticker_interval") # DEPRECATED + assert hasattr(x, "timeframe") def test_hyperoptresolver_wrongname(mocker, default_conf, caplog) -> None: diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py index df7e35197..1b1648db9 100644 --- a/tests/strategy/test_default_strategy.py +++ b/tests/strategy/test_default_strategy.py @@ -6,7 +6,7 @@ from .strats.default_strategy import DefaultStrategy def test_default_strategy_structure(): assert hasattr(DefaultStrategy, 'minimal_roi') assert hasattr(DefaultStrategy, 'stoploss') - assert hasattr(DefaultStrategy, 'ticker_interval') + assert hasattr(DefaultStrategy, 'timeframe') assert hasattr(DefaultStrategy, 'populate_indicators') assert hasattr(DefaultStrategy, 'populate_buy_trend') assert hasattr(DefaultStrategy, 'populate_sell_trend') From febc95dcdf95d9ee6a080d25684491320f24f236 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 10:03:23 +0200 Subject: [PATCH 14/54] Update documentation to remove ticker_interval --- docs/bot-usage.md | 25 ++++++++++++------------- docs/configuration.md | 4 ++-- docs/hyperopt.md | 8 ++++---- docs/plotting.md | 15 ++++++++------- docs/strategy-customization.md | 14 +++++++------- docs/strategy_analysis_example.md | 4 ++-- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index b1649374a..b5af60f2b 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -72,7 +72,6 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. -. ``` @@ -197,7 +196,7 @@ Backtesting also uses the config specified via `-c/--config`. ``` usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] [-i TICKER_INTERVAL] + [--strategy-path PATH] [-i TIMEFRAME] [--timerange TIMERANGE] [--max-open-trades INT] [--stake-amount STAKE_AMOUNT] [--fee FLOAT] [--eps] [--dmmp] @@ -206,7 +205,7 @@ usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] optional arguments: -h, --help show this help message and exit - -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + -i TIMEFRAME, --timeframe TIMEFRAME, --ticker-interval TIMEFRAME Specify ticker interval (`1m`, `5m`, `30m`, `1h`, `1d`). --timerange TIMERANGE @@ -280,7 +279,7 @@ to find optimal parameter values for your strategy. ``` usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] [--strategy-path PATH] - [-i TICKER_INTERVAL] [--timerange TIMERANGE] + [-i TIMEFRAME] [--timerange TIMERANGE] [--max-open-trades INT] [--stake-amount STAKE_AMOUNT] [--fee FLOAT] [--hyperopt NAME] [--hyperopt-path PATH] [--eps] @@ -292,7 +291,7 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] optional arguments: -h, --help show this help message and exit - -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + -i TIMEFRAME, --timeframe TIMEFRAME, --ticker-interval TIMEFRAME Specify ticker interval (`1m`, `5m`, `30m`, `1h`, `1d`). --timerange TIMERANGE @@ -323,7 +322,7 @@ optional arguments: --print-all Print all results, not only the best ones. --no-color Disable colorization of hyperopt results. May be useful if you are redirecting output to a file. - --print-json Print best results in JSON format. + --print-json Print output in JSON format. -j JOBS, --job-workers JOBS The number of concurrently running jobs for hyperoptimization (hyperopt worker processes). If -1 @@ -341,11 +340,11 @@ optional arguments: class (IHyperOptLoss). Different functions can generate completely different results, since the target for optimization is different. Built-in - Hyperopt-loss-functions are: - DefaultHyperOptLoss, OnlyProfitHyperOptLoss, - SharpeHyperOptLoss, SharpeHyperOptLossDaily, - SortinoHyperOptLoss, SortinoHyperOptLossDaily. - (default: `DefaultHyperOptLoss`). + Hyperopt-loss-functions are: DefaultHyperOptLoss, + OnlyProfitHyperOptLoss, SharpeHyperOptLoss, + SharpeHyperOptLossDaily, SortinoHyperOptLoss, + SortinoHyperOptLossDaily.(default: + `DefaultHyperOptLoss`). Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). @@ -378,13 +377,13 @@ To know your trade expectancy and winrate against historical data, you can use E ``` usage: freqtrade edge [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] [--strategy-path PATH] - [-i TICKER_INTERVAL] [--timerange TIMERANGE] + [-i TIMEFRAME] [--timerange TIMERANGE] [--max-open-trades INT] [--stake-amount STAKE_AMOUNT] [--fee FLOAT] [--stoplosses STOPLOSS_RANGE] optional arguments: -h, --help show this help message and exit - -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + -i TIMEFRAME, --timeframe TIMEFRAME, --ticker-interval TIMEFRAME Specify ticker interval (`1m`, `5m`, `30m`, `1h`, `1d`). --timerange TIMERANGE diff --git a/docs/configuration.md b/docs/configuration.md index a1cb45e0f..4dbcc6d2a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -47,7 +47,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `amend_last_stake_amount` | Use reduced last stake amount if necessary. [More information below](#configuring-amount-per-trade).
*Defaults to `false`.*
**Datatype:** Boolean | `last_stake_amount_min_ratio` | Defines minimum stake amount that has to be left and executed. Applies only to the last stake amount when it's amended to a reduced value (i.e. if `amend_last_stake_amount` is set to `true`). [More information below](#configuring-amount-per-trade).
*Defaults to `0.5`.*
**Datatype:** Float (as ratio) | `amount_reserve_percent` | Reserve some amount in min pair stake amount. The bot will reserve `amount_reserve_percent` + stoploss value when calculating min pair stake amount in order to avoid possible trade refusals.
*Defaults to `0.05` (5%).*
**Datatype:** Positive Float as ratio. -| `ticker_interval` | The timeframe (ticker interval) to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** String +| `timeframe` | The timeframe (former ticker interval) to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** String | `fiat_display_currency` | Fiat currency used to show your profits. [More information below](#what-values-can-be-used-for-fiat_display_currency).
**Datatype:** String | `dry_run` | **Required.** Define if the bot must be in Dry Run or production mode.
*Defaults to `true`.*
**Datatype:** Boolean | `dry_run_wallet` | Define the starting amount in stake currency for the simulated wallet used by the bot running in the Dry Run mode.
*Defaults to `1000`.*
**Datatype:** Float @@ -125,7 +125,7 @@ The following parameters can be set in either configuration file or strategy. Values set in the configuration file always overwrite values set in the strategy. * `minimal_roi` -* `ticker_interval` +* `timeframe` * `stoploss` * `trailing_stop` * `trailing_stop_positive` diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 8efc51a39..a4d36530c 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -124,9 +124,9 @@ To avoid naming collisions in the search-space, please prefix all sell-spaces wi #### Using timeframe as a part of the Strategy -The Strategy class exposes the timeframe (ticker interval) value as the `self.ticker_interval` attribute. -The same value is available as class-attribute `HyperoptName.ticker_interval`. -In the case of the linked sample-value this would be `SampleHyperOpt.ticker_interval`. +The Strategy class exposes the timeframe value as the `self.timeframe` attribute. +The same value is available as class-attribute `HyperoptName.timeframe`. +In the case of the linked sample-value this would be `SampleHyperOpt.timeframe`. ## Solving a Mystery @@ -403,7 +403,7 @@ As stated in the comment, you can also use it as the value of the `minimal_roi` #### Default ROI Search Space -If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps). Hyperopt implements adaptive ranges for ROI tables with ranges for values in the ROI steps that depend on the ticker_interval used. By default the values vary in the following ranges (for some of the most used timeframes, values are rounded to 5 digits after the decimal point): +If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps). Hyperopt implements adaptive ranges for ROI tables with ranges for values in the ROI steps that depend on the timeframe used. By default the values vary in the following ranges (for some of the most used timeframes, values are rounded to 5 digits after the decimal point): | # step | 1m | | 5m | | 1h | | 1d | | | ------ | ------ | ----------------- | -------- | ----------- | ---------- | ----------------- | ------------ | ----------------- | diff --git a/docs/plotting.md b/docs/plotting.md index be83065a6..d3a2df1c1 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -31,7 +31,7 @@ usage: freqtrade plot-dataframe [-h] [-v] [--logfile FILE] [-V] [-c PATH] [--plot-limit INT] [--db-url PATH] [--trade-source {DB,file}] [--export EXPORT] [--export-filename PATH] - [--timerange TIMERANGE] [-i TICKER_INTERVAL] + [--timerange TIMERANGE] [-i TIMEFRAME] [--no-trades] optional arguments: @@ -65,7 +65,7 @@ optional arguments: _today.json` --timerange TIMERANGE Specify what timerange of data to use. - -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + -i TIMEFRAME, --timeframe TIMEFRAME, --ticker-interval TIMEFRAME Specify ticker interval (`1m`, `5m`, `30m`, `1h`, `1d`). --no-trades Skip using trades from backtesting file and DB. @@ -227,7 +227,7 @@ usage: freqtrade plot-profit [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-p PAIRS [PAIRS ...]] [--timerange TIMERANGE] [--export EXPORT] [--export-filename PATH] [--db-url PATH] - [--trade-source {DB,file}] [-i TICKER_INTERVAL] + [--trade-source {DB,file}] [-i TIMEFRAME] optional arguments: -h, --help show this help message and exit @@ -250,7 +250,7 @@ optional arguments: --trade-source {DB,file} Specify the source for trades (Can be DB or file (backtest file)) Default: file - -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + -i TIMEFRAME, --timeframe TIMEFRAME, --ticker-interval TIMEFRAME Specify ticker interval (`1m`, `5m`, `30m`, `1h`, `1d`). @@ -261,9 +261,10 @@ Common arguments: details. -V, --version show program's version number and exit -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. -d PATH, --datadir PATH Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 7197b0fba..ca0d9a9a3 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -248,7 +248,7 @@ minimal_roi = { While technically not completely disabled, this would sell once the trade reaches 10000% Profit. -To use times based on candle duration (ticker_interval or timeframe), the following snippet can be handy. +To use times based on candle duration (timeframe), the following snippet can be handy. This will allow you to change the ticket_interval for the strategy, and ROI times will still be set as candles (e.g. after 3 candles ...) ``` python @@ -256,12 +256,12 @@ from freqtrade.exchange import timeframe_to_minutes class AwesomeStrategy(IStrategy): - ticker_interval = "1d" - ticker_interval_mins = timeframe_to_minutes(ticker_interval) + timeframe = "1d" + timeframe_mins = timeframe_to_minutes(timeframe) minimal_roi = { "0": 0.05, # 5% for the first 3 candles - str(ticker_interval_mins * 3)): 0.02, # 2% after 3 candles - str(ticker_interval_mins * 6)): 0.01, # 1% After 6 candles + str(timeframe_mins * 3)): 0.02, # 2% after 3 candles + str(timeframe_mins * 6)): 0.01, # 1% After 6 candles } ``` @@ -290,7 +290,7 @@ Common values are `"1m"`, `"5m"`, `"15m"`, `"1h"`, however all values supported Please note that the same buy/sell signals may work well with one timeframe, but not with the others. -This setting is accessible within the strategy methods as the `self.ticker_interval` attribute. +This setting is accessible within the strategy methods as the `self.timeframe` attribute. ### Metadata dict @@ -400,7 +400,7 @@ This is where calling `self.dp.current_whitelist()` comes in handy. class SampleStrategy(IStrategy): # strategy init stuff... - ticker_interval = '5m' + timeframe = '5m' # more strategy init stuff.. diff --git a/docs/strategy_analysis_example.md b/docs/strategy_analysis_example.md index d26d684ce..6b4ad567f 100644 --- a/docs/strategy_analysis_example.md +++ b/docs/strategy_analysis_example.md @@ -18,7 +18,7 @@ config = Configuration.from_files([]) # config = Configuration.from_files(["config.json"]) # Define some constants -config["ticker_interval"] = "5m" +config["timeframe"] = "5m" # Name of the strategy class config["strategy"] = "SampleStrategy" # Location of the data @@ -33,7 +33,7 @@ pair = "BTC_USDT" from freqtrade.data.history import load_pair_history candles = load_pair_history(datadir=data_location, - timeframe=config["ticker_interval"], + timeframe=config["timeframe"], pair=pair) # Confirm success From 33b7046260fc500fe5ae2c60ab999f67ac7f0ff4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 10:06:26 +0200 Subject: [PATCH 15/54] Update more documentation --- README.md | 5 +++-- docs/backtesting.md | 10 +++++----- docs/bot-usage.md | 25 +++++++++++++++++++------ docs/edge.md | 2 +- docs/hyperopt.md | 2 +- docs/strategy-customization.md | 2 +- docs/utils.md | 2 +- freqtrade/commands/arguments.py | 2 +- 8 files changed, 32 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index cfb384702..7e0acde46 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,8 @@ positional arguments: new-hyperopt Create new hyperopt new-strategy Create new strategy download-data Download backtesting data. - convert-data Convert candle (OHLCV) data from one format to another. + convert-data Convert candle (OHLCV) data from one format to + another. convert-trade-data Convert trade data from one format to another. backtesting Backtesting module. edge Edge module. @@ -94,7 +95,7 @@ positional arguments: list-markets Print markets on exchange. list-pairs Print pairs on exchange. list-strategies Print available strategies. - list-timeframes Print available ticker intervals (timeframes) for the exchange. + list-timeframes Print available timeframes for the exchange. show-trades Show trades. test-pairlist Test your pairlist configuration. plot-dataframe Plot candles with indicators. diff --git a/docs/backtesting.md b/docs/backtesting.md index 9b2997510..51b2e953b 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -12,7 +12,7 @@ real data. This is what we call [backtesting](https://en.wikipedia.org/wiki/Backtesting). Backtesting will use the crypto-currencies (pairs) from your config file and load historical candle (OHCLV) data from `user_data/data/` by default. -If no data is available for the exchange / pair / timeframe (ticker interval) combination, backtesting will ask you to download them first using `freqtrade download-data`. +If no data is available for the exchange / pair / timeframe combination, backtesting will ask you to download them first using `freqtrade download-data`. For details on downloading, please refer to the [Data Downloading](data-download.md) section in the documentation. The result of backtesting will confirm if your bot has better odds of making a profit than a loss. @@ -35,7 +35,7 @@ freqtrade backtesting #### With 1 min candle (OHLCV) data ```bash -freqtrade backtesting --ticker-interval 1m +freqtrade backtesting --timeframe 1m ``` #### Using a different on-disk historical candle (OHLCV) data source @@ -58,7 +58,7 @@ Where `-s SampleStrategy` refers to the class name within the strategy file `sam #### Comparing multiple Strategies ```bash -freqtrade backtesting --strategy-list SampleStrategy1 AwesomeStrategy --ticker-interval 5m +freqtrade backtesting --strategy-list SampleStrategy1 AwesomeStrategy --timeframe 5m ``` Where `SampleStrategy1` and `AwesomeStrategy` refer to class names of strategies. @@ -228,13 +228,13 @@ You can then load the trades to perform further analysis as shown in our [data a To compare multiple strategies, a list of Strategies can be provided to backtesting. -This is limited to 1 timeframe (ticker interval) value per run. However, data is only loaded once from disk so if you have multiple +This is limited to 1 timeframe value per run. However, data is only loaded once from disk so if you have multiple strategies you'd like to compare, this will give a nice runtime boost. All listed Strategies need to be in the same directory. ``` bash -freqtrade backtesting --timerange 20180401-20180410 --ticker-interval 5m --strategy-list Strategy001 Strategy002 --export trades +freqtrade backtesting --timerange 20180401-20180410 --timeframe 5m --strategy-list Strategy001 Strategy002 --export trades ``` This will save the results to `user_data/backtest_results/backtest-result-.json`, injecting the strategy-name into the target filename. diff --git a/docs/bot-usage.md b/docs/bot-usage.md index b5af60f2b..40ff3d82b 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -9,22 +9,35 @@ This page explains the different parameters of the bot and how to run it. ``` usage: freqtrade [-h] [-V] - {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit} + {trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} ... Free, open source crypto trading bot positional arguments: - {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit} + {trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} trade Trade module. + create-userdir Create user-data directory. + new-config Create new config + new-hyperopt Create new hyperopt + new-strategy Create new strategy + download-data Download backtesting data. + convert-data Convert candle (OHLCV) data from one format to + another. + convert-trade-data Convert trade data from one format to another. backtesting Backtesting module. edge Edge module. hyperopt Hyperopt module. - create-userdir Create user-data directory. + hyperopt-list List Hyperopt results + hyperopt-show Show details of Hyperopt results list-exchanges Print available exchanges. - list-timeframes Print available ticker intervals (timeframes) for the - exchange. - download-data Download backtesting data. + list-hyperopts Print available hyperopt classes. + list-markets Print markets on exchange. + list-pairs Print pairs on exchange. + list-strategies Print available strategies. + list-timeframes Print available timeframes for the exchange. + show-trades Show trades. + test-pairlist Test your pairlist configuration. plot-dataframe Plot candles with indicators. plot-profit Generate plot showing profits. diff --git a/docs/edge.md b/docs/edge.md index 029844c0b..28a7f14cb 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -156,7 +156,7 @@ Edge module has following configuration options: | `minimum_winrate` | It filters out pairs which don't have at least minimum_winrate.
This comes handy if you want to be conservative and don't comprise win rate in favour of risk reward ratio.
*Defaults to `0.60`.*
**Datatype:** Float | `minimum_expectancy` | It filters out pairs which have the expectancy lower than this number.
Having an expectancy of 0.20 means if you put 10$ on a trade you expect a 12$ return.
*Defaults to `0.20`.*
**Datatype:** Float | `min_trade_number` | When calculating *W*, *R* and *E* (expectancy) against historical data, you always want to have a minimum number of trades. The more this number is the more Edge is reliable.
Having a win rate of 100% on a single trade doesn't mean anything at all. But having a win rate of 70% over past 100 trades means clearly something.
*Defaults to `10` (it is highly recommended not to decrease this number).*
**Datatype:** Integer -| `max_trade_duration_minute` | Edge will filter out trades with long duration. If a trade is profitable after 1 month, it is hard to evaluate the strategy based on it. But if most of trades are profitable and they have maximum duration of 30 minutes, then it is clearly a good sign.
**NOTICE:** While configuring this value, you should take into consideration your timeframe (ticker interval). As an example filtering out trades having duration less than one day for a strategy which has 4h interval does not make sense. Default value is set assuming your strategy interval is relatively small (1m or 5m, etc.).
*Defaults to `1440` (one day).*
**Datatype:** Integer +| `max_trade_duration_minute` | Edge will filter out trades with long duration. If a trade is profitable after 1 month, it is hard to evaluate the strategy based on it. But if most of trades are profitable and they have maximum duration of 30 minutes, then it is clearly a good sign.
**NOTICE:** While configuring this value, you should take into consideration your timeframe. As an example filtering out trades having duration less than one day for a strategy which has 4h interval does not make sense. Default value is set assuming your strategy interval is relatively small (1m or 5m, etc.).
*Defaults to `1440` (one day).*
**Datatype:** Integer | `remove_pumps` | Edge will remove sudden pumps in a given market while going through historical data. However, given that pumps happen very often in crypto markets, we recommend you keep this off.
*Defaults to `false`.*
**Datatype:** Boolean ## Running Edge independently diff --git a/docs/hyperopt.md b/docs/hyperopt.md index a4d36530c..c9c87ead3 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -412,7 +412,7 @@ If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace f | 3 | 4...20 | 0.00387...0.01547 | 20...100 | 0.01...0.04 | 240...1200 | 0.02294...0.09177 | 5760...28800 | 0.04059...0.16237 | | 4 | 6...44 | 0.0 | 30...220 | 0.0 | 360...2640 | 0.0 | 8640...63360 | 0.0 | -These ranges should be sufficient in most cases. The minutes in the steps (ROI dict keys) are scaled linearly depending on the timeframe (ticker interval) used. The ROI values in the steps (ROI dict values) are scaled logarithmically depending on the timeframe used. +These ranges should be sufficient in most cases. The minutes in the steps (ROI dict keys) are scaled linearly depending on the timeframe used. The ROI values in the steps (ROI dict values) are scaled logarithmically depending on the timeframe used. If you have the `generate_roi_table()` and `roi_space()` methods in your custom hyperopt file, remove them in order to utilize these adaptive ROI tables and the ROI hyperoptimization space generated by Freqtrade by default. diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index ca0d9a9a3..70013c821 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -142,7 +142,7 @@ By letting the bot know how much history is needed, backtest trades can start at Let's try to backtest 1 month (January 2019) of 5m candles using the an example strategy with EMA100, as above. ``` bash -freqtrade backtesting --timerange 20190101-20190201 --ticker-interval 5m +freqtrade backtesting --timerange 20190101-20190201 --timeframe 5m ``` Assuming `startup_candle_count` is set to 100, backtesting knows it needs 100 candles to generate valid buy signals. It will load data from `20190101 - (100 * 5m)` - which is ~2019-12-31 15:30:00. diff --git a/docs/utils.md b/docs/utils.md index 7ed31376f..793c84a93 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -62,7 +62,7 @@ $ freqtrade new-config --config config_binance.json ? Please insert your stake currency: BTC ? Please insert your stake amount: 0.05 ? Please insert max_open_trades (Integer or 'unlimited'): 3 -? Please insert your timeframe (ticker interval): 5m +? Please insert your desired timeframe (e.g. 5m): 5m ? Please insert your display Currency (for reporting): USD ? Select exchange binance ? Do you want to enable Telegram? No diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 36e3dedf0..72f2a02f0 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -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) From 8e1a664a48f7b315907a9bbdd3b7f8fd790a8211 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 10:11:50 +0200 Subject: [PATCH 16/54] Add test for deprecation updating --- tests/strategy/strats/legacy_strategy.py | 1 + tests/strategy/test_strategy.py | 2 ++ tests/test_configuration.py | 12 ++++++++++++ 3 files changed, 15 insertions(+) diff --git a/tests/strategy/strats/legacy_strategy.py b/tests/strategy/strats/legacy_strategy.py index 89ce3f8cb..9cbce0ad5 100644 --- a/tests/strategy/strats/legacy_strategy.py +++ b/tests/strategy/strats/legacy_strategy.py @@ -31,6 +31,7 @@ class TestStrategyLegacy(IStrategy): stoploss = -0.10 # Optimal ticker interval for the strategy + # Keep the legacy value here to test compatibility ticker_interval = '5m' def populate_indicators(self, dataframe: DataFrame) -> DataFrame: diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 1bb45f28c..f2cf11712 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -370,6 +370,8 @@ def test_call_deprecated_function(result, monkeypatch, default_conf): assert strategy._buy_fun_len == 2 assert strategy._sell_fun_len == 2 assert strategy.INTERFACE_VERSION == 1 + assert strategy.timeframe == '5m' + assert strategy.ticker_interval == '5m' indicator_df = strategy.advise_indicators(result, metadata=metadata) assert isinstance(indicator_df, DataFrame) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 1ed97f728..9602f6389 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1137,3 +1137,15 @@ def test_process_deprecated_setting(mocker, default_conf, caplog): 'sectionB', 'deprecated_setting') assert not log_has_re('DEPRECATED', caplog) assert default_conf['sectionA']['new_setting'] == 'valA' + + +def test_process_deprecated_ticker_interval(mocker, default_conf, caplog): + message = "DEPRECATED: Please use 'timeframe' instead of 'ticker_interval." + process_temporary_deprecated_settings(default_conf) + assert not log_has(message, caplog) + + del default_conf['timeframe'] + default_conf['ticker_interval'] = '15m' + process_temporary_deprecated_settings(default_conf) + assert log_has(message, caplog) + assert default_conf['ticker_interval'] == '15m' From a8005819c97461055bf29a0bae4a1e662a313c28 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 10:19:27 +0200 Subject: [PATCH 17/54] Add class-level attributes to hyperopt and strategy --- freqtrade/optimize/hyperopt_interface.py | 3 ++- freqtrade/strategy/interface.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 20209d8a9..00353cbf4 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -31,7 +31,8 @@ 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 diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7bf750249..f9f3a3678 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -86,6 +86,7 @@ class IStrategy(ABC): trailing_only_offset_is_reached = False # associated timeframe + ticker_interval: str # DEPRECATED timeframe: str # Optional order types From b106c886309f446861bd01b4062e3e285c23a3ff Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 13:08:21 +0200 Subject: [PATCH 18/54] Add test case for strategy overwriting --- tests/strategy/test_strategy.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index f2cf11712..85cb8c132 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -358,8 +358,9 @@ def test_deprecate_populate_indicators(result, default_conf): @pytest.mark.filterwarnings("ignore:deprecated") -def test_call_deprecated_function(result, monkeypatch, default_conf): +def test_call_deprecated_function(result, monkeypatch, default_conf, caplog): default_location = Path(__file__).parent / "strats" + del default_conf['timeframe'] default_conf.update({'strategy': 'TestStrategyLegacy', 'strategy_path': default_location}) strategy = StrategyResolver.load_strategy(default_conf) @@ -385,6 +386,9 @@ def test_call_deprecated_function(result, monkeypatch, default_conf): assert isinstance(selldf, DataFrame) assert 'sell' in selldf + assert log_has('DEPRECATED: Please migrate to using timeframe instead of ticker_interval.', + caplog) + def test_strategy_interface_versioning(result, monkeypatch, default_conf): default_conf.update({'strategy': 'DefaultStrategy'}) From 9995a5899fb16dcfe1e13a7525077142a4152076 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Tue, 2 Jun 2020 16:25:22 +0300 Subject: [PATCH 19/54] Fix merge --- freqtrade/persistence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index a3b394769..bb9db703d 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -108,7 +108,7 @@ def check_migrate(engine) -> None: sell_reason = get_column_def(cols, 'sell_reason', 'null') strategy = get_column_def(cols, 'strategy', 'null') # If ticker-interval existed use that, else null. - if has_column(cols, '): + if has_column(cols, 'ticker_interval'): timeframe = get_column_def(cols, 'timeframe', 'ticker_interval') else: timeframe = get_column_def(cols, 'timeframe', 'null') From edf8e39bc11c6c37b06459ac9d9bdfc38f86eb9a Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 2 Jun 2020 17:57:45 +0300 Subject: [PATCH 20/54] Fix tests after merge --- tests/rpc/test_rpc.py | 2 -- tests/rpc/test_rpc_apiserver.py | 4 +--- tests/test_persistence.py | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 9a55c7639..44d54bad6 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -65,7 +65,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, - 'ticker_interval': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, @@ -124,7 +123,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, - 'ticker_interval': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index b3859c4e6..5547ee004 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -322,7 +322,7 @@ def test_api_show_config(botclient, mocker): assert_response(rc) assert 'dry_run' in rc.json assert rc.json['exchange'] == 'bittrex' - assert rc.json['ticker_interval'] == '5m' + assert rc.json['timeframe'] == '5m' assert rc.json['state'] == 'running' assert not rc.json['trailing_stop'] @@ -547,7 +547,6 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'sell_reason': None, 'sell_order_status': None, 'strategy': 'DefaultStrategy', - 'ticker_interval': 5, 'timeframe': 5, 'exchange': 'bittrex', }] @@ -671,7 +670,6 @@ def test_api_forcebuy(botclient, mocker, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': None, - 'ticker_interval': None, 'timeframe': None, 'exchange': 'bittrex', } diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 69ee014c5..d55f24719 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -776,7 +776,6 @@ def test_to_json(default_conf, fee): 'min_rate': None, 'max_rate': None, 'strategy': None, - 'ticker_interval': None, 'timeframe': None, 'exchange': 'bittrex', } @@ -838,7 +837,6 @@ def test_to_json(default_conf, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': None, - 'ticker_interval': None, 'timeframe': None, 'exchange': 'bittrex', } From 1a5dba9a79a50e567bbc16da3da63a82294c7802 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 19:39:17 +0200 Subject: [PATCH 21/54] Revert "Fix tests after merge" This reverts commit edf8e39bc11c6c37b06459ac9d9bdfc38f86eb9a. --- tests/rpc/test_rpc.py | 2 ++ tests/rpc/test_rpc_apiserver.py | 4 +++- tests/test_persistence.py | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 44d54bad6..9a55c7639 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -65,6 +65,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, + 'ticker_interval': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, @@ -123,6 +124,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, + 'ticker_interval': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 5547ee004..b3859c4e6 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -322,7 +322,7 @@ def test_api_show_config(botclient, mocker): assert_response(rc) assert 'dry_run' in rc.json assert rc.json['exchange'] == 'bittrex' - assert rc.json['timeframe'] == '5m' + assert rc.json['ticker_interval'] == '5m' assert rc.json['state'] == 'running' assert not rc.json['trailing_stop'] @@ -547,6 +547,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'sell_reason': None, 'sell_order_status': None, 'strategy': 'DefaultStrategy', + 'ticker_interval': 5, 'timeframe': 5, 'exchange': 'bittrex', }] @@ -670,6 +671,7 @@ def test_api_forcebuy(botclient, mocker, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': None, + 'ticker_interval': None, 'timeframe': None, 'exchange': 'bittrex', } diff --git a/tests/test_persistence.py b/tests/test_persistence.py index d55f24719..69ee014c5 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -776,6 +776,7 @@ def test_to_json(default_conf, fee): 'min_rate': None, 'max_rate': None, 'strategy': None, + 'ticker_interval': None, 'timeframe': None, 'exchange': 'bittrex', } @@ -837,6 +838,7 @@ def test_to_json(default_conf, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': None, + 'ticker_interval': None, 'timeframe': None, 'exchange': 'bittrex', } From 02fca141a03fccf9fd49dffc3ba8b30e84c6dfbc Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Jun 2020 19:43:15 +0200 Subject: [PATCH 22/54] Readd ticker_interval to trade api response --- freqtrade/persistence.py | 1 + tests/rpc/test_rpc_apiserver.py | 1 + 2 files changed, 2 insertions(+) diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index bb9db703d..628c9cf22 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -258,6 +258,7 @@ class Trade(_DECL_BASE): 'amount': round(self.amount, 8), 'stake_amount': round(self.stake_amount, 8), 'strategy': self.strategy, + 'ticker_interval': self.timeframe, # DEPRECATED 'timeframe': self.timeframe, 'fee_open': self.fee_open, diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index b3859c4e6..daee0186a 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -323,6 +323,7 @@ def test_api_show_config(botclient, mocker): assert 'dry_run' in rc.json assert rc.json['exchange'] == 'bittrex' assert rc.json['ticker_interval'] == '5m' + assert rc.json['timeframe'] == '5m' assert rc.json['state'] == 'running' assert not rc.json['trailing_stop'] From 78dea19ffb4e0efa9fa7594fadfddd49010a06e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Mar 2020 20:57:12 +0100 Subject: [PATCH 23/54] Implement first version of FTX stop --- freqtrade/exchange/ftx.py | 53 +++++++++++++++++ tests/exchange/test_ftx.py | 106 ++++++++++++++++++++++++++++++++++ tests/exchange/test_kraken.py | 10 ++-- 3 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 tests/exchange/test_ftx.py diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 75915122b..d06d49e5a 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -2,6 +2,10 @@ import logging from typing import Dict +import ccxt + +from freqtrade.exceptions import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.exchange import Exchange logger = logging.getLogger(__name__) @@ -10,5 +14,54 @@ logger = logging.getLogger(__name__) class Ftx(Exchange): _ft_has: Dict = { + "stoploss_on_exchange": True, "ohlcv_candle_limit": 1500, } + + def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool: + """ + Verify stop_loss against stoploss-order value (limit or price) + Returns True if adjustment is necessary. + """ + return order['type'] == 'stop' and stop_loss > float(order['price']) + + def stoploss(self, pair: str, amount: float, stop_price: float, order_types: Dict) -> Dict: + """ + Creates a stoploss market order. + Stoploss market orders is the only stoploss type supported by kraken. + """ + + ordertype = "stop" + + stop_price = self.price_to_precision(pair, stop_price) + + if self._config['dry_run']: + dry_order = self.dry_run_order( + pair, ordertype, "sell", amount, stop_price) + return dry_order + + try: + params = self._params.copy() + + amount = self.amount_to_precision(pair, amount) + + order = self._api.create_order(symbol=pair, type=ordertype, side='sell', + amount=amount, price=stop_price, params=params) + logger.info('stoploss order added for %s. ' + 'stop price: %s.', pair, stop_price) + return order + except ccxt.InsufficientFunds as e: + raise DependencyException( + f'Insufficient funds to create {ordertype} sell order on market {pair}.' + f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. ' + f'Message: {e}') from e + except ccxt.InvalidOrder as e: + raise InvalidOrderException( + f'Could not create {ordertype} sell order on market {pair}. ' + f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. ' + f'Message: {e}') from e + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e + except ccxt.BaseError as e: + raise OperationalException(e) from e diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py new file mode 100644 index 000000000..f17d5b42e --- /dev/null +++ b/tests/exchange/test_ftx.py @@ -0,0 +1,106 @@ +# pragma pylint: disable=missing-docstring, C0103, bad-continuation, global-statement +# pragma pylint: disable=protected-access +from random import randint +from unittest.mock import MagicMock + +import ccxt +import pytest + +from freqtrade.exceptions import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) +from tests.conftest import get_patched_exchange + + +STOPLOSS_ORDERTYPE = 'stop' + + +def test_stoploss_order_ftx(default_conf, mocker): + api_mock = MagicMock() + order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) + + api_mock.create_order = MagicMock(return_value={ + 'id': order_id, + 'info': { + 'foo': 'bar' + } + }) + + default_conf['dry_run'] = False + mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) + mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) + + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') + + # stoploss_on_exchange_limit_ratio is irrelevant for ftx market orders + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=190, + order_types={'stoploss_on_exchange_limit_ratio': 1.05}) + assert api_mock.create_order.call_count == 1 + + api_mock.create_order.reset_mock() + + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}) + + assert 'id' in order + assert 'info' in order + assert order['id'] == order_id + assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' + assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE + assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' + assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 + assert api_mock.create_order.call_args_list[0][1]['price'] == 220 + + # test exception handling + with pytest.raises(DependencyException): + api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}) + + with pytest.raises(InvalidOrderException): + api_mock.create_order = MagicMock( + side_effect=ccxt.InvalidOrder("ftx Order would trigger immediately.")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}) + + with pytest.raises(TemporaryError): + api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No connection")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}) + + with pytest.raises(OperationalException, match=r".*DeadBeef.*"): + api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}) + + +def test_stoploss_order_dry_run_ftx(default_conf, mocker): + api_mock = MagicMock() + default_conf['dry_run'] = True + mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) + mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) + + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') + + api_mock.create_order.reset_mock() + + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}) + + assert 'id' in order + assert 'info' in order + assert 'type' in order + + assert order['type'] == STOPLOSS_ORDERTYPE + assert order['price'] == 220 + assert order['amount'] == 1 + + +def test_stoploss_adjust_ftx(mocker, default_conf): + exchange = get_patched_exchange(mocker, default_conf, id='ftx') + order = { + 'type': STOPLOSS_ORDERTYPE, + 'price': 1500, + } + assert exchange.stoploss_adjust(1501, order) + assert not exchange.stoploss_adjust(1499, order) + # Test with invalid order case ... + order['type'] = 'stop_loss_limit' + assert not exchange.stoploss_adjust(1501, order) diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py index d63dd66cc..0950979cf 100644 --- a/tests/exchange/test_kraken.py +++ b/tests/exchange/test_kraken.py @@ -11,6 +11,8 @@ from freqtrade.exceptions import (DependencyException, InvalidOrderException, from tests.conftest import get_patched_exchange from tests.exchange.test_exchange import ccxt_exceptionhandlers +STOPLOSS_ORDERTYPE = 'stop-loss' + def test_buy_kraken_trading_agreement(default_conf, mocker): api_mock = MagicMock() @@ -159,7 +161,6 @@ def test_get_balances_prod(default_conf, mocker): def test_stoploss_order_kraken(default_conf, mocker): api_mock = MagicMock() order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) - order_type = 'stop-loss' api_mock.create_order = MagicMock(return_value={ 'id': order_id, @@ -187,7 +188,7 @@ def test_stoploss_order_kraken(default_conf, mocker): assert 'info' in order assert order['id'] == order_id assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' - assert api_mock.create_order.call_args_list[0][1]['type'] == order_type + assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 assert api_mock.create_order.call_args_list[0][1]['price'] == 220 @@ -218,7 +219,6 @@ def test_stoploss_order_kraken(default_conf, mocker): def test_stoploss_order_dry_run_kraken(default_conf, mocker): api_mock = MagicMock() - order_type = 'stop-loss' default_conf['dry_run'] = True mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) @@ -233,7 +233,7 @@ def test_stoploss_order_dry_run_kraken(default_conf, mocker): assert 'info' in order assert 'type' in order - assert order['type'] == order_type + assert order['type'] == STOPLOSS_ORDERTYPE assert order['price'] == 220 assert order['amount'] == 1 @@ -241,7 +241,7 @@ def test_stoploss_order_dry_run_kraken(default_conf, mocker): def test_stoploss_adjust_kraken(mocker, default_conf): exchange = get_patched_exchange(mocker, default_conf, id='kraken') order = { - 'type': 'stop-loss', + 'type': STOPLOSS_ORDERTYPE, 'price': 1500, } assert exchange.stoploss_adjust(1501, order) From 68a59fd26d720efa3d22f9f67bbafac8ce2d280b Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Mar 2020 20:58:05 +0100 Subject: [PATCH 24/54] Add Hint to suggest this is still broken --- freqtrade/exchange/ftx.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index d06d49e5a..97257530e 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -28,7 +28,8 @@ class Ftx(Exchange): def stoploss(self, pair: str, amount: float, stop_price: float, order_types: Dict) -> Dict: """ Creates a stoploss market order. - Stoploss market orders is the only stoploss type supported by kraken. + Stoploss market orders is the only stoploss type supported by ftx. + TODO: This doesnot work yet as the order cannot be aquired via fetch_orders - so Freqtrade assumes the order as always missing. """ ordertype = "stop" From a808fb3b1068baa7a1c522ac4840ac93d8438b07 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Mar 2020 15:32:52 +0100 Subject: [PATCH 25/54] versionbump ccxt to first version that has FTX implemented correctly --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 20963a15f..6d832e3f5 100644 --- a/setup.py +++ b/setup.py @@ -63,7 +63,7 @@ setup(name='freqtrade', tests_require=['pytest', 'pytest-asyncio', 'pytest-cov', 'pytest-mock', ], install_requires=[ # from requirements-common.txt - 'ccxt>=1.18.1080', + 'ccxt>=1.24.96', 'SQLAlchemy', 'python-telegram-bot', 'arrow', From d90d6ed5d01283040b61416925a19dd1541fe037 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Mar 2020 15:50:33 +0100 Subject: [PATCH 26/54] Add ftx to tested exchanges --- tests/exchange/test_exchange.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 32163f696..d8950dd09 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -25,7 +25,7 @@ from freqtrade.resolvers.exchange_resolver import ExchangeResolver from tests.conftest import get_patched_exchange, log_has, log_has_re # Make sure to always keep one exchange here which is NOT subclassed!! -EXCHANGES = ['bittrex', 'binance', 'kraken', ] +EXCHANGES = ['bittrex', 'binance', 'kraken', 'ftx'] # Source: https://stackoverflow.com/questions/29881236/how-to-mock-asyncio-coroutines @@ -1258,7 +1258,8 @@ def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name): exchange._async_get_candle_history = Mock(wraps=mock_candle_hist) # one_call calculation * 1.8 should do 2 calls - since = 5 * 60 * 500 * 1.8 + + since = 5 * 60 * exchange._ft_has['ohlcv_candle_limit'] * 1.8 ret = exchange.get_historic_ohlcv(pair, "5m", int((arrow.utcnow().timestamp - since) * 1000)) assert exchange._async_get_candle_history.call_count == 2 From f83c1c5abf5d2b20ec29adfc84c85d3bcca6911d Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Mar 2020 17:01:11 +0100 Subject: [PATCH 27/54] Use get_stoploss_order and cancel_stoploss_order This allows exchanges to use stoploss which don't have the same endpoints --- freqtrade/exchange/exchange.py | 8 ++++++- freqtrade/exchange/ftx.py | 44 ++++++++++++++++++++++++++++++++++ freqtrade/freqtradebot.py | 10 ++++---- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 038fc22bc..e666b07ec 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -79,7 +79,7 @@ class Exchange: if config['dry_run']: logger.info('Instance is running with dry_run enabled') - + logger.info(f"Using CCXT {ccxt.__version__}") exchange_config = config['exchange'] # Deep merge ft_has with default ft_has options @@ -952,6 +952,9 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e + # Assign method to get_stoploss_order to allow easy overriding in other classes + cancel_stoploss_order = cancel_order + def is_cancel_order_result_suitable(self, corder) -> bool: if not isinstance(corder, dict): return False @@ -1004,6 +1007,9 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e + # Assign method to get_stoploss_order to allow easy overriding in other classes + get_stoploss_order = get_order + @retrier def fetch_l2_order_book(self, pair: str, limit: int = 100) -> dict: """ diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 97257530e..70f140ac5 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -7,6 +7,7 @@ import ccxt from freqtrade.exceptions import (DependencyException, InvalidOrderException, OperationalException, TemporaryError) from freqtrade.exchange import Exchange +from freqtrade.exchange.common import retrier logger = logging.getLogger(__name__) @@ -66,3 +67,46 @@ class Ftx(Exchange): f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: raise OperationalException(e) from e + + @retrier + def get_stoploss_order(self, order_id: str, pair: str) -> Dict: + if self._config['dry_run']: + try: + order = self._dry_run_open_orders[order_id] + return order + except KeyError as e: + # Gracefully handle errors with dry-run orders. + raise InvalidOrderException( + f'Tried to get an invalid dry-run-order (id: {order_id}). Message: {e}') from e + try: + orders = self._api.fetch_orders('BNB/USD', None, params={'type': 'stop'}) + + order = [order for order in orders if order['id'] == order_id] + if len(order) == 1: + return order[0] + else: + raise InvalidOrderException(f"Could not get Stoploss Order for id {order_id}") + + except ccxt.InvalidOrder as e: + raise InvalidOrderException( + f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + f'Could not get order due to {e.__class__.__name__}. Message: {e}') from e + except ccxt.BaseError as e: + raise OperationalException(e) from e + + @retrier + def cancel_stoploss_order(self, order_id: str, pair: str) -> None: + if self._config['dry_run']: + return + try: + return self._api.cancel_order(order_id, pair, params={'type': 'stop'}) + except ccxt.InvalidOrder as e: + raise InvalidOrderException( + f'Could not cancel order. Message: {e}') from e + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e + except ccxt.BaseError as e: + raise OperationalException(e) from e diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a74b0a5a1..4e4fe6e11 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -774,13 +774,13 @@ class FreqtradeBot: try: # First we check if there is already a stoploss on exchange - stoploss_order = self.exchange.get_order(trade.stoploss_order_id, trade.pair) \ + stoploss_order = self.exchange.get_stoploss_order(trade.stoploss_order_id, trade.pair) \ if trade.stoploss_order_id else None except InvalidOrderException as exception: logger.warning('Unable to fetch stoploss order: %s', exception) # We check if stoploss order is fulfilled - if stoploss_order and stoploss_order['status'] == 'closed': + if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'): trade.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value self.update_trade_state(trade, stoploss_order, sl_order=True) # Lock pair for one candle to prevent immediate rebuys @@ -807,7 +807,7 @@ class FreqtradeBot: return False # If stoploss order is canceled for some reason we add it - if stoploss_order and stoploss_order['status'] == 'canceled': + if stoploss_order and stoploss_order['status'] in ('canceled', 'cancelled'): if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, rate=trade.stop_loss): return False @@ -840,7 +840,7 @@ class FreqtradeBot: logger.info('Trailing stoploss: cancelling current stoploss on exchange (id:{%s}) ' 'in order to add another one ...', order['id']) try: - self.exchange.cancel_order(order['id'], trade.pair) + self.exchange.cancel_stoploss_order(order['id'], trade.pair) except InvalidOrderException: logger.exception(f"Could not cancel stoploss order {order['id']} " f"for pair {trade.pair}") @@ -1068,7 +1068,7 @@ class FreqtradeBot: # First cancelling stoploss on exchange ... if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id: try: - self.exchange.cancel_order(trade.stoploss_order_id, trade.pair) + self.exchange.cancel_stoploss_order(trade.stoploss_order_id, trade.pair) except InvalidOrderException: logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}") From cf50c1cb7be618f48ab9268be506a5b18765d871 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Mar 2020 17:01:45 +0100 Subject: [PATCH 28/54] Add tests for new exchange methods --- tests/exchange/test_exchange.py | 51 +++++++++++++++++++++++++++++++++ tests/exchange/test_ftx.py | 35 +++++++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index d8950dd09..c06b934ba 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1734,6 +1734,7 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name): default_conf['dry_run'] = True exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) assert exchange.cancel_order(order_id='123', pair='TKN/BTC') == {} + assert exchange.cancel_stoploss_order(order_id='123', pair='TKN/BTC') == {} @pytest.mark.parametrize("exchange_name", EXCHANGES) @@ -1818,6 +1819,25 @@ def test_cancel_order(default_conf, mocker, exchange_name): order_id='_', pair='TKN/BTC') +@pytest.mark.parametrize("exchange_name", EXCHANGES) +def test_cancel_stoploss_order(default_conf, mocker, exchange_name): + default_conf['dry_run'] = False + api_mock = MagicMock() + api_mock.cancel_order = MagicMock(return_value=123) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) + assert exchange.cancel_stoploss_order(order_id='_', pair='TKN/BTC') == 123 + + with pytest.raises(InvalidOrderException): + api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) + exchange.cancel_stoploss_order(order_id='_', pair='TKN/BTC') + assert api_mock.cancel_order.call_count == 1 + + ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, + "cancel_stoploss_order", "cancel_order", + order_id='_', pair='TKN/BTC') + + @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_get_order(default_conf, mocker, exchange_name): default_conf['dry_run'] = True @@ -1847,6 +1867,37 @@ def test_get_order(default_conf, mocker, exchange_name): order_id='_', pair='TKN/BTC') +@pytest.mark.parametrize("exchange_name", EXCHANGES) +def test_get_stoploss_order(default_conf, mocker, exchange_name): + # Don't test FTX here - that needs a seperate test + if exchange_name == 'ftx': + return + default_conf['dry_run'] = True + order = MagicMock() + order.myid = 123 + exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) + exchange._dry_run_open_orders['X'] = order + assert exchange.get_stoploss_order('X', 'TKN/BTC').myid == 123 + + with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'): + exchange.get_stoploss_order('Y', 'TKN/BTC') + + default_conf['dry_run'] = False + api_mock = MagicMock() + api_mock.fetch_order = MagicMock(return_value=456) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) + assert exchange.get_stoploss_order('X', 'TKN/BTC') == 456 + + with pytest.raises(InvalidOrderException): + api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) + exchange.get_stoploss_order(order_id='_', pair='TKN/BTC') + assert api_mock.fetch_order.call_count == 1 + + ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, + 'get_stoploss_order', 'fetch_order', + order_id='_', pair='TKN/BTC') + @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_name(default_conf, mocker, exchange_name): exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index f17d5b42e..2b75e5324 100644 --- a/tests/exchange/test_ftx.py +++ b/tests/exchange/test_ftx.py @@ -9,7 +9,7 @@ import pytest from freqtrade.exceptions import (DependencyException, InvalidOrderException, OperationalException, TemporaryError) from tests.conftest import get_patched_exchange - +from .test_exchange import ccxt_exceptionhandlers STOPLOSS_ORDERTYPE = 'stop' @@ -104,3 +104,36 @@ def test_stoploss_adjust_ftx(mocker, default_conf): # Test with invalid order case ... order['type'] = 'stop_loss_limit' assert not exchange.stoploss_adjust(1501, order) + + +def test_get_stoploss_order(default_conf, mocker): + default_conf['dry_run'] = True + order = MagicMock() + order.myid = 123 + exchange = get_patched_exchange(mocker, default_conf, id='ftx') + exchange._dry_run_open_orders['X'] = order + assert exchange.get_stoploss_order('X', 'TKN/BTC').myid == 123 + + with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'): + exchange.get_stoploss_order('Y', 'TKN/BTC') + + default_conf['dry_run'] = False + api_mock = MagicMock() + api_mock.fetch_orders = MagicMock(return_value=[{'id': 'X', 'status': '456'}]) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx') + assert exchange.get_stoploss_order('X', 'TKN/BTC')['status'] == '456' + + api_mock.fetch_orders = MagicMock(return_value=[{'id': 'Y', 'status': '456'}]) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx') + with pytest.raises(InvalidOrderException, match=r"Could not get Stoploss Order for id X"): + exchange.get_stoploss_order('X', 'TKN/BTC')['status'] + + with pytest.raises(InvalidOrderException): + api_mock.fetch_orders = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx') + exchange.get_stoploss_order(order_id='_', pair='TKN/BTC') + assert api_mock.fetch_orders.call_count == 1 + + ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'ftx', + 'get_stoploss_order', 'fetch_orders', + order_id='_', pair='TKN/BTC') From 3174f37b41b515a886673501055ce4fe4d394947 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Mar 2020 17:02:47 +0100 Subject: [PATCH 29/54] adapt tests to use stoploss_* methods --- tests/test_freqtradebot.py | 33 ++++++++++++++++++--------------- tests/test_integration.py | 4 ++-- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 487e3a60e..dc5a5c7d3 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1126,7 +1126,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, trade.stoploss_order_id = 100 hanging_stoploss_order = MagicMock(return_value={'status': 'open'}) - mocker.patch('freqtrade.exchange.Exchange.get_order', hanging_stoploss_order) + mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', hanging_stoploss_order) assert freqtrade.handle_stoploss_on_exchange(trade) is False assert trade.stoploss_order_id == 100 @@ -1139,7 +1139,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, trade.stoploss_order_id = 100 canceled_stoploss_order = MagicMock(return_value={'status': 'canceled'}) - mocker.patch('freqtrade.exchange.Exchange.get_order', canceled_stoploss_order) + mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', canceled_stoploss_order) stoploss.reset_mock() assert freqtrade.handle_stoploss_on_exchange(trade) is False @@ -1164,7 +1164,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, 'average': 2, 'amount': limit_buy_order['amount'], }) - mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hit) + mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_order_hit) assert freqtrade.handle_stoploss_on_exchange(trade) is True assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog) assert trade.stoploss_order_id is None @@ -1183,7 +1183,8 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, # It should try to add stoploss order trade.stoploss_order_id = 100 stoploss.reset_mock() - mocker.patch('freqtrade.exchange.Exchange.get_order', side_effect=InvalidOrderException()) + mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', + side_effect=InvalidOrderException()) mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss) freqtrade.handle_stoploss_on_exchange(trade) assert stoploss.call_count == 1 @@ -1214,7 +1215,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, buy=MagicMock(return_value={'id': limit_buy_order['id']}), sell=MagicMock(return_value={'id': limit_sell_order['id']}), get_fee=fee, - get_order=MagicMock(return_value={'status': 'canceled'}), + get_stoploss_order=MagicMock(return_value={'status': 'canceled'}), stoploss=MagicMock(side_effect=DependencyException()), ) freqtrade = FreqtradeBot(default_conf) @@ -1331,7 +1332,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, } }) - mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging) + mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_order_hanging) # stoploss initially at 5% assert freqtrade.handle_trade(trade) is False @@ -1346,7 +1347,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, cancel_order_mock = MagicMock() stoploss_order_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock) + mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', cancel_order_mock) mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss_order_mock) # stoploss should not be updated as the interval is 60 seconds @@ -1429,8 +1430,9 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c 'stopPrice': '0.1' } } - mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException()) - mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging) + mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', + side_effect=InvalidOrderException()) + mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_order_hanging) freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", caplog) @@ -1439,7 +1441,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c # Fail creating stoploss order caplog.clear() - cancel_mock = mocker.patch("freqtrade.exchange.Exchange.cancel_order", MagicMock()) + cancel_mock = mocker.patch("freqtrade.exchange.Exchange.cancel_stoploss_order", MagicMock()) mocker.patch("freqtrade.exchange.Exchange.stoploss", side_effect=DependencyException()) freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) assert cancel_mock.call_count == 1 @@ -1510,7 +1512,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, } }) - mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging) + mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_order_hanging) # stoploss initially at 20% as edge dictated it. assert freqtrade.handle_trade(trade) is False @@ -1519,7 +1521,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, cancel_order_mock = MagicMock() stoploss_order_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock) + mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', cancel_order_mock) mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss_order_mock) # price goes down 5% @@ -2632,7 +2634,8 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException()) + mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', + side_effect=InvalidOrderException()) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=300)) sellmock = MagicMock() patch_exchange(mocker) @@ -2680,7 +2683,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke amount_to_precision=lambda s, x, y: y, price_to_precision=lambda s, x, y: y, stoploss=stoploss, - cancel_order=cancel_order, + cancel_stoploss_order=cancel_order, ) freqtrade = FreqtradeBot(default_conf) @@ -2771,7 +2774,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f "fee": None, "trades": None }) - mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_executed) + mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_executed) freqtrade.exit_positions(trades) assert trade.stoploss_order_id is None diff --git a/tests/test_integration.py b/tests/test_integration.py index 1396e86f5..57960503e 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -62,8 +62,8 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, get_fee=fee, amount_to_precision=lambda s, x, y: y, price_to_precision=lambda s, x, y: y, - get_order=stoploss_order_mock, - cancel_order=cancel_order_mock, + get_stoploss_order=stoploss_order_mock, + cancel_stoploss_order=cancel_order_mock, ) mocker.patch.multiple( From 11ebdefd09ef1d587e326b0d7d23020f32d71ebb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Apr 2020 19:52:21 +0200 Subject: [PATCH 30/54] Fix bug after rebase --- freqtrade/exchange/ftx.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 70f140ac5..0bd32b581 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -97,9 +97,9 @@ class Ftx(Exchange): raise OperationalException(e) from e @retrier - def cancel_stoploss_order(self, order_id: str, pair: str) -> None: + def cancel_stoploss_order(self, order_id: str, pair: str) -> Dict: if self._config['dry_run']: - return + return {} try: return self._api.cancel_order(order_id, pair, params={'type': 'stop'}) except ccxt.InvalidOrder as e: From b58fd179f231ed304d8d47d85c8d43169218b0a3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 10:05:14 +0200 Subject: [PATCH 31/54] Don't hardcode pair ... --- freqtrade/exchange/ftx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 0bd32b581..1bc97c4d3 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -79,7 +79,7 @@ class Ftx(Exchange): raise InvalidOrderException( f'Tried to get an invalid dry-run-order (id: {order_id}). Message: {e}') from e try: - orders = self._api.fetch_orders('BNB/USD', None, params={'type': 'stop'}) + orders = self._api.fetch_orders(pair, None, params={'type': 'stop'}) order = [order for order in orders if order['id'] == order_id] if len(order) == 1: From 1d9aeef792a4eaa100646f04aaf6e5d47a2169b9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 10:36:23 +0200 Subject: [PATCH 32/54] Support stop order in persistence --- freqtrade/persistence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 823bf6dc0..2e02c3999 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -374,7 +374,7 @@ class Trade(_DECL_BASE): elif order_type in ('market', 'limit') and order['side'] == 'sell': self.close(order['price']) logger.info('%s_SELL has been fulfilled for %s.', order_type.upper(), self) - elif order_type in ('stop_loss_limit', 'stop-loss'): + elif order_type in ('stop_loss_limit', 'stop-loss', 'stop'): self.stoploss_order_id = None self.close_rate_requested = self.stop_loss logger.info('%s is hit for %s.', order_type.upper(), self) From 77a62b845a1d5c13146966e3609f40e99f39fbf2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 11:24:23 +0200 Subject: [PATCH 33/54] Fix some comments --- freqtrade/exchange/ftx.py | 2 +- tests/exchange/test_exchange.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 1bc97c4d3..20d643a83 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -30,7 +30,6 @@ class Ftx(Exchange): """ Creates a stoploss market order. Stoploss market orders is the only stoploss type supported by ftx. - TODO: This doesnot work yet as the order cannot be aquired via fetch_orders - so Freqtrade assumes the order as always missing. """ ordertype = "stop" @@ -46,6 +45,7 @@ class Ftx(Exchange): params = self._params.copy() amount = self.amount_to_precision(pair, amount) + # set orderPrice to place limit order (?) order = self._api.create_order(symbol=pair, type=ordertype, side='sell', amount=amount, price=stop_price, params=params) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index c06b934ba..dc272de36 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1898,6 +1898,7 @@ def test_get_stoploss_order(default_conf, mocker, exchange_name): 'get_stoploss_order', 'fetch_order', order_id='_', pair='TKN/BTC') + @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_name(default_conf, mocker, exchange_name): exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) From f0eb0bc350a876c514deb63e331cedac6707a884 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Jun 2020 11:33:40 +0200 Subject: [PATCH 34/54] Support limit orders --- freqtrade/exchange/ftx.py | 12 +++++++++--- tests/exchange/test_ftx.py | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 20d643a83..73347f1eb 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -28,9 +28,13 @@ class Ftx(Exchange): def stoploss(self, pair: str, amount: float, stop_price: float, order_types: Dict) -> Dict: """ - Creates a stoploss market order. - Stoploss market orders is the only stoploss type supported by ftx. + Creates a stoploss order. + depending on order_types.stoploss configuration, uses 'market' or limit order. + + Limit orders are defined by having orderPrice set, otherwise a market order is used. """ + limit_price_pct = order_types.get('stoploss_on_exchange_limit_ratio', 0.99) + limit_rate = stop_price * limit_price_pct ordertype = "stop" @@ -43,9 +47,11 @@ class Ftx(Exchange): try: params = self._params.copy() + if order_types.get('stoploss', 'market') == 'limit': + # set orderPrice to place limit order, otherwise it's a market order + params['orderPrice'] = limit_rate amount = self.amount_to_precision(pair, amount) - # set orderPrice to place limit order (?) order = self._api.create_order(symbol=pair, type=ordertype, side='sell', amount=amount, price=stop_price, params=params) diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index 2b75e5324..bead63096 100644 --- a/tests/exchange/test_ftx.py +++ b/tests/exchange/test_ftx.py @@ -34,6 +34,14 @@ def test_stoploss_order_ftx(default_conf, mocker): # stoploss_on_exchange_limit_ratio is irrelevant for ftx market orders order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=190, order_types={'stoploss_on_exchange_limit_ratio': 1.05}) + + assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' + assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE + assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' + assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 + assert api_mock.create_order.call_args_list[0][1]['price'] == 190 + assert 'orderPrice' not in api_mock.create_order.call_args_list[0][1]['params'] + assert api_mock.create_order.call_count == 1 api_mock.create_order.reset_mock() @@ -48,6 +56,22 @@ def test_stoploss_order_ftx(default_conf, mocker): assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 assert api_mock.create_order.call_args_list[0][1]['price'] == 220 + assert 'orderPrice' not in api_mock.create_order.call_args_list[0][1]['params'] + + api_mock.create_order.reset_mock() + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, + order_types={'stoploss': 'limit'}) + + assert 'id' in order + assert 'info' in order + assert order['id'] == order_id + assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' + assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE + assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' + assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 + assert api_mock.create_order.call_args_list[0][1]['price'] == 220 + assert 'orderPrice' in api_mock.create_order.call_args_list[0][1]['params'] + assert api_mock.create_order.call_args_list[0][1]['params']['orderPrice'] == 217.8 # test exception handling with pytest.raises(DependencyException): From 2f07d21629b8aade68a5916303652133bf57d3cd Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 3 Jun 2020 20:20:39 +0200 Subject: [PATCH 35/54] Update documentation with FTX Stoploss on exchange --- docs/configuration.md | 12 ++++++++---- docs/exchanges.md | 5 +++++ docs/stoploss.md | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index da0f015e8..467190463 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -272,7 +272,7 @@ the static list of pairs) if we should buy. ### Understand order_types -The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and defines stoploss on exchange update interval in seconds. +The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`, `emergencysell`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and defines stoploss on exchange update interval in seconds. This allows to buy using limit orders, sell using limit-orders, and create stoplosses using using market orders. It also allows to set the @@ -288,8 +288,12 @@ If this is configured, the following 4 values (`buy`, `sell`, `stoploss` and `emergencysell` is an optional value, which defaults to `market` and is used when creating stoploss on exchange orders fails. The below is the default which is used if this is not configured in either strategy or configuration file. -Since `stoploss_on_exchange` uses limit orders, the exchange needs 2 prices, the stoploss_price and the Limit price. -`stoploss` defines the stop-price - and limit should be slightly below this. This defaults to 0.99 / 1% (configurable via `stoploss_on_exchange_limit_ratio`). +Not all Exchanges support `stoploss_on_exchange`. If an exchange supports both limit and market stoploss orders, then the value of `stoploss` will be used to determine the stoploss type. + +If `stoploss_on_exchange` uses limit orders, the exchange needs 2 prices, the stoploss_price and the Limit price. +`stoploss` defines the stop-price - and limit should be slightly below this. + +This defaults to 0.99 / 1% (configurable via `stoploss_on_exchange_limit_ratio`). Calculation example: we bought the asset at 100$. Stop-price is 95$, then limit would be `95 * 0.99 = 94.05$` - so the stoploss will happen between 95$ and 94.05$. @@ -331,7 +335,7 @@ Configuration: refer to [the stoploss documentation](stoploss.md). !!! Note - If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new order. + If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new stoploss order. !!! Warning "Warning: stoploss_on_exchange failures" If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting the `emergencysell` value in the `order_types` dictionary - however this is not advised. diff --git a/docs/exchanges.md b/docs/exchanges.md index 81f017023..cd210eb69 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -64,6 +64,11 @@ print(res) ## FTX +!!! Tip "Stoploss on Exchange" + FTX supports `stoploss_on_exchange` and can use both stop-loss-market and stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it. + You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide. + + ### Using subaccounts To use subaccounts with FTX, you need to edit the configuration and add the following: diff --git a/docs/stoploss.md b/docs/stoploss.md index 0e43817ec..cd90a71b4 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -27,7 +27,7 @@ So this parameter will tell the bot how often it should update the stoploss orde This same logic will reapply a stoploss order on the exchange should you cancel it accidentally. !!! Note - Stoploss on exchange is only supported for Binance (stop-loss-limit) and Kraken (stop-loss-market) as of now. + Stoploss on exchange is only supported for Binance (stop-loss-limit), Kraken (stop-loss-market) and FTX (stop limit and stop-market) as of now. ## Static Stop Loss From 69ac5c1ac7a6178113b8f6df8650661f6f3fa284 Mon Sep 17 00:00:00 2001 From: Felipe Lambert Date: Mon, 8 Jun 2020 14:55:28 -0300 Subject: [PATCH 36/54] change hyperopt return to better copy to strategy file --- freqtrade/optimize/hyperopt.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 3a28de785..58bcbd208 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -12,7 +12,7 @@ from math import ceil from collections import OrderedDict from operator import itemgetter from pathlib import Path -from pprint import pprint +from pprint import pformat from typing import Any, Dict, List, Optional import rapidjson @@ -244,11 +244,21 @@ class Hyperopt: def _params_pretty_print(params, space: str, header: str) -> None: if space in params: space_params = Hyperopt._space_params(params, space, 5) + print(f"\n # {header}") if space == 'stoploss': - print(header, space_params.get('stoploss')) + print(" stoploss =", space_params.get('stoploss')) + elif space == 'roi': + minimal_roi_result = rapidjson.dumps( + OrderedDict( + (str(k), v) for k, v in space_params.items() + ), + default=str, indent=4, number_mode=rapidjson.NM_NATIVE) + minimal_roi_result = minimal_roi_result.replace("\n", "\n ") + print(f" minimal_roi = {minimal_roi_result}") else: - print(header) - pprint(space_params, indent=4) + params_result = pformat(space_params, indent=4).replace("}", "\n}") + params_result = params_result.replace("{", "{\n ").replace("\n", "\n ") + print(f" {space}_params = {params_result}") @staticmethod def _space_params(params, space: str, r: int = None) -> Dict: From 3d9b1077612c272267363c72a1653ec39ffdaa83 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sat, 13 Jun 2020 17:12:37 +0300 Subject: [PATCH 37/54] Changes after review --- freqtrade/optimize/hyperopt.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 58bcbd208..1136fc4a7 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -230,6 +230,9 @@ class Hyperopt: if space in ['buy', 'sell']: result_dict.setdefault('params', {}).update(space_params) elif space == 'roi': + # TODO: get rid of OrderedDict when support for python 3.6 will be + # dropped (dicts keep the order as the language feature) + # Convert keys in min_roi dict to strings because # rapidjson cannot dump dicts with integer keys... # OrderedDict is used to keep the numeric order of the items @@ -244,21 +247,24 @@ class Hyperopt: def _params_pretty_print(params, space: str, header: str) -> None: if space in params: space_params = Hyperopt._space_params(params, space, 5) - print(f"\n # {header}") + params_result = f"\n# {header}\n" if space == 'stoploss': - print(" stoploss =", space_params.get('stoploss')) + params_result += f"stoploss = {space_params.get('stoploss')}" elif space == 'roi': minimal_roi_result = rapidjson.dumps( + # TODO: get rid of OrderedDict when support for python 3.6 will be + # dropped (dicts keep the order as the language feature) OrderedDict( (str(k), v) for k, v in space_params.items() ), default=str, indent=4, number_mode=rapidjson.NM_NATIVE) - minimal_roi_result = minimal_roi_result.replace("\n", "\n ") - print(f" minimal_roi = {minimal_roi_result}") + params_result += f"minimal_roi = {minimal_roi_result}" else: - params_result = pformat(space_params, indent=4).replace("}", "\n}") - params_result = params_result.replace("{", "{\n ").replace("\n", "\n ") - print(f" {space}_params = {params_result}") + params_result += f"{space}_params = {pformat(space_params, indent=4)}" + params_result = params_result.replace("}", "\n}").replace("{", "{\n ") + + params_result = params_result.replace("\n", "\n ") + print(params_result) @staticmethod def _space_params(params, space: str, r: int = None) -> Dict: From ea77edce0519bdd7664fd20fd65416f85bdbaada Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sat, 13 Jun 2020 18:54:54 +0300 Subject: [PATCH 38/54] Make flake happy --- freqtrade/optimize/hyperopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 1136fc4a7..153ae3861 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -251,9 +251,9 @@ class Hyperopt: if space == 'stoploss': params_result += f"stoploss = {space_params.get('stoploss')}" elif space == 'roi': - minimal_roi_result = rapidjson.dumps( # TODO: get rid of OrderedDict when support for python 3.6 will be # dropped (dicts keep the order as the language feature) + minimal_roi_result = rapidjson.dumps( OrderedDict( (str(k), v) for k, v in space_params.items() ), From f6f7c99b9c558a6410b98b4b884a5c952cce074a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 14 Jun 2020 06:31:05 +0200 Subject: [PATCH 39/54] Adjust typography and add missing space Co-authored-by: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> --- freqtrade/exchange/ftx.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 73347f1eb..f16db96f5 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -60,7 +60,7 @@ class Ftx(Exchange): return order except ccxt.InsufficientFunds as e: raise DependencyException( - f'Insufficient funds to create {ordertype} sell order on market {pair}.' + f'Insufficient funds to create {ordertype} sell order on market {pair}. ' f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. ' f'Message: {e}') from e except ccxt.InvalidOrder as e: @@ -91,7 +91,7 @@ class Ftx(Exchange): if len(order) == 1: return order[0] else: - raise InvalidOrderException(f"Could not get Stoploss Order for id {order_id}") + raise InvalidOrderException(f"Could not get stoploss order for id {order_id}") except ccxt.InvalidOrder as e: raise InvalidOrderException( From 534c242d1b44f0cda20202da85632936fc1e6ab3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 14 Jun 2020 06:33:08 +0200 Subject: [PATCH 40/54] Apply typography to test too --- tests/exchange/test_ftx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index bead63096..75e98740c 100644 --- a/tests/exchange/test_ftx.py +++ b/tests/exchange/test_ftx.py @@ -149,7 +149,7 @@ def test_get_stoploss_order(default_conf, mocker): api_mock.fetch_orders = MagicMock(return_value=[{'id': 'Y', 'status': '456'}]) exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx') - with pytest.raises(InvalidOrderException, match=r"Could not get Stoploss Order for id X"): + with pytest.raises(InvalidOrderException, match=r"Could not get stoploss order for id X"): exchange.get_stoploss_order('X', 'TKN/BTC')['status'] with pytest.raises(InvalidOrderException): From 837aedb0c7622826a2e5b7f2fc2bf105e43d472c Mon Sep 17 00:00:00 2001 From: muletman <66912721+muletman@users.noreply.github.com> Date: Sun, 14 Jun 2020 17:03:18 +0100 Subject: [PATCH 41/54] Docs: Run multiple instances for new users This is my proposition of contribution for how new users could get up and running with multiple instances of the bot, based on the conversation I had on Slack with @hroff-1902 It appeared to me that the transparent creation and usage of the sqlite databases, and the necessity to create other databases to run multiple bots at the same time was not so straightforward to me in the first place, despite browsing through the docs. It is evident now ;) but that will maybe save time for devs if any other new user come on slack with the same issue. Thanks --- HowToRunMultipleInstances.md | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 HowToRunMultipleInstances.md diff --git a/HowToRunMultipleInstances.md b/HowToRunMultipleInstances.md new file mode 100644 index 000000000..a5ec4c10c --- /dev/null +++ b/HowToRunMultipleInstances.md @@ -0,0 +1,51 @@ +# How to run multiple instances of freqtrade simultaneously + +This page is meant to be a quick tip for new users on how to run multiple bots a the same time, on the same computer (or other devices). + +In order to keep track of your trades, profits, etc., freqtrade is using a SQLite database where it stores various types of information such as the trades you performed in the past and the current position(s) you are holding at any time. This allow you to keep track of your profits, but most importantly, keep track of ongoing activity if the bot process would finish unexpectedly for one or another reason. + +As for various other things, upon docker or manual install, [freqtrade will create by default two different databases, one for dry-run, and the other for live trades.](https://www.freqtrade.io/en/latest/docker/#create-your-database-file) + +By default, executing the trade command in the command line interface, without specifying any database (`--db-url`) argument, freqtrade will store your trades and performance history in one of these two default databases, depending if dry-run mode is enabled or not. These databases are actual .sqlite files which are stored in your main freqtrade folder, under the name `tradesv3.dryrun.sqlite` for the dry-run mode, and `tradesv3.sqlite` for the live mode. + +The optional argument to the trade command used to specify the path of these files is `--db-url`. So when you are starting a bot with only the config and strategy arguments in dry-run mode for instance : + +``` +freqtrade trade -c MyConfig.json -s MyStrategy +``` + +is equivalent to : + +``` +freqtrade trade -c MyConfig.json -s MyStrategy --db-url sqlite:///tradesv3.dryrun.sqlite +``` + +That means that if you are running the trade command in two different terminals, for example to test your strategy both for trades in USDT and in another instance for trades in BTC, you will have to run them with different databases. Even if you are using the same configuration file and strategy. + + If you specify the URL of a database which does not exist, freqtrade will create one with the name you specified. So for example if you want to test your custom strategy in BTC vs USDT, you could type in one terminal : + +``` +freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite://path/tradesBTC.dryrun.sqlite +``` + +and in the other + +``` +freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite://path/path/tradesUSDT.dryrun.sqlite +``` + +Conversely, if you wish to do the same thing in production mode, you will also have to create at least one new database (in addition to the default one) and specify the path to the "live" databases, for example : + +``` +freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite://path/tradesBTC.live.sqlite +``` + +and in the other + +``` +freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite://path/path/tradesUSDT.live.sqlite +``` + +For more information regarding usage of the sqlite databases, for example to manually enter or remove trades, please refer to the following page : + +https://www.freqtrade.io/en/latest/sql_cheatsheet/ From d337fb6c6a4a3ca3e3f7bfb542b6bf7db0a788a8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Jun 2020 06:35:31 +0200 Subject: [PATCH 42/54] Update some comments --- freqtrade/commands/pairlist_commands.py | 1 - freqtrade/optimize/hyperopt_interface.py | 4 ++-- freqtrade/resolvers/strategy_resolver.py | 2 +- tests/strategy/test_strategy.py | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/freqtrade/commands/pairlist_commands.py b/freqtrade/commands/pairlist_commands.py index dffe0c82e..77bcb04b4 100644 --- a/freqtrade/commands/pairlist_commands.py +++ b/freqtrade/commands/pairlist_commands.py @@ -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 timeframe set in the config pairlists = PairListManager(exchange, config) pairlists.refresh_pairlist() results[curr] = pairlists.whitelist diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 00353cbf4..65069b984 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -31,14 +31,14 @@ class IHyperOpt(ABC): Class attributes you can use: ticker_interval -> int: value of the ticker interval to use for the strategy """ - ticker_interval: str # deprecated + 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['timeframe']) # DEPRECTED + IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED IHyperOpt.timeframe = str(config['timeframe']) @staticmethod diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 26bce01ca..121a04877 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -54,7 +54,7 @@ class StrategyResolver(IResolver): # 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." + "DEPRECATED: Please migrate to using 'timeframe' instead of 'ticker_interval'." ) strategy.timeframe = strategy.ticker_interval diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 85cb8c132..240f3d8ec 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -386,7 +386,7 @@ def test_call_deprecated_function(result, monkeypatch, default_conf, caplog): assert isinstance(selldf, DataFrame) assert 'sell' in selldf - assert log_has('DEPRECATED: Please migrate to using timeframe instead of ticker_interval.', + assert log_has("DEPRECATED: Please migrate to using 'timeframe' instead of 'ticker_interval'.", caplog) From 1853350c7d344a0860d2f2d371ca211dd5f3bb4b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2020 09:01:05 +0000 Subject: [PATCH 43/54] Bump mkdocs-material from 5.2.3 to 5.3.0 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 5.2.3 to 5.3.0. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/5.2.3...5.3.0) Signed-off-by: dependabot-preview[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index b8ea338de..666cf5ac4 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,2 +1,2 @@ -mkdocs-material==5.2.3 +mkdocs-material==5.3.0 mdx_truly_sane_lists==1.2 From d66050522c30da1d7b39fee9ff1ac5b906f39587 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2020 09:02:14 +0000 Subject: [PATCH 44/54] Bump ccxt from 1.29.52 to 1.30.2 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.29.52 to 1.30.2. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/doc/exchanges-by-country.rst) - [Commits](https://github.com/ccxt/ccxt/compare/1.29.52...1.30.2) Signed-off-by: dependabot-preview[bot] --- requirements-common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-common.txt b/requirements-common.txt index dab3a5da4..a6420d76a 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,6 +1,6 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs -ccxt==1.29.52 +ccxt==1.30.2 SQLAlchemy==1.3.17 python-telegram-bot==12.7 arrow==0.15.6 From f38f3643f55157e52523df7651df79002b319433 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2020 09:02:34 +0000 Subject: [PATCH 45/54] Bump pytest-cov from 2.9.0 to 2.10.0 Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.9.0 to 2.10.0. - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.9.0...v2.10.0) Signed-off-by: dependabot-preview[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index d62b768c1..d8d09a0c5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,7 +10,7 @@ flake8-tidy-imports==4.1.0 mypy==0.780 pytest==5.4.3 pytest-asyncio==0.12.0 -pytest-cov==2.9.0 +pytest-cov==2.10.0 pytest-mock==3.1.1 pytest-random-order==1.0.4 From df90d631fb3b32327dbae36d0e4d0b25205e7d98 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2020 10:05:20 +0000 Subject: [PATCH 46/54] Bump flake8 from 3.8.2 to 3.8.3 Bumps [flake8](https://gitlab.com/pycqa/flake8) from 3.8.2 to 3.8.3. - [Release notes](https://gitlab.com/pycqa/flake8/tags) - [Commits](https://gitlab.com/pycqa/flake8/compare/3.8.2...3.8.3) Signed-off-by: dependabot-preview[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index d8d09a0c5..840eff15f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ -r requirements-hyperopt.txt coveralls==2.0.0 -flake8==3.8.2 +flake8==3.8.3 flake8-type-annotations==0.1.0 flake8-tidy-imports==4.1.0 mypy==0.780 From e24ffebe691b3e90ac4c660edbb702aca1ca53c5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Jun 2020 19:24:33 +0200 Subject: [PATCH 47/54] Move Multiple instances section to advanced-setup.md --- HowToRunMultipleInstances.md | 51 ----------------------------------- docs/advanced-setup.md | 52 ++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 51 deletions(-) delete mode 100644 HowToRunMultipleInstances.md diff --git a/HowToRunMultipleInstances.md b/HowToRunMultipleInstances.md deleted file mode 100644 index a5ec4c10c..000000000 --- a/HowToRunMultipleInstances.md +++ /dev/null @@ -1,51 +0,0 @@ -# How to run multiple instances of freqtrade simultaneously - -This page is meant to be a quick tip for new users on how to run multiple bots a the same time, on the same computer (or other devices). - -In order to keep track of your trades, profits, etc., freqtrade is using a SQLite database where it stores various types of information such as the trades you performed in the past and the current position(s) you are holding at any time. This allow you to keep track of your profits, but most importantly, keep track of ongoing activity if the bot process would finish unexpectedly for one or another reason. - -As for various other things, upon docker or manual install, [freqtrade will create by default two different databases, one for dry-run, and the other for live trades.](https://www.freqtrade.io/en/latest/docker/#create-your-database-file) - -By default, executing the trade command in the command line interface, without specifying any database (`--db-url`) argument, freqtrade will store your trades and performance history in one of these two default databases, depending if dry-run mode is enabled or not. These databases are actual .sqlite files which are stored in your main freqtrade folder, under the name `tradesv3.dryrun.sqlite` for the dry-run mode, and `tradesv3.sqlite` for the live mode. - -The optional argument to the trade command used to specify the path of these files is `--db-url`. So when you are starting a bot with only the config and strategy arguments in dry-run mode for instance : - -``` -freqtrade trade -c MyConfig.json -s MyStrategy -``` - -is equivalent to : - -``` -freqtrade trade -c MyConfig.json -s MyStrategy --db-url sqlite:///tradesv3.dryrun.sqlite -``` - -That means that if you are running the trade command in two different terminals, for example to test your strategy both for trades in USDT and in another instance for trades in BTC, you will have to run them with different databases. Even if you are using the same configuration file and strategy. - - If you specify the URL of a database which does not exist, freqtrade will create one with the name you specified. So for example if you want to test your custom strategy in BTC vs USDT, you could type in one terminal : - -``` -freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite://path/tradesBTC.dryrun.sqlite -``` - -and in the other - -``` -freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite://path/path/tradesUSDT.dryrun.sqlite -``` - -Conversely, if you wish to do the same thing in production mode, you will also have to create at least one new database (in addition to the default one) and specify the path to the "live" databases, for example : - -``` -freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite://path/tradesBTC.live.sqlite -``` - -and in the other - -``` -freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite://path/path/tradesUSDT.live.sqlite -``` - -For more information regarding usage of the sqlite databases, for example to manually enter or remove trades, please refer to the following page : - -https://www.freqtrade.io/en/latest/sql_cheatsheet/ diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index 95480a2c6..9848d04c8 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -4,6 +4,58 @@ This page explains some advanced tasks and configuration options that can be per If you do not know what things mentioned here mean, you probably do not need it. +## Running multiple instances of Freqtrade + +This page is meant to be a quick tip for new users on how to run multiple bots a the same time, on the same computer (or other devices). + +In order to keep track of your trades, profits, etc., freqtrade is using a SQLite database where it stores various types of information such as the trades you performed in the past and the current position(s) you are holding at any time. This allow you to keep track of your profits, but most importantly, keep track of ongoing activity if the bot process would finish unexpectedly for one or another reason. + +As for various other things, upon docker or manual install, [freqtrade will create by default two different databases, one for dry-run, and the other for live trades.](https://www.freqtrade.io/en/latest/docker/#create-your-database-file) + +By default, executing the trade command in the command line interface, without specifying any database (`--db-url`) argument, freqtrade will store your trades and performance history in one of these two default databases, depending if dry-run mode is enabled or not. These databases are actual .sqlite files which are stored in your main freqtrade folder, under the name `tradesv3.dryrun.sqlite` for the dry-run mode, and `tradesv3.sqlite` for the live mode. + +The optional argument to the trade command used to specify the path of these files is `--db-url`. So when you are starting a bot with only the config and strategy arguments in dry-run mode for instance : + +``` bash +freqtrade trade -c MyConfig.json -s MyStrategy +``` + +is equivalent to: + +``` bash +freqtrade trade -c MyConfig.json -s MyStrategy --db-url sqlite:///tradesv3.dryrun.sqlite +``` + +That means that if you are running the trade command in two different terminals, for example to test your strategy both for trades in USDT and in another instance for trades in BTC, you will have to run them with different databases. Even if you are using the same configuration file and strategy. + + If you specify the URL of a database which does not exist, freqtrade will create one with the name you specified. So for example if you want to test your custom strategy in BTC vs USDT, you could type in one terminal : + +``` bash +freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite://path/tradesBTC.dryrun.sqlite +``` + +and in the other + +``` bash +freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite://path/path/tradesUSDT.dryrun.sqlite +``` + +Conversely, if you wish to do the same thing in production mode, you will also have to create at least one new database (in addition to the default one) and specify the path to the "live" databases, for example : + +``` bash +freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite://path/tradesBTC.live.sqlite +``` + +and in the other + +``` bash +freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite://path/path/tradesUSDT.live.sqlite +``` + +For more information regarding usage of the sqlite databases, for example to manually enter or remove trades, please refer to the following page: + +https://www.freqtrade.io/en/latest/sql_cheatsheet/ + ## Configure the bot running as a systemd service Copy the `freqtrade.service` file to your systemd user directory (usually `~/.config/systemd/user`) and update `WorkingDirectory` and `ExecStart` to match your setup. From 61ce18a4abb17b3c5c7d46701b62a4b4504410ce Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Jun 2020 19:35:57 +0200 Subject: [PATCH 48/54] Slightly reword certain things - add link in FAQ --- docs/advanced-setup.md | 56 ++++++++++++++++++++---------------------- docs/faq.md | 4 +++ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index 9848d04c8..21e428fa7 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -6,55 +6,51 @@ If you do not know what things mentioned here mean, you probably do not need it. ## Running multiple instances of Freqtrade -This page is meant to be a quick tip for new users on how to run multiple bots a the same time, on the same computer (or other devices). +This section will show you how to run multiple bots a the same time, on the same computer (or other devices). -In order to keep track of your trades, profits, etc., freqtrade is using a SQLite database where it stores various types of information such as the trades you performed in the past and the current position(s) you are holding at any time. This allow you to keep track of your profits, but most importantly, keep track of ongoing activity if the bot process would finish unexpectedly for one or another reason. +### Things to consider -As for various other things, upon docker or manual install, [freqtrade will create by default two different databases, one for dry-run, and the other for live trades.](https://www.freqtrade.io/en/latest/docker/#create-your-database-file) +* use different database files. +* use different telegram Bots (requires 2 different configuration files). +* use different ports (*applies only when webserver is enabled). -By default, executing the trade command in the command line interface, without specifying any database (`--db-url`) argument, freqtrade will store your trades and performance history in one of these two default databases, depending if dry-run mode is enabled or not. These databases are actual .sqlite files which are stored in your main freqtrade folder, under the name `tradesv3.dryrun.sqlite` for the dry-run mode, and `tradesv3.sqlite` for the live mode. +#### Different database files -The optional argument to the trade command used to specify the path of these files is `--db-url`. So when you are starting a bot with only the config and strategy arguments in dry-run mode for instance : +In order to keep track of your trades, profits, etc., freqtrade is using a SQLite database where it stores various types of information such as the trades you performed in the past and the current position(s) you are holding at any time. This allows you to keep track of your profits, but most importantly, keep track of ongoing activity if the bot process would be restarted or would be terminated unexpectently. + +Freqtrade will, by default, use seperate database files for dry-run and live bots (this assumes no database-url is given in either configuration nor via command line argument). +For live trading mode, the default database will be `tradesv3.sqlite`, and for dry-run, it will be `tradesv3.dryrun.sqlite`. + +The optional argument to the trade command used to specify the path of these files is `--db-url`, which requires a valid SQLAlchemy url. +So when you are starting a bot with only the config and strategy arguments in dry-run mode, the following 2 commands would have the same outcome. ``` bash freqtrade trade -c MyConfig.json -s MyStrategy -``` - -is equivalent to: - -``` bash +# is equivalent to freqtrade trade -c MyConfig.json -s MyStrategy --db-url sqlite:///tradesv3.dryrun.sqlite ``` -That means that if you are running the trade command in two different terminals, for example to test your strategy both for trades in USDT and in another instance for trades in BTC, you will have to run them with different databases. Even if you are using the same configuration file and strategy. +That means that if you are running the trade command in two different terminals, for example to test your strategy both for trades in USDT and in another instance for trades in BTC, you will have to run them with different databases. - If you specify the URL of a database which does not exist, freqtrade will create one with the name you specified. So for example if you want to test your custom strategy in BTC vs USDT, you could type in one terminal : +If you specify the URL of a database which does not exist, freqtrade will create one with the name you specified. So to test your custom strategy in BTC and USDT, you could use the following commands (in 2 seperate terminals): ``` bash -freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite://path/tradesBTC.dryrun.sqlite +# Terminal 1: +freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite:///user_data/tradesBTC.dryrun.sqlite +# Terminal 2: +freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite:///user_data/tradesUSDT.dryrun.sqlite ``` -and in the other +Conversely, if you wish to do the same thing in production mode, you will also have to create at least one new database (in addition to the default one) and specify the path to the "live" databases, for example: ``` bash -freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite://path/path/tradesUSDT.dryrun.sqlite +# Terminal 1: +freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite:///user_data/tradesBTC.live.sqlite +# Terminal 2: +freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite:///user_data/tradesUSDT.live.sqlite ``` -Conversely, if you wish to do the same thing in production mode, you will also have to create at least one new database (in addition to the default one) and specify the path to the "live" databases, for example : - -``` bash -freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite://path/tradesBTC.live.sqlite -``` - -and in the other - -``` bash -freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite://path/path/tradesUSDT.live.sqlite -``` - -For more information regarding usage of the sqlite databases, for example to manually enter or remove trades, please refer to the following page: - -https://www.freqtrade.io/en/latest/sql_cheatsheet/ +For more information regarding usage of the sqlite databases, for example to manually enter or remove trades, please refer to the [SQL Cheatsheet](sql_cheatsheet.md) ## Configure the bot running as a systemd service diff --git a/docs/faq.md b/docs/faq.md index 8e8a1bf35..31c49171d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -45,6 +45,10 @@ the tutorial [here|Testing-new-strategies-with-Hyperopt](bot-usage.md#hyperopt-c You can use the `/forcesell all` command from Telegram. +### I want to run multiple bots on the same machine + +Please look at the [advanced setup documentation Page](advanced-setup.md#running-multiple-instances-of-freqtrade). + ### I'm getting the "RESTRICTED_MARKET" message in the log Currently known to happen for US Bittrex users. From 5e4dd44155958487d5ae141953951f9471a3843f Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Jun 2020 20:55:06 +0200 Subject: [PATCH 49/54] Fix formatting --- docs/advanced-setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index 21e428fa7..d3f692f33 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -6,13 +6,13 @@ If you do not know what things mentioned here mean, you probably do not need it. ## Running multiple instances of Freqtrade -This section will show you how to run multiple bots a the same time, on the same computer (or other devices). +This section will show you how to run multiple bots a the same time, on the same machine. ### Things to consider * use different database files. * use different telegram Bots (requires 2 different configuration files). -* use different ports (*applies only when webserver is enabled). +* use different ports (applies only when webserver is enabled). #### Different database files From 9cc04c929ff13595d0e29ab72c75673233eaa648 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 16 Jun 2020 07:17:15 +0200 Subject: [PATCH 50/54] Fix documentation typo --- docs/data-download.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/data-download.md b/docs/data-download.md index 903d62854..3fb775e69 100644 --- a/docs/data-download.md +++ b/docs/data-download.md @@ -109,7 +109,7 @@ The following command will convert all candle (OHLCV) data available in `~/.freq It'll also remove original json data files (`--erase` parameter). ``` bash -freqtrade convert-data --format-from json --format-to jsongz --data-dir ~/.freqtrade/data/binance -t 5m 15m --erase +freqtrade convert-data --format-from json --format-to jsongz --datadir ~/.freqtrade/data/binance -t 5m 15m --erase ``` #### Subcommand convert-trade data @@ -155,7 +155,7 @@ The following command will convert all available trade-data in `~/.freqtrade/dat It'll also remove original jsongz data files (`--erase` parameter). ``` bash -freqtrade convert-trade-data --format-from jsongz --format-to json --data-dir ~/.freqtrade/data/kraken --erase +freqtrade convert-trade-data --format-from jsongz --format-to json --datadir ~/.freqtrade/data/kraken --erase ``` ### Pairs file From 3517c86fa28786630bcb49c3c58bcddd41f0cf74 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 16 Jun 2020 16:02:38 +0200 Subject: [PATCH 51/54] Fail if both ticker_interval and timeframe are present in a configuration Otherwise the wrong might be used, as it's unclear which one the intend of the user is --- .../configuration/deprecated_settings.py | 5 +++++ tests/test_configuration.py | 20 ++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index cefc6ac14..03ed41ab8 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -72,4 +72,9 @@ def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None: "DEPRECATED: " "Please use 'timeframe' instead of 'ticker_interval." ) + if 'timeframe' in config: + raise OperationalException( + "Both 'timeframe' and 'ticker_interval' detected." + "Please remove 'ticker_interval' from your configuration to continue operating." + ) config['timeframe'] = config['ticker_interval'] diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 689e62ab9..cccc87670 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1144,11 +1144,21 @@ def test_process_deprecated_setting(mocker, default_conf, caplog): def test_process_deprecated_ticker_interval(mocker, default_conf, caplog): message = "DEPRECATED: Please use 'timeframe' instead of 'ticker_interval." - process_temporary_deprecated_settings(default_conf) + config = deepcopy(default_conf) + process_temporary_deprecated_settings(config) assert not log_has(message, caplog) - del default_conf['timeframe'] - default_conf['ticker_interval'] = '15m' - process_temporary_deprecated_settings(default_conf) + del config['timeframe'] + config['ticker_interval'] = '15m' + process_temporary_deprecated_settings(config) assert log_has(message, caplog) - assert default_conf['ticker_interval'] == '15m' + assert config['ticker_interval'] == '15m' + + config = deepcopy(default_conf) + # Have both timeframe and ticker interval in config + # Can also happen when using ticker_interval in configuration, and --timeframe as cli argument + config['timeframe'] = '5m' + config['ticker_interval'] = '4h' + with pytest.raises(OperationalException, + match=r"Both 'timeframe' and 'ticker_interval' detected."): + process_temporary_deprecated_settings(config) From d4fb5af456771083cf3facfd296fc59001d59fdb Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 17 Jun 2020 07:23:20 +0200 Subject: [PATCH 52/54] Also reload async markets fixes #2876 - Logs and Empty ticker history for new pair --- freqtrade/exchange/exchange.py | 2 ++ tests/exchange/test_exchange.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 35c62db27..b62410c34 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -285,6 +285,8 @@ class Exchange: logger.debug("Performing scheduled market reload..") try: self._api.load_markets(reload=True) + # Also reload async markets to avoid issues with newly listed pairs + self._load_async_markets(reload=True) self._last_markets_refresh = arrow.utcnow().timestamp except ccxt.BaseError: logger.exception("Could not reload markets.") diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 762ee295e..c9a600d50 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -365,6 +365,7 @@ def test_reload_markets(default_conf, mocker, caplog): default_conf['exchange']['markets_refresh_interval'] = 10 exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance", mock_markets=False) + exchange._load_async_markets = MagicMock() exchange._last_markets_refresh = arrow.utcnow().timestamp updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} @@ -373,11 +374,13 @@ def test_reload_markets(default_conf, mocker, caplog): # less than 10 minutes have passed, no reload exchange.reload_markets() assert exchange.markets == initial_markets + assert exchange._load_async_markets.call_count == 0 # more than 10 minutes have passed, reload is executed exchange._last_markets_refresh = arrow.utcnow().timestamp - 15 * 60 exchange.reload_markets() assert exchange.markets == updated_markets + assert exchange._load_async_markets.call_count == 1 assert log_has('Performing scheduled market reload..', caplog) From e2465f979bd6641f0413f58719622e471afef807 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 17 Jun 2020 08:33:53 +0200 Subject: [PATCH 53/54] Correctly mock out async_reload --- tests/conftest.py | 1 + tests/exchange/test_exchange.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 3ca431f40..a4106c767 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -56,6 +56,7 @@ def patched_configuration_load_config_file(mocker, config) -> None: def patch_exchange(mocker, api_mock=None, id='bittrex', mock_markets=True) -> None: + mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock(return_value={})) mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index c9a600d50..700aff969 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -319,7 +319,12 @@ def test_set_sandbox_exception(default_conf, mocker): def test__load_async_markets(default_conf, mocker, caplog): - exchange = get_patched_exchange(mocker, default_conf) + mocker.patch('freqtrade.exchange.Exchange._init_ccxt') + mocker.patch('freqtrade.exchange.Exchange.validate_pairs') + mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') + mocker.patch('freqtrade.exchange.Exchange._load_markets') + mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') + exchange = Exchange(default_conf) exchange._api_async.load_markets = get_mock_coro(None) exchange._load_async_markets() assert exchange._api_async.load_markets.call_count == 1 From 0b8cac68befa83b2e705da8d350442b7a06cfe8c Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 17 Jun 2020 22:49:01 +0300 Subject: [PATCH 54/54] Improve advanced-setup.md --- docs/advanced-setup.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index d3f692f33..f03bc10c0 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -6,20 +6,20 @@ If you do not know what things mentioned here mean, you probably do not need it. ## Running multiple instances of Freqtrade -This section will show you how to run multiple bots a the same time, on the same machine. +This section will show you how to run multiple bots at the same time, on the same machine. ### Things to consider -* use different database files. -* use different telegram Bots (requires 2 different configuration files). -* use different ports (applies only when webserver is enabled). +* Use different database files. +* Use different Telegram bots (requires multiple different configuration files; applies only when Telegram is enabled). +* Use different ports (applies only when Freqtrade REST API webserver is enabled). -#### Different database files +### Different database files -In order to keep track of your trades, profits, etc., freqtrade is using a SQLite database where it stores various types of information such as the trades you performed in the past and the current position(s) you are holding at any time. This allows you to keep track of your profits, but most importantly, keep track of ongoing activity if the bot process would be restarted or would be terminated unexpectently. +In order to keep track of your trades, profits, etc., freqtrade is using a SQLite database where it stores various types of information such as the trades you performed in the past and the current position(s) you are holding at any time. This allows you to keep track of your profits, but most importantly, keep track of ongoing activity if the bot process would be restarted or would be terminated unexpectedly. -Freqtrade will, by default, use seperate database files for dry-run and live bots (this assumes no database-url is given in either configuration nor via command line argument). -For live trading mode, the default database will be `tradesv3.sqlite`, and for dry-run, it will be `tradesv3.dryrun.sqlite`. +Freqtrade will, by default, use separate database files for dry-run and live bots (this assumes no database-url is given in either configuration nor via command line argument). +For live trading mode, the default database will be `tradesv3.sqlite` and for dry-run it will be `tradesv3.dryrun.sqlite`. The optional argument to the trade command used to specify the path of these files is `--db-url`, which requires a valid SQLAlchemy url. So when you are starting a bot with only the config and strategy arguments in dry-run mode, the following 2 commands would have the same outcome. @@ -30,9 +30,9 @@ freqtrade trade -c MyConfig.json -s MyStrategy freqtrade trade -c MyConfig.json -s MyStrategy --db-url sqlite:///tradesv3.dryrun.sqlite ``` -That means that if you are running the trade command in two different terminals, for example to test your strategy both for trades in USDT and in another instance for trades in BTC, you will have to run them with different databases. +It means that if you are running the trade command in two different terminals, for example to test your strategy both for trades in USDT and in another instance for trades in BTC, you will have to run them with different databases. -If you specify the URL of a database which does not exist, freqtrade will create one with the name you specified. So to test your custom strategy in BTC and USDT, you could use the following commands (in 2 seperate terminals): +If you specify the URL of a database which does not exist, freqtrade will create one with the name you specified. So to test your custom strategy with BTC and USDT stake currencies, you could use the following commands (in 2 separate terminals): ``` bash # Terminal 1: @@ -50,7 +50,7 @@ freqtrade trade -c MyConfigBTC.json -s MyCustomStrategy --db-url sqlite:///user_ freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite:///user_data/tradesUSDT.live.sqlite ``` -For more information regarding usage of the sqlite databases, for example to manually enter or remove trades, please refer to the [SQL Cheatsheet](sql_cheatsheet.md) +For more information regarding usage of the sqlite databases, for example to manually enter or remove trades, please refer to the [SQL Cheatsheet](sql_cheatsheet.md). ## Configure the bot running as a systemd service