Esempio n. 1
0
def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog,
                                      default_conf, testdatadir) -> None:
    """
    Test load_pair_history() with 1 min ticker
    """
    mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ticker_history_list)
    exchange = get_patched_exchange(mocker, default_conf)
    file = testdatadir / 'MEME_BTC-1m.json'

    _backup_file(file)
    # do not download a new pair if refresh_pairs isn't set
    history.load_pair_history(datadir=testdatadir,
                              timeframe='1m',
                              pair='MEME/BTC')
    assert not file.is_file()
    assert log_has(
        'No history data for pair: "MEME/BTC", timeframe: 1m. '
        'Use `freqtrade download-data` to download the data', caplog
    )

    # download a new pair if refresh_pairs is set
    history.load_pair_history(datadir=testdatadir,
                              timeframe='1m',
                              refresh_pairs=True,
                              exchange=exchange,
                              pair='MEME/BTC')
    assert file.is_file()
    assert log_has_re(
        'Download history data for pair: "MEME/BTC", timeframe: 1m '
        'and store in .*', caplog
    )
    with pytest.raises(OperationalException, match=r'Exchange needs to be initialized when.*'):
        history.load_pair_history(datadir=testdatadir,
                                  timeframe='1m',
                                  refresh_pairs=True,
                                  exchange=None,
                                  pair='MEME/BTC')
    _clean_test_file(file)
Esempio n. 2
0
def test_save_results_saves_epochs(hyperopt, tmpdir, caplog) -> None:

    hyperopt.results_file = Path(tmpdir / 'ut_results.fthypt')

    hyperopt_epochs = HyperoptTools.load_filtered_results(
        hyperopt.results_file, {})
    assert log_has_re("Hyperopt file .* not found.", caplog)
    assert hyperopt_epochs == ([], 0)

    # Test writing to temp dir and reading again
    epochs = create_results()

    caplog.set_level(logging.DEBUG)

    for epoch in epochs:
        hyperopt._save_result(epoch)
    assert log_has(f"1 epoch saved to '{hyperopt.results_file}'.", caplog)

    hyperopt._save_result(epochs[0])
    assert log_has(f"2 epochs saved to '{hyperopt.results_file}'.", caplog)

    hyperopt_epochs = HyperoptTools.load_filtered_results(
        hyperopt.results_file, {})
    assert len(hyperopt_epochs) == 2
    assert hyperopt_epochs[1] == 2
    assert len(hyperopt_epochs[0]) == 2

    result_gen = HyperoptTools._read_results(hyperopt.results_file, 1)
    epoch = next(result_gen)
    assert len(epoch) == 1
    assert epoch[0] == epochs[0]
    epoch = next(result_gen)
    assert len(epoch) == 1
    epoch = next(result_gen)
    assert len(epoch) == 0
    with pytest.raises(StopIteration):
        next(result_gen)
Esempio n. 3
0
def test_process_temporary_deprecated_settings(mocker, default_conf, setting,
                                               caplog):
    patched_configuration_load_config_file(mocker, default_conf)

    # Create sections for new and deprecated settings
    # (they may not exist in the config)
    default_conf[setting[0]] = {}
    default_conf[setting[3]] = {}

    # Assign deprecated setting
    default_conf[setting[0]][setting[1]] = setting[2]
    # Assign new setting
    if setting[3]:
        default_conf[setting[3]][setting[4]] = setting[5]
    else:
        default_conf[setting[4]] = setting[5]

    # New and deprecated settings are conflicting ones
    with pytest.raises(OperationalException, match=r'DEPRECATED'):
        process_temporary_deprecated_settings(default_conf)

    caplog.clear()

    # Delete new setting
    if setting[3]:
        del default_conf[setting[3]][setting[4]]
    else:
        del default_conf[setting[4]]

    process_temporary_deprecated_settings(default_conf)
    assert log_has_re('DEPRECATED', caplog)
    # The value of the new setting shall have been set to the
    # value of the deprecated one
    if setting[3]:
        assert default_conf[setting[3]][setting[4]] == setting[2]
    else:
        assert default_conf[setting[4]] == setting[2]
Esempio n. 4
0
def test_assert_df(ohlcv_history, caplog):
    df_len = len(ohlcv_history) - 1
    ohlcv_history.loc[:, 'buy'] = 0
    ohlcv_history.loc[:, 'sell'] = 0
    # Ensure it's running when passed correctly
    _STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
                        ohlcv_history.loc[df_len, 'close'], ohlcv_history.loc[df_len, 'date'])

    with pytest.raises(StrategyError, match=r"Dataframe returned from strategy.*length\."):
        _STRATEGY.assert_df(ohlcv_history, len(ohlcv_history) + 1,
                            ohlcv_history.loc[df_len, 'close'], ohlcv_history.loc[df_len, 'date'])

    with pytest.raises(StrategyError,
                       match=r"Dataframe returned from strategy.*last close price\."):
        _STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
                            ohlcv_history.loc[df_len, 'close'] + 0.01,
                            ohlcv_history.loc[df_len, 'date'])
    with pytest.raises(StrategyError,
                       match=r"Dataframe returned from strategy.*last date\."):
        _STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
                            ohlcv_history.loc[df_len, 'close'], ohlcv_history.loc[0, 'date'])
    with pytest.raises(StrategyError,
                       match=r"No dataframe returned \(return statement missing\?\)."):
        _STRATEGY.assert_df(None, len(ohlcv_history),
                            ohlcv_history.loc[df_len, 'close'], ohlcv_history.loc[0, 'date'])
    with pytest.raises(StrategyError,
                       match="Buy column not set"):
        _STRATEGY.assert_df(ohlcv_history.drop('buy', axis=1), len(ohlcv_history),
                            ohlcv_history.loc[df_len, 'close'], ohlcv_history.loc[0, 'date'])

    _STRATEGY.disable_dataframe_checks = True
    caplog.clear()
    _STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
                        ohlcv_history.loc[2, 'close'], ohlcv_history.loc[0, 'date'])
    assert log_has_re(r"Dataframe returned from strategy.*last date\.", caplog)
    # reset to avoid problems in other tests due to test leakage
    _STRATEGY.disable_dataframe_checks = False
Esempio n. 5
0
def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplog):
    default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
                                 {'method': 'SpreadFilter', 'max_spread_ratio': 0.1}]

    mocker.patch.multiple('freqtrade.exchange.Exchange',
                          markets=PropertyMock(return_value=markets),
                          exchange_has=MagicMock(return_value=True),
                          get_tickers=tickers
                          )

    ftbot = get_patched_freqtradebot(mocker, default_conf)
    ftbot.pairlists.refresh_pairlist()

    assert len(ftbot.pairlists.whitelist) == 5

    tickers.return_value['ETH/BTC']['ask'] = 0.0
    del tickers.return_value['TKN/BTC']
    del tickers.return_value['LTC/BTC']
    mocker.patch.multiple('freqtrade.exchange.Exchange', get_tickers=tickers)

    ftbot.pairlists.refresh_pairlist()
    assert log_has_re(r'Removed .* invalid ticker data.*', caplog)

    assert len(ftbot.pairlists.whitelist) == 2
Esempio n. 6
0
def test_start_new_config(mocker, caplog, exchange):
    wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
    mocker.patch.object(Path, "exists", MagicMock(return_value=True))
    unlink_mock = mocker.patch.object(Path, "unlink", MagicMock())
    mocker.patch('freqtrade.commands.build_config_commands.ask_user_overwrite', return_value=True)

    sample_selections = {
        'max_open_trades': 3,
        'stake_currency': 'USDT',
        'stake_amount': 100,
        'fiat_display_currency': 'EUR',
        'timeframe': '15m',
        'dry_run': True,
        'exchange_name': exchange,
        'exchange_key': 'sampleKey',
        'exchange_secret': 'Samplesecret',
        'telegram': False,
        'telegram_token': 'asdf1244',
        'telegram_chat_id': '1144444',
    }
    mocker.patch('freqtrade.commands.build_config_commands.ask_user_config',
                 return_value=sample_selections)
    args = [
        "new-config",
        "--config",
        "coolconfig.json"
    ]
    start_new_config(get_args(args))

    assert log_has_re("Writing config to .*", caplog)
    assert wt_mock.call_count == 1
    assert unlink_mock.call_count == 1
    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['timeframe'] == '15m'
Esempio n. 7
0
def test_load_data_with_new_pair_1min(ohlcv_history_list, mocker, caplog,
                                      default_conf, tmpdir,
                                      candle_type) -> None:
    """
    Test load_pair_history() with 1 min timeframe
    """
    tmpdir1 = Path(tmpdir)
    mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv',
                 return_value=ohlcv_history_list)
    exchange = get_patched_exchange(mocker, default_conf)
    file = tmpdir1 / 'MEME_BTC-1m.json'

    # do not download a new pair if refresh_pairs isn't set
    load_pair_history(datadir=tmpdir1,
                      timeframe='1m',
                      pair='MEME/BTC',
                      candle_type=candle_type)
    assert not file.is_file()
    assert log_has(
        f"No history for MEME/BTC, {candle_type}, 1m found. "
        "Use `freqtrade download-data` to download the data", caplog)

    # download a new pair if refresh_pairs is set
    refresh_data(datadir=tmpdir1,
                 timeframe='1m',
                 pairs=['MEME/BTC'],
                 exchange=exchange,
                 candle_type=CandleType.SPOT)
    load_pair_history(datadir=tmpdir1,
                      timeframe='1m',
                      pair='MEME/BTC',
                      candle_type=candle_type)
    assert file.is_file()
    assert log_has_re(
        r'Download history data for pair: "MEME/BTC" \(0/1\), timeframe: 1m, '
        r'candle type: spot and store in .*', caplog)
Esempio n. 8
0
def test_setup_optimize_configuration_without_arguments(mocker, default_conf, caplog) -> None:
    patched_configuration_load_config_file(mocker, default_conf)

    args = [
        'edge',
        '--config', 'config.json',
        '--strategy', 'DefaultStrategy',
    ]

    config = setup_optimize_configuration(get_args(args), RunMode.EDGE)
    assert config['runmode'] == RunMode.EDGE

    assert 'max_open_trades' in config
    assert 'stake_currency' in config
    assert 'stake_amount' in config
    assert 'exchange' in config
    assert 'pair_whitelist' in config['exchange']
    assert 'datadir' in config
    assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
    assert 'timeframe' in config
    assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog)

    assert 'timerange' not in config
    assert 'stoploss_range' not in config
Esempio n. 9
0
def test_update_limit_order(limit_buy_order_usdt, limit_sell_order_usdt, fee, caplog):
    """
        On this test we will buy and sell a crypto currency.
        fee: 0.25% quote
        open_rate: 2.00 quote
        close_rate: 2.20 quote
        amount: = 30.0 crypto
        stake_amount
            60.0  quote
        borrowed
             0 quote
        open_value: (amount * open_rate) + (amount * open_rate * fee)
             30 * 2 + 30 * 2 * 0.0025 = 60.15 quote
        close_value:
            (amount * close_rate) - (amount * close_rate * fee) - interest
            (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835
        total_profit:
            close_value - open_value
            65.835 - 60.15             = 5.685
        total_profit_ratio:
            ((close_value/open_value) - 1) * leverage
            ((65.835 / 60.15) - 1)  * 1 = 0.0945137157107232

    """

    trade = Trade(
        id=2,
        pair='ADA/USDT',
        stake_amount=60.0,
        open_rate=2.0,
        amount=30.0,
        is_open=True,
        open_date=arrow.utcnow().datetime,
        fee_open=fee.return_value,
        fee_close=fee.return_value,
        exchange='binance',
    )
    assert trade.open_order_id is None
    assert trade.close_profit is None
    assert trade.close_date is None

    trade.open_order_id = 'something'
    trade.update(limit_buy_order_usdt)
    assert trade.open_order_id is None
    assert trade.open_rate == 2.00
    assert trade.close_profit is None
    assert trade.close_date is None
    assert log_has_re(r"LIMIT_BUY has been fulfilled for Trade\(id=2, "
                      r"pair=ADA/USDT, amount=30.00000000, open_rate=2.00000000, open_since=.*\).",
                      caplog)

    caplog.clear()
    trade.open_order_id = 'something'
    trade.update(limit_sell_order_usdt)
    assert trade.open_order_id is None
    assert trade.close_rate == 2.20
    assert trade.close_profit == round(0.0945137157107232, 8)
    assert trade.close_date is not None
    assert log_has_re(r"LIMIT_SELL has been fulfilled for Trade\(id=2, "
                      r"pair=ADA/USDT, amount=30.00000000, open_rate=2.00000000, open_since=.*\).",
                      caplog)
    caplog.clear()
Esempio n. 10
0
def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, tickers,
                                      ohlcv_history, pairlists, base_currency,
                                      whitelist_result, caplog) -> None:
    whitelist_conf['pairlists'] = pairlists
    whitelist_conf['stake_currency'] = base_currency

    ohlcv_data = {
        ('ETH/BTC', '1d'): ohlcv_history,
        ('TKN/BTC', '1d'): ohlcv_history,
        ('LTC/BTC', '1d'): ohlcv_history,
        ('XRP/BTC', '1d'): ohlcv_history,
        ('HOT/BTC', '1d'): ohlcv_history,
    }

    mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))

    if whitelist_result == 'static_in_the_middle':
        with pytest.raises(OperationalException,
                           match=r"StaticPairList can only be used in the first position "
                                 r"in the list of Pairlist Handlers."):
            freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
        return

    freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
    mocker.patch.multiple('freqtrade.exchange.Exchange',
                          get_tickers=tickers,
                          markets=PropertyMock(return_value=shitcoinmarkets)
                          )
    mocker.patch.multiple(
        'freqtrade.exchange.Exchange',
        refresh_latest_ohlcv=MagicMock(return_value=ohlcv_data),
    )

    # Provide for PerformanceFilter's dependency
    mocker.patch.multiple('freqtrade.persistence.Trade',
                          get_overall_performance=MagicMock(return_value=[])
                          )

    # Set whitelist_result to None if pairlist is invalid and should produce exception
    if whitelist_result == 'filter_at_the_beginning':
        with pytest.raises(OperationalException,
                           match=r"This Pairlist Handler should not be used at the first position "
                                 r"in the list of Pairlist Handlers."):
            freqtrade.pairlists.refresh_pairlist()
    else:
        freqtrade.pairlists.refresh_pairlist()
        whitelist = freqtrade.pairlists.whitelist

        assert isinstance(whitelist, list)

        # Verify length of pairlist matches (used for ShuffleFilter without seed)
        if type(whitelist_result) is list:
            assert whitelist == whitelist_result
        else:
            len(whitelist) == whitelist_result

        for pairlist in pairlists:
            if pairlist['method'] == 'AgeFilter' and pairlist['min_days_listed'] and \
                    len(ohlcv_history) <= pairlist['min_days_listed']:
                assert log_has_re(r'^Removed .* from whitelist, because age .* is less than '
                                  r'.* day.*', caplog)
            if pairlist['method'] == 'PrecisionFilter' and whitelist_result:
                assert log_has_re(r'^Removed .* from whitelist, because stop price .* '
                                  r'would be <= stop limit.*', caplog)
            if pairlist['method'] == 'PriceFilter' and whitelist_result:
                assert (log_has_re(r'^Removed .* from whitelist, because 1 unit is .*%$', caplog) or
                        log_has_re(r'^Removed .* from whitelist, '
                                   r'because last price < .*%$', caplog) or
                        log_has_re(r'^Removed .* from whitelist, '
                                   r'because last price > .*%$', caplog) or
                        log_has_re(r"^Removed .* from whitelist, because ticker\['last'\] "
                                   r"is empty.*", caplog))
            if pairlist['method'] == 'VolumePairList':
                logmsg = ("DEPRECATED: using any key other than quoteVolume for "
                          "VolumePairList is deprecated.")
                if pairlist['sort_key'] != 'quoteVolume':
                    assert log_has(logmsg, caplog)
                else:
                    assert not log_has(logmsg, caplog)
Esempio n. 11
0
def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee, caplog):
    """
    On this test we will buy and sell a crypto currency.

    Buy
    - Buy: 90.99181073 Crypto at 0.00001099 BTC
        (90.99181073*0.00001099 = 0.0009999 BTC)
    - Buying fee: 0.25%
    - Total cost of buy trade: 0.001002500 BTC
        ((90.99181073*0.00001099) + ((90.99181073*0.00001099)*0.0025))

    Sell
    - Sell: 90.99181073 Crypto at 0.00001173 BTC
        (90.99181073*0.00001173 = 0,00106733394 BTC)
    - Selling fee: 0.25%
    - Total cost of sell trade: 0.001064666 BTC
        ((90.99181073*0.00001173) - ((90.99181073*0.00001173)*0.0025))

    Profit/Loss: +0.000062166 BTC
        (Sell:0.001064666 - Buy:0.001002500)
    Profit/Loss percentage: 0.0620
        ((0.001064666/0.001002500)-1 = 6.20%)

    :param limit_buy_order:
    :param limit_sell_order:
    :return:
    """

    trade = Trade(
        id=2,
        pair='ETH/BTC',
        stake_amount=0.001,
        open_rate=0.01,
        amount=5,
        is_open=True,
        open_date=arrow.utcnow().datetime,
        fee_open=fee.return_value,
        fee_close=fee.return_value,
        exchange='bittrex',
    )
    assert trade.open_order_id is None
    assert trade.close_profit is None
    assert trade.close_date is None

    trade.open_order_id = 'something'
    trade.update(limit_buy_order)
    assert trade.open_order_id is None
    assert trade.open_rate == 0.00001099
    assert trade.close_profit is None
    assert trade.close_date is None
    assert log_has_re(
        r"LIMIT_BUY has been fulfilled for Trade\(id=2, "
        r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=.*\).",
        caplog)

    caplog.clear()
    trade.open_order_id = 'something'
    trade.update(limit_sell_order)
    assert trade.open_order_id is None
    assert trade.close_rate == 0.00001173
    assert trade.close_profit == 0.06201058
    assert trade.close_date is not None
    assert log_has_re(
        r"LIMIT_SELL has been fulfilled for Trade\(id=2, "
        r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=.*\).",
        caplog)
Esempio n. 12
0
def test_LowProfitPairs(mocker, default_conf, fee, caplog):
    default_conf['protections'] = [{
        "method": "LowProfitPairs",
        "lookback_period": 400,
        "stop_duration": 60,
        "trade_limit": 2,
        "required_profit": 0.0,
    }]
    freqtrade = get_patched_freqtradebot(mocker, default_conf)
    message = r"Trading stopped due to .*"
    assert not freqtrade.protections.global_stop()
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')

    assert not log_has_re(message, caplog)
    caplog.clear()

    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=800,
            min_ago_close=450,
            profit_rate=0.9,
        ))

    # Not locked with 1 trade
    assert not freqtrade.protections.global_stop()
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')
    assert not PairLocks.is_pair_locked('XRP/BTC')
    assert not PairLocks.is_global_lock()

    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=200,
            min_ago_close=120,
            profit_rate=0.9,
        ))

    # Not locked with 1 trade (first trade is outside of lookback_period)
    assert not freqtrade.protections.global_stop()
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')
    assert not PairLocks.is_pair_locked('XRP/BTC')
    assert not PairLocks.is_global_lock()

    # Add positive trade
    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.ROI.value,
            min_ago_open=20,
            min_ago_close=10,
            profit_rate=1.15,
        ))
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')
    assert not PairLocks.is_pair_locked('XRP/BTC')

    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=110,
            min_ago_close=20,
            profit_rate=0.8,
        ))

    # Locks due to 2nd trade
    assert not freqtrade.protections.global_stop()
    assert freqtrade.protections.stop_per_pair('XRP/BTC')
    assert PairLocks.is_pair_locked('XRP/BTC')
    assert not PairLocks.is_global_lock()
Esempio n. 13
0
def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
    patch_exchange(mocker, mock_markets=True)
    mocker.patch.multiple(
        'freqtrade.exchange.Exchange',
        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}')
Esempio n. 14
0
def test_stoploss_guard(mocker, default_conf, fee, caplog):
    default_conf['protections'] = [{
        "method": "StoplossGuard",
        "lookback_period": 60,
        "stop_duration": 40,
        "trade_limit": 3
    }]
    freqtrade = get_patched_freqtradebot(mocker, default_conf)
    message = r"Trading stopped due to .*"
    assert not freqtrade.protections.global_stop()
    assert not log_has_re(message, caplog)
    caplog.clear()

    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=200,
            min_ago_close=30,
        ))

    assert not freqtrade.protections.global_stop()
    assert not log_has_re(message, caplog)
    caplog.clear()
    # This trade does not count, as it's closed too long ago
    Trade.session.add(
        generate_mock_trade(
            'BCH/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=250,
            min_ago_close=100,
        ))

    Trade.session.add(
        generate_mock_trade(
            'ETH/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=240,
            min_ago_close=30,
        ))
    # 3 Trades closed - but the 2nd has been closed too long ago.
    assert not freqtrade.protections.global_stop()
    assert not log_has_re(message, caplog)
    caplog.clear()

    Trade.session.add(
        generate_mock_trade(
            'LTC/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=180,
            min_ago_close=30,
        ))

    assert freqtrade.protections.global_stop()
    assert log_has_re(message, caplog)
    assert PairLocks.is_global_lock()

    # Test 5m after lock-period - this should try and relock the pair, but end-time
    # should be the previous end-time
    end_time = PairLocks.get_pair_longest_lock('*').lock_end_time + timedelta(
        minutes=5)
    assert freqtrade.protections.global_stop(end_time)
    assert not PairLocks.is_global_lock(end_time)
def test_check_exchange(default_conf, caplog) -> None:
    # Test an officially supported by Freqtrade team exchange
    default_conf['runmode'] = RunMode.DRY_RUN
    default_conf.get('exchange').update({'name': 'BITTREX'})
    assert check_exchange(default_conf)
    assert log_has_re(
        r"Exchange .* is officially supported by the Freqtrade development team\.",
        caplog)
    caplog.clear()

    # Test an officially supported by Freqtrade team exchange
    default_conf.get('exchange').update({'name': 'binance'})
    assert check_exchange(default_conf)
    assert log_has_re(
        r"Exchange .* is officially supported by the Freqtrade development team\.",
        caplog)
    caplog.clear()

    # Test an available exchange, supported by ccxt
    default_conf.get('exchange').update({'name': 'huobipro'})
    assert check_exchange(default_conf)
    assert log_has_re(
        r"Exchange .* is known to the the ccxt library, available for the bot, "
        r"but not officially supported "
        r"by the Freqtrade development team\. .*", caplog)
    caplog.clear()

    # Test a 'bad' exchange, which known to have serious problems
    default_conf.get('exchange').update({'name': 'bitmex'})
    with pytest.raises(
            OperationalException,
            match=r"Exchange .* is known to not work with the bot yet.*"):
        check_exchange(default_conf)
    caplog.clear()

    # Test a 'bad' exchange with check_for_bad=False
    default_conf.get('exchange').update({'name': 'bitmex'})
    assert check_exchange(default_conf, False)
    assert log_has_re(
        r"Exchange .* is known to the the ccxt library, available for the bot, "
        r"but not officially supported "
        r"by the Freqtrade development team\. .*", caplog)
    caplog.clear()

    # Test an invalid exchange
    default_conf.get('exchange').update({'name': 'unknown_exchange'})
    with pytest.raises(
            OperationalException,
            match=
            r'Exchange "unknown_exchange" is not known to the ccxt library '
            r'and therefore not available for the bot.*'):
        check_exchange(default_conf)

    # Test no exchange...
    default_conf.get('exchange').update({'name': ''})
    default_conf['runmode'] = RunMode.PLOT
    assert check_exchange(default_conf)

    # Test no exchange...
    default_conf.get('exchange').update({'name': ''})
    default_conf['runmode'] = RunMode.UTIL_EXCHANGE
    with pytest.raises(OperationalException,
                       match=r'This command requires a configured exchange.*'):
        check_exchange(default_conf)
Esempio n. 16
0
def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog,
                                only_per_pair):
    default_conf['protections'] = [{
        "method": "StoplossGuard",
        "lookback_period": 60,
        "trade_limit": 2,
        "stop_duration": 60,
        "only_per_pair": only_per_pair
    }]
    freqtrade = get_patched_freqtradebot(mocker, default_conf)
    message = r"Trading stopped due to .*"
    pair = 'XRP/BTC'
    assert not freqtrade.protections.stop_per_pair(pair)
    assert not freqtrade.protections.global_stop()
    assert not log_has_re(message, caplog)
    caplog.clear()

    Trade.session.add(
        generate_mock_trade(
            pair,
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=200,
            min_ago_close=30,
            profit_rate=0.9,
        ))

    assert not freqtrade.protections.stop_per_pair(pair)
    assert not freqtrade.protections.global_stop()
    assert not log_has_re(message, caplog)
    caplog.clear()
    # This trade does not count, as it's closed too long ago
    Trade.session.add(
        generate_mock_trade(
            pair,
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=250,
            min_ago_close=100,
            profit_rate=0.9,
        ))
    # Trade does not count for per pair stop as it's the wrong pair.
    Trade.session.add(
        generate_mock_trade(
            'ETH/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=240,
            min_ago_close=30,
            profit_rate=0.9,
        ))
    # 3 Trades closed - but the 2nd has been closed too long ago.
    assert not freqtrade.protections.stop_per_pair(pair)
    assert freqtrade.protections.global_stop() != only_per_pair
    if not only_per_pair:
        assert log_has_re(message, caplog)
    else:
        assert not log_has_re(message, caplog)

    caplog.clear()

    # 2nd Trade that counts with correct pair
    Trade.session.add(
        generate_mock_trade(
            pair,
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=180,
            min_ago_close=30,
            profit_rate=0.9,
        ))

    assert freqtrade.protections.stop_per_pair(pair)
    assert freqtrade.protections.global_stop() != only_per_pair
    assert PairLocks.is_pair_locked(pair)
    assert PairLocks.is_global_lock() != only_per_pair
Esempio n. 17
0
def test_download_trades_history(trades_history, mocker, default_conf,
                                 testdatadir, caplog) -> None:

    ght_mock = MagicMock(
        side_effect=lambda pair, *args, **kwargs: (pair, trades_history))
    mocker.patch('freqtrade.exchange.Exchange.get_historic_trades', ght_mock)
    exchange = get_patched_exchange(mocker, default_conf)
    file1 = testdatadir / 'ETH_BTC-trades.json.gz'
    data_handler = get_datahandler(testdatadir, data_format='jsongz')
    _backup_file(file1)

    assert not file1.is_file()

    assert _download_trades_history(data_handler=data_handler,
                                    exchange=exchange,
                                    pair='ETH/BTC')
    assert log_has("New Amount of trades: 5", caplog)
    assert file1.is_file()

    ght_mock.reset_mock()
    since_time = int(trades_history[-3][0] // 1000)
    since_time2 = int(trades_history[-1][0] // 1000)
    timerange = TimeRange('date', None, since_time, 0)
    assert _download_trades_history(data_handler=data_handler,
                                    exchange=exchange,
                                    pair='ETH/BTC',
                                    timerange=timerange)

    assert ght_mock.call_count == 1
    # Check this in seconds - since we had to convert to seconds above too.
    assert int(ght_mock.call_args_list[0][1]['since'] //
               1000) == since_time2 - 5
    assert ght_mock.call_args_list[0][1]['from_id'] is not None

    # clean files freshly downloaded
    _clean_test_file(file1)

    mocker.patch('freqtrade.exchange.Exchange.get_historic_trades',
                 MagicMock(side_effect=ValueError))

    assert not _download_trades_history(
        data_handler=data_handler, exchange=exchange, pair='ETH/BTC')
    assert log_has_re(
        'Failed to download historic trades for pair: "ETH/BTC".*', caplog)

    file2 = testdatadir / 'XRP_ETH-trades.json.gz'

    _backup_file(file2, True)

    ght_mock.reset_mock()
    mocker.patch('freqtrade.exchange.Exchange.get_historic_trades', ght_mock)
    # Since before first start date
    since_time = int(trades_history[0][0] // 1000) - 500
    timerange = TimeRange('date', None, since_time, 0)

    assert _download_trades_history(data_handler=data_handler,
                                    exchange=exchange,
                                    pair='XRP/ETH',
                                    timerange=timerange)

    assert ght_mock.call_count == 1

    assert int(ght_mock.call_args_list[0][1]['since'] // 1000) == since_time
    assert ght_mock.call_args_list[0][1]['from_id'] is None
    assert log_has_re(
        r'Start earlier than available data. Redownloading trades for.*',
        caplog)
    _clean_test_file(file2)
Esempio n. 18
0
def test_MaxDrawdown(mocker, default_conf, fee, caplog):
    default_conf['protections'] = [{
        "method": "MaxDrawdown",
        "lookback_period": 1000,
        "stop_duration": 60,
        "trade_limit": 3,
        "max_allowed_drawdown": 0.15
    }]
    freqtrade = get_patched_freqtradebot(mocker, default_conf)
    message = r"Trading stopped due to Max.*"

    assert not freqtrade.protections.global_stop()
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')
    caplog.clear()

    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=1000,
            min_ago_close=900,
            profit_rate=1.1,
        ))
    Trade.session.add(
        generate_mock_trade(
            'ETH/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=1000,
            min_ago_close=900,
            profit_rate=1.1,
        ))
    Trade.session.add(
        generate_mock_trade(
            'NEO/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=1000,
            min_ago_close=900,
            profit_rate=1.1,
        ))
    # No losing trade yet ... so max_drawdown will raise exception
    assert not freqtrade.protections.global_stop()
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')

    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=500,
            min_ago_close=400,
            profit_rate=0.9,
        ))
    # Not locked with one trade
    assert not freqtrade.protections.global_stop()
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')
    assert not PairLocks.is_pair_locked('XRP/BTC')
    assert not PairLocks.is_global_lock()

    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.STOP_LOSS.value,
            min_ago_open=1200,
            min_ago_close=1100,
            profit_rate=0.5,
        ))

    # Not locked with 1 trade (2nd trade is outside of lookback_period)
    assert not freqtrade.protections.global_stop()
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')
    assert not PairLocks.is_pair_locked('XRP/BTC')
    assert not PairLocks.is_global_lock()
    assert not log_has_re(message, caplog)

    # Winning trade ... (should not lock, does not change drawdown!)
    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.ROI.value,
            min_ago_open=320,
            min_ago_close=410,
            profit_rate=1.5,
        ))
    assert not freqtrade.protections.global_stop()
    assert not PairLocks.is_global_lock()

    caplog.clear()

    # Add additional negative trade, causing a loss of > 15%
    Trade.session.add(
        generate_mock_trade(
            'XRP/BTC',
            fee.return_value,
            False,
            sell_reason=SellType.ROI.value,
            min_ago_open=20,
            min_ago_close=10,
            profit_rate=0.8,
        ))
    assert not freqtrade.protections.stop_per_pair('XRP/BTC')
    # local lock not supported
    assert not PairLocks.is_pair_locked('XRP/BTC')
    assert freqtrade.protections.global_stop()
    assert PairLocks.is_global_lock()
    assert log_has_re(message, caplog)
Esempio n. 19
0
def test_parse_args_None(caplog) -> None:
    with pytest.raises(SystemExit):
        main([])
    assert log_has_re(r"Usage of Freqtrade requires a subcommand.*", caplog)
Esempio n. 20
0
def test_api_run(default_conf, mocker, caplog):
    default_conf.update({
        "api_server": {
            "enabled": True,
            "listen_ip_address": "127.0.0.1",
            "listen_port": 8080,
            "username": "******",
            "password": "******",
        }
    })
    mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())

    server_mock = MagicMock()
    mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer',
                 server_mock)

    apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)),
                          default_conf)

    assert server_mock.call_count == 1
    assert apiserver._config == default_conf
    apiserver.start_api()
    assert server_mock.call_count == 2
    assert server_mock.call_args_list[0][0][0].host == "127.0.0.1"
    assert server_mock.call_args_list[0][0][0].port == 8080
    assert isinstance(server_mock.call_args_list[0][0][0].app, FastAPI)

    assert log_has("Starting HTTP Server at 127.0.0.1:8080", caplog)
    assert log_has("Starting Local Rest Server.", caplog)

    # Test binding to public
    caplog.clear()
    server_mock.reset_mock()
    apiserver._config.update({
        "api_server": {
            "enabled": True,
            "listen_ip_address": "0.0.0.0",
            "listen_port": 8089,
            "password": "",
        }
    })
    apiserver.start_api()

    assert server_mock.call_count == 1
    assert server_mock.call_args_list[0][0][0].host == "0.0.0.0"
    assert server_mock.call_args_list[0][0][0].port == 8089
    assert isinstance(server_mock.call_args_list[0][0][0].app, FastAPI)
    assert log_has("Starting HTTP Server at 0.0.0.0:8089", caplog)
    assert log_has("Starting Local Rest Server.", caplog)
    assert log_has(
        "SECURITY WARNING - Local Rest Server listening to external connections",
        caplog)
    assert log_has(
        "SECURITY WARNING - This is insecure please set to your loopback,"
        "e.g 127.0.0.1 in config.json", caplog)
    assert log_has(
        "SECURITY WARNING - No password for local REST Server defined. "
        "Please make sure that this is intentional!", caplog)
    assert log_has_re(
        "SECURITY WARNING - `jwt_secret_key` seems to be default.*", caplog)

    # Test crashing API server
    caplog.clear()
    mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer',
                 MagicMock(side_effect=Exception))
    apiserver.start_api()
    assert log_has("Api server failed to start.", caplog)
Esempio n. 21
0
def test_custom_exit(default_conf, fee, caplog) -> None:

    strategy = StrategyResolver.load_strategy(default_conf)
    trade = Trade(
        pair='ETH/BTC',
        stake_amount=0.01,
        amount=1,
        open_date=arrow.utcnow().shift(hours=-1).datetime,
        fee_open=fee.return_value,
        fee_close=fee.return_value,
        exchange='binance',
        open_rate=1,
    )

    now = arrow.utcnow().datetime
    res = strategy.should_exit(trade,
                               1,
                               now,
                               enter=False,
                               exit_=False,
                               low=None,
                               high=None)

    assert res.exit_flag is False
    assert res.exit_type == ExitType.NONE

    strategy.custom_exit = MagicMock(return_value=True)
    res = strategy.should_exit(trade,
                               1,
                               now,
                               enter=False,
                               exit_=False,
                               low=None,
                               high=None)
    assert res.exit_flag is True
    assert res.exit_type == ExitType.CUSTOM_EXIT
    assert res.exit_reason == 'custom_exit'

    strategy.custom_exit = MagicMock(return_value='hello world')

    res = strategy.should_exit(trade,
                               1,
                               now,
                               enter=False,
                               exit_=False,
                               low=None,
                               high=None)
    assert res.exit_type == ExitType.CUSTOM_EXIT
    assert res.exit_flag is True
    assert res.exit_reason == 'hello world'

    caplog.clear()
    strategy.custom_exit = MagicMock(return_value='h' * 100)
    res = strategy.should_exit(trade,
                               1,
                               now,
                               enter=False,
                               exit_=False,
                               low=None,
                               high=None)
    assert res.exit_type == ExitType.CUSTOM_EXIT
    assert res.exit_flag is True
    assert res.exit_reason == 'h' * 64
    assert log_has_re(
        'Custom exit reason returned from custom_exit is too long.*', caplog)