import json
import re
from datetime import datetime
from io import BytesIO
from pathlib import Path
from unittest.mock import MagicMock, PropertyMock
from zipfile import ZipFile

import arrow
import pytest

from freqtrade.commands import (start_backtesting_show, start_convert_data, start_convert_trades,
                                start_create_userdir, start_download_data, start_hyperopt_list,
                                start_hyperopt_show, start_install_ui, start_list_data,
                                start_list_exchanges, start_list_markets, start_list_strategies,
                                start_list_timeframes, start_new_strategy, start_show_trades,
                                start_test_pairlist, start_trading, start_webserver)
from freqtrade.commands.db_commands import start_convert_db
from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui,
                                                get_ui_download_url, read_ui_version)
from freqtrade.commands.list_commands import start_list_freqAI_models
from freqtrade.configuration import setup_utils_configuration
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.persistence.models import init_db
from freqtrade.persistence.pairlock_middleware import PairLocks
from tests.conftest import (CURRENT_TEST_STRATEGY, EXMS, create_mock_trades, get_args, log_has,
                            log_has_re, patch_exchange, patched_configuration_load_config_file)
from tests.conftest_trades import MOCK_TRADE_COUNT


def test_setup_utils_configuration():
    args = [
        'list-exchanges', '--config', 'config_examples/config_bittrex.example.json',
    ]

    config = setup_utils_configuration(get_args(args), RunMode.OTHER)
    assert "exchange" in config
    assert config['dry_run'] is True


def test_start_trading_fail(mocker, caplog):

    mocker.patch("freqtrade.worker.Worker.run", MagicMock(side_effect=OperationalException))

    mocker.patch("freqtrade.worker.Worker.__init__", MagicMock(return_value=None))

    exitmock = mocker.patch("freqtrade.worker.Worker.exit", MagicMock())
    args = [
        'trade',
        '-c', 'config_examples/config_bittrex.example.json'
    ]
    start_trading(get_args(args))
    assert exitmock.call_count == 1

    exitmock.reset_mock()
    caplog.clear()
    mocker.patch("freqtrade.worker.Worker.__init__", MagicMock(side_effect=OperationalException))
    start_trading(get_args(args))
    assert exitmock.call_count == 0
    assert log_has('Fatal exception!', caplog)


def test_start_webserver(mocker, caplog):

    api_server_mock = mocker.patch("freqtrade.rpc.api_server.ApiServer", )

    args = [
        'webserver',
        '-c', 'config_examples/config_bittrex.example.json'
    ]
    start_webserver(get_args(args))
    assert api_server_mock.call_count == 1


def test_list_exchanges(capsys):

    args = [
        "list-exchanges",
    ]

    start_list_exchanges(get_args(args))
    captured = capsys.readouterr()
    assert re.match(r"Exchanges available for Freqtrade.*", captured.out)
    assert re.search(r".*binance.*", captured.out)
    assert re.search(r".*bittrex.*", captured.out)

    # Test with --one-column
    args = [
        "list-exchanges",
        "--one-column",
    ]

    start_list_exchanges(get_args(args))
    captured = capsys.readouterr()
    assert re.search(r"^binance$", captured.out, re.MULTILINE)
    assert re.search(r"^bittrex$", captured.out, re.MULTILINE)

    # Test with --all
    args = [
        "list-exchanges",
        "--all",
    ]

    start_list_exchanges(get_args(args))
    captured = capsys.readouterr()
    assert re.match(r"All exchanges supported by the ccxt library.*", captured.out)
    assert re.search(r".*binance.*", captured.out)
    assert re.search(r".*bittrex.*", captured.out)
    assert re.search(r".*bitmex.*", captured.out)

    # Test with --one-column --all
    args = [
        "list-exchanges",
        "--one-column",
        "--all",
    ]

    start_list_exchanges(get_args(args))
    captured = capsys.readouterr()
    assert re.search(r"^binance$", captured.out, re.MULTILINE)
    assert re.search(r"^bittrex$", captured.out, re.MULTILINE)
    assert re.search(r"^bitmex$", captured.out, re.MULTILINE)


def test_list_timeframes(mocker, capsys):

    api_mock = MagicMock()
    api_mock.timeframes = {'1m': 'oneMin',
                           '5m': 'fiveMin',
                           '30m': 'thirtyMin',
                           '1h': 'hour',
                           '1d': 'day',
                           }
    patch_exchange(mocker, api_mock=api_mock, id='bittrex')
    args = [
        "list-timeframes",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    with pytest.raises(OperationalException,
                       match=r"This command requires a configured exchange.*"):
        start_list_timeframes(pargs)

    # Test with --config config_examples/config_bittrex.example.json
    args = [
        "list-timeframes",
        '--config', 'config_examples/config_bittrex.example.json',
    ]
    start_list_timeframes(get_args(args))
    captured = capsys.readouterr()
    assert re.match("Timeframes available for the exchange `Bittrex`: "
                    "1m, 5m, 30m, 1h, 1d",
                    captured.out)

    # Test with --exchange bittrex
    args = [
        "list-timeframes",
        "--exchange", "bittrex",
    ]
    start_list_timeframes(get_args(args))
    captured = capsys.readouterr()
    assert re.match("Timeframes available for the exchange `Bittrex`: "
                    "1m, 5m, 30m, 1h, 1d",
                    captured.out)

    api_mock.timeframes = {'1m': '1m',
                           '5m': '5m',
                           '15m': '15m',
                           '30m': '30m',
                           '1h': '1h',
                           '6h': '6h',
                           '12h': '12h',
                           '1d': '1d',
                           '3d': '3d',
                           }
    patch_exchange(mocker, api_mock=api_mock, id='binance')
    # Test with --exchange binance
    args = [
        "list-timeframes",
        "--exchange", "binance",
    ]
    start_list_timeframes(get_args(args))
    captured = capsys.readouterr()
    assert re.match("Timeframes available for the exchange `Binance`: "
                    "1m, 5m, 15m, 30m, 1h, 6h, 12h, 1d, 3d",
                    captured.out)

    # Test with --one-column
    args = [
        "list-timeframes",
        '--config', 'config_examples/config_bittrex.example.json',
        "--one-column",
    ]
    start_list_timeframes(get_args(args))
    captured = capsys.readouterr()
    assert re.search(r"^1m$", captured.out, re.MULTILINE)
    assert re.search(r"^5m$", captured.out, re.MULTILINE)
    assert re.search(r"^1h$", captured.out, re.MULTILINE)
    assert re.search(r"^1d$", captured.out, re.MULTILINE)

    # Test with --exchange binance --one-column
    args = [
        "list-timeframes",
        "--exchange", "binance",
        "--one-column",
    ]
    start_list_timeframes(get_args(args))
    captured = capsys.readouterr()
    assert re.search(r"^1m$", captured.out, re.MULTILINE)
    assert re.search(r"^5m$", captured.out, re.MULTILINE)
    assert re.search(r"^1h$", captured.out, re.MULTILINE)
    assert re.search(r"^1d$", captured.out, re.MULTILINE)


def test_list_markets(mocker, markets_static, capsys):

    api_mock = MagicMock()
    patch_exchange(mocker, api_mock=api_mock, id='bittrex', mock_markets=markets_static)

    # Test with no --config
    args = [
        "list-markets",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    with pytest.raises(OperationalException,
                       match=r"This command requires a configured exchange.*"):
        start_list_markets(pargs, False)

    # Test with --config config_examples/config_bittrex.example.json
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 12 active markets: "
            "ADA/USDT:USDT, BLK/BTC, ETH/BTC, ETH/USDT, ETH/USDT:USDT, LTC/BTC, "
            "LTC/ETH, LTC/USD, NEO/BTC, TKN/BTC, XLTCUSDT, XRP/BTC.\n"
            in captured.out)

    patch_exchange(mocker, api_mock=api_mock, id="binance", mock_markets=markets_static)
    # Test with --exchange
    args = [
        "list-markets",
        "--exchange", "binance"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_list_markets(pargs, False)
    captured = capsys.readouterr()
    assert re.match("\nExchange Binance has 12 active markets:\n",
                    captured.out)

    patch_exchange(mocker, api_mock=api_mock, id="bittrex", mock_markets=markets_static)
    # Test with --all: all markets
    args = [
        "list-markets", "--all",
        '--config', 'config_examples/config_bittrex.example.json',
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 14 markets: "
            "ADA/USDT:USDT, BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, ETH/USDT:USDT, "
            "LTC/BTC, LTC/ETH, LTC/USD, LTC/USDT, NEO/BTC, TKN/BTC, XLTCUSDT, XRP/BTC.\n"
            in captured.out)

    # Test list-pairs subcommand: active pairs
    args = [
        "list-pairs",
        '--config', 'config_examples/config_bittrex.example.json',
        "--print-list",
    ]
    start_list_markets(get_args(args), True)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 9 active pairs: "
            "BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, NEO/BTC, TKN/BTC, XRP/BTC.\n"
            in captured.out)

    # Test list-pairs subcommand with --all: all pairs
    args = [
        "list-pairs", "--all",
        '--config', 'config_examples/config_bittrex.example.json',
        "--print-list",
    ]
    start_list_markets(get_args(args), True)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 11 pairs: "
            "BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, LTC/USDT, NEO/BTC, "
            "TKN/BTC, XRP/BTC.\n"
            in captured.out)

    # active markets, base=ETH, LTC
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--base", "ETH", "LTC",
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 7 active markets with ETH, LTC as base currencies: "
            "ETH/BTC, ETH/USDT, ETH/USDT:USDT, LTC/BTC, LTC/ETH, LTC/USD, XLTCUSDT.\n"
            in captured.out)

    # active markets, base=LTC
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--base", "LTC",
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 4 active markets with LTC as base currency: "
            "LTC/BTC, LTC/ETH, LTC/USD, XLTCUSDT.\n"
            in captured.out)

    # active markets, quote=USDT, USD
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--quote", "USDT", "USD",
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 5 active markets with USDT, USD as quote currencies: "
            "ADA/USDT:USDT, ETH/USDT, ETH/USDT:USDT, LTC/USD, XLTCUSDT.\n"
            in captured.out)

    # active markets, quote=USDT
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--quote", "USDT",
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 4 active markets with USDT as quote currency: "
            "ADA/USDT:USDT, ETH/USDT, ETH/USDT:USDT, XLTCUSDT.\n"
            in captured.out)

    # active markets, base=LTC, quote=USDT
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--base", "LTC", "--quote", "USDT",
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 1 active market with LTC as base currency and "
            "with USDT as quote currency: XLTCUSDT.\n"
            in captured.out)

    # active pairs, base=LTC, quote=USDT
    args = [
        "list-pairs",
        '--config', 'config_examples/config_bittrex.example.json',
        "--base", "LTC", "--quote", "USD",
        "--print-list",
    ]
    start_list_markets(get_args(args), True)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 1 active pair with LTC as base currency and "
            "with USD as quote currency: LTC/USD.\n"
            in captured.out)

    # active markets, base=LTC, quote=USDT, NONEXISTENT
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--base", "LTC", "--quote", "USDT", "NONEXISTENT",
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 1 active market with LTC as base currency and "
            "with USDT, NONEXISTENT as quote currencies: XLTCUSDT.\n"
            in captured.out)

    # active markets, base=LTC, quote=NONEXISTENT
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--base", "LTC", "--quote", "NONEXISTENT",
        "--print-list",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 0 active markets with LTC as base currency and "
            "with NONEXISTENT as quote currency.\n"
            in captured.out)

    # Test tabular output
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 12 active markets:\n"
            in captured.out)

    # Test tabular output, no markets found
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--base", "LTC", "--quote", "NONEXISTENT",
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Exchange Bittrex has 0 active markets with LTC as base currency and "
            "with NONEXISTENT as quote currency.\n"
            in captured.out)

    # Test --print-json
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--print-json"
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ('["ADA/USDT:USDT","BLK/BTC","ETH/BTC","ETH/USDT","ETH/USDT:USDT",'
            '"LTC/BTC","LTC/ETH","LTC/USD","NEO/BTC","TKN/BTC","XLTCUSDT","XRP/BTC"]'
            in captured.out)

    # Test --print-csv
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--print-csv"
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert ("Id,Symbol,Base,Quote,Active,Spot,Margin,Future,Leverage" in captured.out)
    assert ("blkbtc,BLK/BTC,BLK,BTC,True,Spot" in captured.out)
    assert ("USD-LTC,LTC/USD,LTC,USD,True,Spot" in captured.out)

    # Test --one-column
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--one-column"
    ]
    start_list_markets(get_args(args), False)
    captured = capsys.readouterr()
    assert re.search(r"^BLK/BTC$", captured.out, re.MULTILINE)
    assert re.search(r"^LTC/USD$", captured.out, re.MULTILINE)

    mocker.patch(f'{EXMS}.markets', PropertyMock(side_effect=ValueError))
    # Test --one-column
    args = [
        "list-markets",
        '--config', 'config_examples/config_bittrex.example.json',
        "--one-column"
    ]
    with pytest.raises(OperationalException, match=r"Cannot get markets.*"):
        start_list_markets(get_args(args), False)


def test_create_datadir_failed(caplog):

    args = [
        "create-userdir",
    ]
    with pytest.raises(SystemExit):
        start_create_userdir(get_args(args))
    assert log_has("`create-userdir` requires --userdir to be set.", caplog)


def test_create_datadir(caplog, mocker):

    cud = mocker.patch("freqtrade.commands.deploy_commands.create_userdata_dir", MagicMock())
    csf = mocker.patch("freqtrade.commands.deploy_commands.copy_sample_files", MagicMock())
    args = [
        "create-userdir",
        "--userdir",
        "/temp/freqtrade/test"
    ]
    start_create_userdir(get_args(args))

    assert cud.call_count == 1
    assert csf.call_count == 1


def test_start_new_strategy(mocker, caplog):
    wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
    mocker.patch.object(Path, "exists", MagicMock(return_value=False))

    args = [
        "new-strategy",
        "--strategy",
        "CoolNewStrategy"
    ]
    start_new_strategy(get_args(args))

    assert wt_mock.call_count == 1
    assert "CoolNewStrategy" in wt_mock.call_args_list[0][0][0]
    assert log_has_re("Writing strategy to .*", caplog)

    mocker.patch('freqtrade.commands.deploy_commands.setup_utils_configuration')
    mocker.patch.object(Path, "exists", MagicMock(return_value=True))
    with pytest.raises(OperationalException,
                       match=r".* already exists. Please choose another Strategy Name\."):
        start_new_strategy(get_args(args))


def test_start_new_strategy_no_arg(mocker, caplog):
    args = [
        "new-strategy",
    ]
    with pytest.raises(OperationalException,
                       match="`new-strategy` requires --strategy to be set."):
        start_new_strategy(get_args(args))


def test_start_install_ui(mocker):
    clean_mock = mocker.patch('freqtrade.commands.deploy_commands.clean_ui_subdir')
    get_url_mock = mocker.patch('freqtrade.commands.deploy_commands.get_ui_download_url',
                                return_value=('https://example.com/whatever', '0.0.1'))
    download_mock = mocker.patch('freqtrade.commands.deploy_commands.download_and_install_ui')
    mocker.patch('freqtrade.commands.deploy_commands.read_ui_version', return_value=None)
    args = [
        "install-ui",
    ]
    start_install_ui(get_args(args))
    assert clean_mock.call_count == 1
    assert get_url_mock.call_count == 1
    assert download_mock.call_count == 1

    clean_mock.reset_mock()
    get_url_mock.reset_mock()
    download_mock.reset_mock()

    args = [
        "install-ui",
        "--erase",
    ]
    start_install_ui(get_args(args))
    assert clean_mock.call_count == 1
    assert get_url_mock.call_count == 1
    assert download_mock.call_count == 0


def test_clean_ui_subdir(mocker, tmpdir, caplog):
    mocker.patch("freqtrade.commands.deploy_commands.Path.is_dir",
                 side_effect=[True, True])
    mocker.patch("freqtrade.commands.deploy_commands.Path.is_file",
                 side_effect=[False, True])
    rd_mock = mocker.patch("freqtrade.commands.deploy_commands.Path.rmdir")
    ul_mock = mocker.patch("freqtrade.commands.deploy_commands.Path.unlink")

    mocker.patch("freqtrade.commands.deploy_commands.Path.glob",
                 return_value=[Path('test1'), Path('test2'), Path('.gitkeep')])
    folder = Path(tmpdir) / "uitests"
    clean_ui_subdir(folder)
    assert log_has("Removing UI directory content.", caplog)
    assert rd_mock.call_count == 1
    assert ul_mock.call_count == 1


def test_download_and_install_ui(mocker, tmpdir):
    # Create zipfile
    requests_mock = MagicMock()
    file_like_object = BytesIO()
    with ZipFile(file_like_object, mode='w') as zipfile:
        for file in ('test1.txt', 'hello/', 'test2.txt'):
            zipfile.writestr(file, file)
    file_like_object.seek(0)
    requests_mock.content = file_like_object.read()

    mocker.patch("freqtrade.commands.deploy_commands.requests.get", return_value=requests_mock)

    mocker.patch("freqtrade.commands.deploy_commands.Path.is_dir",
                 side_effect=[True, False])
    wb_mock = mocker.patch("freqtrade.commands.deploy_commands.Path.write_bytes")

    folder = Path(tmpdir) / "uitests_dl"
    folder.mkdir(exist_ok=True)

    assert read_ui_version(folder) is None

    download_and_install_ui(folder, 'http://whatever.xxx/download/file.zip', '22')

    assert wb_mock.call_count == 2

    assert read_ui_version(folder) == '22'


def test_get_ui_download_url(mocker):
    response = MagicMock()
    response.json = MagicMock(
        side_effect=[[{'assets_url': 'http://whatever.json', 'name': '0.0.1'}],
                     [{'browser_download_url': 'http://download.zip'}]])
    get_mock = mocker.patch("freqtrade.commands.deploy_commands.requests.get",
                            return_value=response)
    x, last_version = get_ui_download_url()
    assert get_mock.call_count == 2
    assert last_version == '0.0.1'
    assert x == 'http://download.zip'


def test_get_ui_download_url_direct(mocker):
    response = MagicMock()
    response.json = MagicMock(
        return_value=[
            {
                'assets_url': 'http://whatever.json',
                'name': '0.0.2',
                'assets': [{'browser_download_url': 'http://download22.zip'}]
            },
            {
                'assets_url': 'http://whatever.json',
                'name': '0.0.1',
                'assets': [{'browser_download_url': 'http://download1.zip'}]
            },
        ])
    get_mock = mocker.patch("freqtrade.commands.deploy_commands.requests.get",
                            return_value=response)
    x, last_version = get_ui_download_url()
    assert get_mock.call_count == 1
    assert last_version == '0.0.2'
    assert x == 'http://download22.zip'
    get_mock.reset_mock()
    response.json.reset_mock()

    x, last_version = get_ui_download_url('0.0.1')
    assert last_version == '0.0.1'
    assert x == 'http://download1.zip'

    with pytest.raises(ValueError, match="UI-Version not found."):
        x, last_version = get_ui_download_url('0.0.3')


def test_download_data_keyboardInterrupt(mocker, markets):
    dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
                           MagicMock(side_effect=KeyboardInterrupt))
    patch_exchange(mocker)
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets))
    args = [
        "download-data",
        "--exchange", "binance",
        "--pairs", "ETH/BTC", "XRP/BTC",
    ]
    with pytest.raises(SystemExit):
        pargs = get_args(args)
        pargs['config'] = None

        start_download_data(pargs)

    assert dl_mock.call_count == 1


def test_download_data_timerange(mocker, markets):
    dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
                           MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
    patch_exchange(mocker)
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets))
    args = [
        "download-data",
        "--exchange", "binance",
        "--pairs", "ETH/BTC", "XRP/BTC",
        "--days", "20",
        "--timerange", "20200101-"
    ]
    with pytest.raises(OperationalException,
                       match=r"--days and --timerange are mutually.*"):
        pargs = get_args(args)
        pargs['config'] = None
        start_download_data(pargs)
    assert dl_mock.call_count == 0

    args = [
        "download-data",
        "--exchange", "binance",
        "--pairs", "ETH/BTC", "XRP/BTC",
        "--days", "20",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_download_data(pargs)
    assert dl_mock.call_count == 1
    # 20days ago
    days_ago = arrow.get(arrow.now().shift(days=-20).date()).int_timestamp
    assert dl_mock.call_args_list[0][1]['timerange'].startts == days_ago

    dl_mock.reset_mock()
    args = [
        "download-data",
        "--exchange", "binance",
        "--pairs", "ETH/BTC", "XRP/BTC",
        "--timerange", "20200101-"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_download_data(pargs)
    assert dl_mock.call_count == 1

    assert dl_mock.call_args_list[0][1]['timerange'].startts == arrow.Arrow(
        2020, 1, 1).int_timestamp


def test_download_data_no_markets(mocker, caplog):
    dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
                           MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
    patch_exchange(mocker, id='binance')
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
    args = [
        "download-data",
        "--exchange", "binance",
        "--pairs", "ETH/BTC", "XRP/BTC",
        "--days", "20"
    ]
    start_download_data(get_args(args))
    assert dl_mock.call_args[1]['timerange'].starttype == "date"
    assert log_has("Pairs [ETH/BTC,XRP/BTC] not available on exchange Binance.", caplog)


def test_download_data_no_exchange(mocker, caplog):
    mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
                 MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
    patch_exchange(mocker)
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
    args = [
        "download-data",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    with pytest.raises(OperationalException,
                       match=r"This command requires a configured exchange.*"):
        start_download_data(pargs)


def test_download_data_no_pairs(mocker):

    mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
                 MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
    patch_exchange(mocker)
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
    args = [
        "download-data",
        "--exchange",
        "binance",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    with pytest.raises(OperationalException,
                       match=r"Downloading data requires a list of pairs\..*"):
        start_download_data(pargs)


def test_download_data_all_pairs(mocker, markets):

    dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
                           MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
    patch_exchange(mocker)
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets))
    args = [
        "download-data",
        "--exchange",
        "binance",
        "--pairs",
        ".*/USDT"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_download_data(pargs)
    expected = set(['ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
    assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
    assert dl_mock.call_count == 1

    dl_mock.reset_mock()
    args = [
        "download-data",
        "--exchange",
        "binance",
        "--pairs",
        ".*/USDT",
        "--include-inactive-pairs",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_download_data(pargs)
    expected = set(['ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
    assert set(dl_mock.call_args_list[0][1]['pairs']) == expected


def test_download_data_trades(mocker, caplog):
    dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_trades_data',
                           MagicMock(return_value=[]))
    convert_mock = mocker.patch('freqtrade.commands.data_commands.convert_trades_to_ohlcv',
                                MagicMock(return_value=[]))
    patch_exchange(mocker)
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
    args = [
        "download-data",
        "--exchange", "kraken",
        "--pairs", "ETH/BTC", "XRP/BTC",
        "--days", "20",
        "--dl-trades"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_download_data(pargs)
    assert dl_mock.call_args[1]['timerange'].starttype == "date"
    assert dl_mock.call_count == 1
    assert convert_mock.call_count == 1
    args = [
        "download-data",
        "--exchange", "kraken",
        "--pairs", "ETH/BTC", "XRP/BTC",
        "--days", "20",
        "--trading-mode", "futures",
        "--dl-trades"
    ]
    with pytest.raises(OperationalException,
                       match="Trade download not supported for futures."):
        pargs = get_args(args)
        pargs['config'] = None
        start_download_data(pargs)


def test_download_data_data_invalid(mocker):
    patch_exchange(mocker, id="kraken")
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
    args = [
        "download-data",
        "--exchange", "kraken",
        "--pairs", "ETH/BTC", "XRP/BTC",
        "--days", "20",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    with pytest.raises(OperationalException, match=r"Historic klines not available for .*"):
        start_download_data(pargs)


def test_start_convert_trades(mocker, caplog):
    convert_mock = mocker.patch('freqtrade.commands.data_commands.convert_trades_to_ohlcv',
                                MagicMock(return_value=[]))
    patch_exchange(mocker)
    mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
    args = [
        "trades-to-ohlcv",
        "--exchange", "kraken",
        "--pairs", "ETH/BTC", "XRP/BTC",
    ]
    start_convert_trades(get_args(args))
    assert convert_mock.call_count == 1


def test_start_list_strategies(capsys):

    args = [
        "list-strategies",
        "--strategy-path",
        str(Path(__file__).parent.parent / "strategy" / "strats"),
        "-1"
    ]
    pargs = get_args(args)
    # pargs['config'] = None
    start_list_strategies(pargs)
    captured = capsys.readouterr()
    assert "StrategyTestV2" in captured.out
    assert "strategy_test_v2.py" not in captured.out
    assert CURRENT_TEST_STRATEGY in captured.out

    # Test regular output
    args = [
        "list-strategies",
        "--strategy-path",
        str(Path(__file__).parent.parent / "strategy" / "strats"),
        '--no-color',
    ]
    pargs = get_args(args)
    # pargs['config'] = None
    start_list_strategies(pargs)
    captured = capsys.readouterr()
    assert "StrategyTestV2" in captured.out
    assert "strategy_test_v2.py" in captured.out
    assert CURRENT_TEST_STRATEGY in captured.out

    # Test color output
    args = [
        "list-strategies",
        "--strategy-path",
        str(Path(__file__).parent.parent / "strategy" / "strats"),
    ]
    pargs = get_args(args)
    # pargs['config'] = None
    start_list_strategies(pargs)
    captured = capsys.readouterr()
    assert "StrategyTestV2" in captured.out
    assert "strategy_test_v2.py" in captured.out
    assert CURRENT_TEST_STRATEGY in captured.out
    assert "LOAD FAILED" in captured.out
    # Recursive
    assert "TestStrategyNoImplements" not in captured.out

    # Test recursive
    args = [
        "list-strategies",
        "--strategy-path",
        str(Path(__file__).parent.parent / "strategy" / "strats"),
        '--no-color',
        '--recursive-strategy-search'
    ]
    pargs = get_args(args)
    # pargs['config'] = None
    start_list_strategies(pargs)
    captured = capsys.readouterr()
    assert "StrategyTestV2" in captured.out
    assert "strategy_test_v2.py" in captured.out
    assert "StrategyTestV2" in captured.out
    assert "TestStrategyNoImplements" in captured.out
    assert str(Path("broken_strats/broken_futures_strategies.py")) in captured.out


def test_start_list_freqAI_models(capsys):

    args = [
        "list-freqaimodels",
        "-1"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_list_freqAI_models(pargs)
    captured = capsys.readouterr()
    assert "LightGBMClassifier" in captured.out
    assert "LightGBMRegressor" in captured.out
    assert "XGBoostRegressor" in captured.out
    assert "<builtin>/LightGBMRegressor.py" not in captured.out

    args = [
        "list-freqaimodels",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_list_freqAI_models(pargs)
    captured = capsys.readouterr()
    assert "LightGBMClassifier" in captured.out
    assert "LightGBMRegressor" in captured.out
    assert "XGBoostRegressor" in captured.out
    assert "<builtin>/LightGBMRegressor.py" in captured.out


def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
    patch_exchange(mocker, mock_markets=True)
    mocker.patch.multiple(EXMS,
                          exchange_has=MagicMock(return_value=True),
                          get_tickers=tickers,
                          )

    default_conf['pairlists'] = [
        {
            "method": "VolumePairList",
            "number_assets": 5,
            "sort_key": "quoteVolume",
        },
        {"method": "PrecisionFilter"},
        {"method": "PriceFilter", "low_price_ratio": 0.02},
    ]

    patched_configuration_load_config_file(mocker, default_conf)
    args = [
        'test-pairlist',
        '-c', 'config_examples/config_bittrex.example.json'
    ]

    start_test_pairlist(get_args(args))

    assert log_has_re(r"^Using resolved pairlist VolumePairList.*", caplog)
    assert log_has_re(r"^Using resolved pairlist PrecisionFilter.*", caplog)
    assert log_has_re(r"^Using resolved pairlist PriceFilter.*", caplog)
    captured = capsys.readouterr()
    assert re.match(r"Pairs for .*", captured.out)
    assert re.match("['ETH/BTC', 'TKN/BTC', 'BLK/BTC', 'LTC/BTC', 'XRP/BTC']", captured.out)

    args = [
        'test-pairlist',
        '-c', 'config_examples/config_bittrex.example.json',
        '--one-column',
    ]
    start_test_pairlist(get_args(args))
    captured = capsys.readouterr()
    assert re.match(r"ETH/BTC\nTKN/BTC\nBLK/BTC\nLTC/BTC\nXRP/BTC\n", captured.out)

    args = [
        'test-pairlist',
        '-c', 'config_examples/config_bittrex.example.json',
        '--print-json',
    ]
    start_test_pairlist(get_args(args))
    captured = capsys.readouterr()
    try:
        json_pairs = json.loads(captured.out)
        assert 'ETH/BTC' in json_pairs
        assert 'TKN/BTC' in json_pairs
        assert 'BLK/BTC' in json_pairs
        assert 'LTC/BTC' in json_pairs
        assert 'XRP/BTC' in json_pairs
    except json.decoder.JSONDecodeError:
        pytest.fail(f'Expected well formed JSON, but failed to parse: {captured.out}')


def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results, tmpdir):
    csv_file = Path(tmpdir) / "test.csv"
    mocker.patch(
        'freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist',
        return_value=True
    )

    def fake_iterator(*args, **kwargs):
        yield from [saved_hyperopt_results]

    mocker.patch(
        'freqtrade.optimize.hyperopt_tools.HyperoptTools._read_results',
        side_effect=fake_iterator
    )

    args = [
        "hyperopt-list",
        "--no-details",
        "--no-color",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12",
                         " 6/12", " 7/12", " 8/12", " 9/12", " 10/12",
                         " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--best",
        "--no-details",
        "--no-color",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 1/12", " 5/12", " 10/12"])
    assert all(x not in captured.out
               for x in [" 2/12", " 3/12", " 4/12", " 6/12", " 7/12", " 8/12", " 9/12",
                         " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--profitable",
        "--no-details",
        "--no-color",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 2/12", " 10/12"])
    assert all(x not in captured.out
               for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
                         " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--profitable",
        "--no-color",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 2/12", " 10/12", "Best result:", "Buy hyperspace params",
                         "Sell hyperspace params", "ROI table", "Stoploss"])
    assert all(x not in captured.out
               for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
                         " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--no-details",
        "--no-color",
        "--min-trades", "20",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 3/12", " 6/12", " 7/12", " 9/12", " 11/12"])
    assert all(x not in captured.out
               for x in [" 1/12", " 2/12", " 4/12", " 5/12", " 8/12", " 10/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--profitable",
        "--no-details",
        "--no-color",
        "--max-trades", "20",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 2/12", " 10/12"])
    assert all(x not in captured.out
               for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
                         " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--profitable",
        "--no-details",
        "--no-color",
        "--min-avg-profit", "0.11",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 2/12"])
    assert all(x not in captured.out
               for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
                         " 10/12", " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--no-details",
        "--no-color",
        "--max-avg-profit", "0.10",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 1/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
                         " 11/12"])
    assert all(x not in captured.out
               for x in [" 2/12", " 4/12", " 10/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--no-details",
        "--no-color",
        "--min-total-profit", "0.4",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 10/12"])
    assert all(x not in captured.out
               for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12",
                         " 9/12", " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--no-details",
        "--no-color",
        "--max-total-profit", "0.4",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12",
                         " 9/12", " 11/12"])
    assert all(x not in captured.out
               for x in [" 4/12", " 10/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--no-details",
        "--no-color",
        "--min-objective", "0.1",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 10/12"])
    assert all(x not in captured.out
               for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12",
                         " 9/12", " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--no-details",
        "--max-objective", "0.1",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12",
                         " 9/12", " 11/12"])
    assert all(x not in captured.out
               for x in [" 4/12", " 10/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--profitable",
        "--no-details",
        "--no-color",
        "--min-avg-time", "2000",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 10/12"])
    assert all(x not in captured.out
               for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12",
                         " 8/12", " 9/12", " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--no-details",
        "--no-color",
        "--max-avg-time", "1500",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    assert all(x in captured.out
               for x in [" 2/12", " 6/12"])
    assert all(x not in captured.out
               for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12"
                         " 9/12", " 10/12", " 11/12", " 12/12"])
    args = [
        "hyperopt-list",
        "--no-details",
        "--no-color",
        "--export-csv",
        str(csv_file),
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_list(pargs)
    captured = capsys.readouterr()
    log_has("CSV file created: test_file.csv", caplog)
    assert csv_file.is_file()
    line = csv_file.read_text()
    assert ('Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,"3,930.0 m",0.43662' in line
            or "Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,2 days 17:30:00,2,0,0.43662" in line)
    csv_file.unlink()


def test_hyperopt_show(mocker, capsys, saved_hyperopt_results):
    mocker.patch(
        'freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist',
        return_value=True
    )

    def fake_iterator(*args, **kwargs):
        yield from [saved_hyperopt_results]

    mocker.patch(
        'freqtrade.optimize.hyperopt_tools.HyperoptTools._read_results',
        side_effect=fake_iterator
    )
    mocker.patch('freqtrade.commands.hyperopt_commands.show_backtest_result')

    args = [
        "hyperopt-show",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_show(pargs)
    captured = capsys.readouterr()
    assert " 12/12" in captured.out

    args = [
        "hyperopt-show",
        "--best"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_show(pargs)
    captured = capsys.readouterr()
    assert " 10/12" in captured.out

    args = [
        "hyperopt-show",
        "-n", "1"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_show(pargs)
    captured = capsys.readouterr()
    assert " 1/12" in captured.out

    args = [
        "hyperopt-show",
        "--best",
        "-n", "2"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_show(pargs)
    captured = capsys.readouterr()
    assert " 5/12" in captured.out

    args = [
        "hyperopt-show",
        "--best",
        "-n", "-1"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_hyperopt_show(pargs)
    captured = capsys.readouterr()
    assert " 10/12" in captured.out

    args = [
        "hyperopt-show",
        "--best",
        "-n", "-4"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    with pytest.raises(OperationalException,
                       match="The index of the epoch to show should be greater than -4."):
        start_hyperopt_show(pargs)

    args = [
        "hyperopt-show",
        "--best",
        "-n", "4"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    with pytest.raises(OperationalException,
                       match="The index of the epoch to show should be less than 4."):
        start_hyperopt_show(pargs)


def test_convert_data(mocker, testdatadir):
    ohlcv_mock = mocker.patch("freqtrade.commands.data_commands.convert_ohlcv_format")
    trades_mock = mocker.patch("freqtrade.commands.data_commands.convert_trades_format")
    args = [
        "convert-data",
        "--format-from",
        "json",
        "--format-to",
        "jsongz",
        "--datadir",
        str(testdatadir),
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_convert_data(pargs, True)
    assert trades_mock.call_count == 0
    assert ohlcv_mock.call_count == 1
    assert ohlcv_mock.call_args[1]['convert_from'] == 'json'
    assert ohlcv_mock.call_args[1]['convert_to'] == 'jsongz'
    assert ohlcv_mock.call_args[1]['erase'] is False


def test_convert_data_trades(mocker, testdatadir):
    ohlcv_mock = mocker.patch("freqtrade.commands.data_commands.convert_ohlcv_format")
    trades_mock = mocker.patch("freqtrade.commands.data_commands.convert_trades_format")
    args = [
        "convert-trade-data",
        "--format-from",
        "jsongz",
        "--format-to",
        "json",
        "--datadir",
        str(testdatadir),
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_convert_data(pargs, False)
    assert ohlcv_mock.call_count == 0
    assert trades_mock.call_count == 1
    assert trades_mock.call_args[1]['convert_from'] == 'jsongz'
    assert trades_mock.call_args[1]['convert_to'] == 'json'
    assert trades_mock.call_args[1]['erase'] is False


def test_start_list_data(testdatadir, capsys):
    args = [
        "list-data",
        "--data-format-ohlcv",
        "json",
        "--datadir",
        str(testdatadir),
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_list_data(pargs)
    captured = capsys.readouterr()
    assert "Found 17 pair / timeframe combinations." in captured.out
    assert "\n|         Pair |       Timeframe |   Type |\n" in captured.out
    assert "\n| UNITTEST/BTC | 1m, 5m, 8m, 30m |   spot |\n" in captured.out

    args = [
        "list-data",
        "--data-format-ohlcv",
        "json",
        "--pairs", "XRP/ETH",
        "--datadir",
        str(testdatadir),
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_list_data(pargs)
    captured = capsys.readouterr()
    assert "Found 2 pair / timeframe combinations." in captured.out
    assert "\n|    Pair |   Timeframe |   Type |\n" in captured.out
    assert "UNITTEST/BTC" not in captured.out
    assert "\n| XRP/ETH |      1m, 5m |   spot |\n" in captured.out

    args = [
        "list-data",
        "--data-format-ohlcv",
        "json",
        "--trading-mode", "futures",
        "--datadir",
        str(testdatadir),
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_list_data(pargs)
    captured = capsys.readouterr()

    assert "Found 6 pair / timeframe combinations." in captured.out
    assert "\n|               Pair |   Timeframe |         Type |\n" in captured.out
    assert "\n|      XRP/USDT:USDT |      5m, 1h |      futures |\n" in captured.out
    assert "\n|      XRP/USDT:USDT |      1h, 8h |         mark |\n" in captured.out

    args = [
        "list-data",
        "--data-format-ohlcv",
        "json",
        "--pairs", "XRP/ETH",
        "--datadir",
        str(testdatadir),
        "--show-timerange",
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_list_data(pargs)
    captured = capsys.readouterr()
    assert "Found 2 pair / timeframe combinations." in captured.out
    assert ("\n|    Pair |   Timeframe |   Type |                From |                  To |\n"
            in captured.out)
    assert "UNITTEST/BTC" not in captured.out
    assert (
            "\n| XRP/ETH |          1m |   spot | 2019-10-11 00:00:00 | 2019-10-13 11:19:00 |\n"
            in captured.out)


@pytest.mark.usefixtures("init_persistence")
def test_show_trades(mocker, fee, capsys, caplog):
    mocker.patch("freqtrade.persistence.init_db")
    create_mock_trades(fee, False)
    args = [
        "show-trades",
        "--db-url",
        "sqlite:///"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_show_trades(pargs)
    assert log_has(f"Printing {MOCK_TRADE_COUNT} Trades: ", caplog)
    captured = capsys.readouterr()
    assert "Trade(id=1" in captured.out
    assert "Trade(id=2" in captured.out
    assert "Trade(id=3" in captured.out
    args = [
        "show-trades",
        "--db-url",
        "sqlite:///",
        "--print-json",
        "--trade-ids", "1", "2"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_show_trades(pargs)

    captured = capsys.readouterr()
    assert log_has("Printing 2 Trades: ", caplog)
    assert '"trade_id": 1' in captured.out
    assert '"trade_id": 2' in captured.out
    assert '"trade_id": 3' not in captured.out
    args = [
        "show-trades",
    ]
    pargs = get_args(args)
    pargs['config'] = None

    with pytest.raises(OperationalException, match=r"--db-url is required for this command."):
        start_show_trades(pargs)


def test_backtesting_show(mocker, testdatadir, capsys):
    sbr = mocker.patch('freqtrade.optimize.optimize_reports.show_backtest_results')
    args = [
        "backtesting-show",
        "--export-filename",
        f"{testdatadir / 'backtest_results/backtest-result.json'}",
        "--show-pair-list"
    ]
    pargs = get_args(args)
    pargs['config'] = None
    start_backtesting_show(pargs)
    assert sbr.call_count == 1
    out, err = capsys.readouterr()
    assert "Pairs for Strategy" in out


def test_start_convert_db(mocker, fee, tmpdir, caplog):
    db_src_file = Path(f"{tmpdir}/db.sqlite")
    db_from = f"sqlite:///{db_src_file}"
    db_target_file = Path(f"{tmpdir}/db_target.sqlite")
    db_to = f"sqlite:///{db_target_file}"
    args = [
        "convert-db",
        "--db-url-from",
        db_from,
        "--db-url",
        db_to,
    ]

    assert not db_src_file.is_file()
    init_db(db_from)

    create_mock_trades(fee)

    PairLocks.timeframe = '5m'
    PairLocks.lock_pair('XRP/USDT', datetime.now(), 'Random reason 125', side='long')
    assert db_src_file.is_file()
    assert not db_target_file.is_file()

    pargs = get_args(args)
    pargs['config'] = None
    start_convert_db(pargs)

    assert db_target_file.is_file()