diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 6fc9650d2..64bdd8d9f 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -1,10 +1,58 @@ # pragma pylint: disable=missing-docstring import json +from datetime import datetime +from unittest.mock import MagicMock + import pytest +from jsonschema import validate +from telegram import Message, Chat, Update + +from freqtrade.misc import CONF_SCHEMA @pytest.fixture(scope="module") -def conf(): +def default_conf(): + """ Returns validated configuration suitable for most tests """ + configuration = { + "max_open_trades": 3, + "stake_currency": "BTC", + "stake_amount": 0.05, + "dry_run": True, + "minimal_roi": { + "40": 0.0, + "30": 0.01, + "20": 0.02, + "0": 0.04 + }, + "stoploss": -0.05, + "bid_strategy": { + "ask_last_balance": 0.0 + }, + "exchange": { + "name": "bittrex", + "enabled": True, + "key": "key", + "secret": "secret", + "pair_whitelist": [ + "BTC_ETH", + "BTC_TKN", + "BTC_TRST", + "BTC_SWT" + ] + }, + "telegram": { + "enabled": True, + "token": "token", + "chat_id": "0" + }, + "initial_state": "running" + } + validate(configuration, CONF_SCHEMA) + return configuration + + +@pytest.fixture(scope="module") +def backtest_conf(): return { "minimal_roi": { "40": 0.0, @@ -24,3 +72,47 @@ def backdata(): with open('freqtrade/tests/testdata/' + pair + '.json') as data_file: result[pair] = json.load(data_file) return result + + +@pytest.fixture +def update(): + _update = Update(0) + _update.message = Message(0, 0, datetime.utcnow(), Chat(0, 0)) + return _update + + +@pytest.fixture +def ticker(): + return MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, + 'last': 0.07256061, + }) + + +@pytest.fixture +def limit_buy_order(): + return { + 'id': 'mocked_limit_buy', + 'type': 'LIMIT_BUY', + 'pair': 'mocked', + 'opened': datetime.utcnow(), + 'rate': 0.07256061, + 'amount': 206.43811673387373, + 'remaining': 0.0, + 'closed': datetime.utcnow(), + } + + +@pytest.fixture +def limit_sell_order(): + return { + 'id': 'mocked_limit_sell', + 'type': 'LIMIT_SELL', + 'pair': 'mocked', + 'opened': datetime.utcnow(), + 'rate': 0.0802134, + 'amount': 206.43811673387373, + 'remaining': 0.0, + 'closed': datetime.utcnow(), + } diff --git a/freqtrade/tests/test_analyze.py b/freqtrade/tests/test_analyze.py index 3ae61d14f..3c0ce15b2 100644 --- a/freqtrade/tests/test_analyze.py +++ b/freqtrade/tests/test_analyze.py @@ -14,12 +14,12 @@ def result(): return parse_ticker_dataframe(json.load(data_file)) -def test_dataframe_has_correct_columns(result): +def test_dataframe_correct_columns(result): assert result.columns.tolist() == \ ['close', 'high', 'low', 'open', 'date', 'volume'] -def test_dataframe_has_correct_length(result): +def test_dataframe_correct_length(result): assert len(result.index) == 5751 diff --git a/freqtrade/tests/test_backtesting.py b/freqtrade/tests/test_backtesting.py index 6cc49c991..7f019dd17 100644 --- a/freqtrade/tests/test_backtesting.py +++ b/freqtrade/tests/test_backtesting.py @@ -1,5 +1,4 @@ # pragma pylint: disable=missing-docstring -import json import logging import os @@ -26,11 +25,11 @@ def print_pair_results(pair, results): print(format_results(results[results.currency == pair])) -def backtest(conf, backdata, mocker): +def backtest(backtest_conf, backdata, mocker): trades = [] exchange._API = Bittrex({'key': '', 'secret': ''}) mocked_history = mocker.patch('freqtrade.analyze.get_ticker_history') - mocker.patch.dict('freqtrade.main._CONF', conf) + mocker.patch.dict('freqtrade.main._CONF', backtest_conf) mocker.patch('arrow.utcnow', return_value=arrow.get('2017-08-20T14:50:00')) for pair, pair_data in backdata.items(): mocked_history.return_value = pair_data @@ -56,8 +55,8 @@ def backtest(conf, backdata, mocker): @pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set") -def test_backtest(conf, backdata, mocker, report=True): - results = backtest(conf, backdata, mocker) +def test_backtest(backtest_conf, backdata, mocker, report=True): + results = backtest(backtest_conf, backdata, mocker) print('====================== BACKTESTING REPORT ================================') for pair in backdata: diff --git a/freqtrade/tests/test_hyperopt.py b/freqtrade/tests/test_hyperopt.py index ed8a4a0e6..50f3b94f4 100644 --- a/freqtrade/tests/test_hyperopt.py +++ b/freqtrade/tests/test_hyperopt.py @@ -63,13 +63,13 @@ def buy_strategy_generator(params): @pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set") -def test_hyperopt(conf, backdata, mocker): +def test_hyperopt(backtest_conf, backdata, mocker): mocked_buy_trend = mocker.patch('freqtrade.analyze.populate_buy_trend') def optimizer(params): mocked_buy_trend.side_effect = buy_strategy_generator(params) - results = backtest(conf, backdata, mocker) + results = backtest(backtest_conf, backdata, mocker) result = format_results(results) print(result) diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index b49a685b6..15754cbc9 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -1,71 +1,25 @@ # pragma pylint: disable=missing-docstring import copy -from datetime import datetime from unittest.mock import MagicMock, call -import pytest -from jsonschema import validate - from freqtrade.exchange import Exchanges from freqtrade.main import create_trade, handle_trade, close_trade_if_fulfilled, init, \ get_target_bid -from freqtrade.misc import CONF_SCHEMA from freqtrade.persistence import Trade -@pytest.fixture -def conf(): - configuration = { - "max_open_trades": 3, - "stake_currency": "BTC", - "stake_amount": 0.05, - "dry_run": True, - "minimal_roi": { - "2880": 0.005, - "720": 0.01, - "0": 0.02 - }, - "bid_strategy": { - "ask_last_balance": 0.0 - }, - "exchange": { - "name": "bittrex", - "enabled": True, - "key": "key", - "secret": "secret", - "pair_whitelist": [ - "BTC_ETH", - "BTC_TKN", - "BTC_TRST", - "BTC_SWT", - ] - }, - "telegram": { - "enabled": True, - "token": "token", - "chat_id": "chat_id" - } - } - validate(configuration, CONF_SCHEMA) - return configuration - - -def test_create_trade(conf, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_create_trade(default_conf, ticker, limit_buy_order, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) buy_signal = mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - }), + get_ticker=ticker, buy=MagicMock(return_value='mocked_limit_buy')) # Save state of current whitelist - whitelist = copy.deepcopy(conf['exchange']['pair_whitelist']) + whitelist = copy.deepcopy(default_conf['exchange']['pair_whitelist']) - init(conf, 'sqlite://') + init(default_conf, 'sqlite://') for _ in ['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']: trade = create_trade(15.0) Trade.session.add(trade) @@ -77,29 +31,20 @@ def test_create_trade(conf, mocker): assert trade.exchange == Exchanges.BITTREX.name # Simulate fulfilled LIMIT_BUY order for trade - trade.update({ - 'id': 'mocked_limit_buy', - 'type': 'LIMIT_BUY', - 'pair': 'mocked', - 'opened': datetime.utcnow(), - 'rate': 0.072661, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': datetime.utcnow(), - }) + trade.update(limit_buy_order) - assert trade.open_rate == 0.072661 + assert trade.open_rate == 0.07256061 assert trade.amount == 206.43811673387373 - assert whitelist == conf['exchange']['pair_whitelist'] + assert whitelist == default_conf['exchange']['pair_whitelist'] buy_signal.assert_has_calls( [call('BTC_ETH'), call('BTC_TKN'), call('BTC_TRST'), call('BTC_SWT')] ) -def test_handle_trade(conf, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_handle_trade(default_conf, limit_sell_order, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), @@ -116,24 +61,15 @@ def test_handle_trade(conf, mocker): assert trade.open_order_id == 'mocked_limit_sell' # Simulate fulfilled LIMIT_SELL order for trade - trade.update({ - 'id': 'mocked_sell_limit', - 'type': 'LIMIT_SELL', - 'pair': 'mocked', - 'opened': datetime.utcnow(), - 'rate': 0.17256061, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': datetime.utcnow(), - }) + trade.update(limit_sell_order) - assert trade.close_rate == 0.17256061 - assert trade.close_profit == 1.3698725 + assert trade.close_rate == 0.0802134 + assert trade.close_profit == 0.10046755 assert trade.close_date is not None -def test_close_trade(conf, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_close_trade(default_conf, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) trade = Trade.query.filter(Trade.is_open.is_(True)).first() assert trade @@ -155,6 +91,6 @@ def test_balance_fully_last_side(mocker): assert get_target_bid({'ask': 20, 'last': 10}) == 10 -def test_balance_when_last_bigger_than_ask(mocker): +def test_balance_bigger_last_ask(mocker): mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}) assert get_target_bid({'ask': 5, 'last': 10}) == 5 diff --git a/freqtrade/tests/test_telegram.py b/freqtrade/tests/test_telegram.py index 87e27a668..f9e91a0c6 100644 --- a/freqtrade/tests/test_telegram.py +++ b/freqtrade/tests/test_telegram.py @@ -1,85 +1,34 @@ -# pragma pylint: disable=missing-docstring -import logging +# pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors import re from datetime import datetime from unittest.mock import MagicMock -import pytest -from jsonschema import validate -from telegram import Bot, Update, Message, Chat +from telegram import Bot -from freqtrade import exchange from freqtrade.main import init, create_trade -from freqtrade.misc import update_state, State, get_state, CONF_SCHEMA +from freqtrade.misc import update_state, State, get_state from freqtrade.persistence import Trade from freqtrade.rpc.telegram import ( _status, _status_table, _profit, _forcesell, _performance, _count, _start, _stop, _balance ) -@pytest.fixture -def conf(): - configuration = { - "max_open_trades": 3, - "stake_currency": "BTC", - "stake_amount": 0.05, - "dry_run": True, - "minimal_roi": { - "2880": 0.005, - "720": 0.01, - "0": 0.02 - }, - "bid_strategy": { - "ask_last_balance": 0.0 - }, - "exchange": { - "name": "bittrex", - "enabled": True, - "key": "key", - "secret": "secret", - "pair_whitelist": [ - "BTC_ETH", - "BTC_ETC" - ] - }, - "telegram": { - "enabled": True, - "token": "token", - "chat_id": "0" - }, - "initial_state": "running" - } - validate(configuration, CONF_SCHEMA) - return configuration - - -@pytest.fixture -def update(): - _update = Update(0) - _update.message = Message(0, 0, datetime.utcnow(), Chat(0, 0)) - return _update - - class MagicBot(MagicMock, Bot): pass -def test_status_handle(conf, update, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_status_handle(default_conf, update, ticker, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - })) - init(conf, 'sqlite://') + get_ticker=ticker) + init(default_conf, 'sqlite://') # Create some test data trade = create_trade(15.0) @@ -94,24 +43,20 @@ def test_status_handle(conf, update, mocker): assert '[BTC_ETH]' in msg_mock.call_args_list[-1][0][0] -def test_status_table_handle(conf, update, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_status_table_handle(default_conf, update, ticker, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) msg_mock = MagicMock() mocker.patch.multiple( 'freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - }), + get_ticker=ticker, buy=MagicMock(return_value='mocked_order_id')) - init(conf, 'sqlite://') + init(default_conf, 'sqlite://') # Create some test data trade = create_trade(15.0) @@ -121,7 +66,7 @@ def test_status_table_handle(conf, update, mocker): _status_table(bot=MagicBot(), update=update) - text = re.sub('<\/?pre>', '', msg_mock.call_args_list[-1][0][0]) + text = re.sub('', '', msg_mock.call_args_list[-1][0][0]) line = text.split("\n") fields = re.sub('[ ]+', ' ', line[2].strip()).split(' ') @@ -130,49 +75,27 @@ def test_status_table_handle(conf, update, mocker): assert msg_mock.call_count == 2 -def test_profit_handle(conf, update, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_profit_handle(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - })) - init(conf, 'sqlite://') + get_ticker=ticker) + init(default_conf, 'sqlite://') # Create some test data trade = create_trade(15.0) assert trade # Simulate fulfilled LIMIT_BUY order for trade - trade.update({ - 'id': 'mocked_limit_buy', - 'type': 'LIMIT_BUY', - 'pair': 'mocked', - 'opened': datetime.utcnow(), - 'rate': 0.07256061, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': datetime.utcnow(), - }) + trade.update(limit_buy_order) # Simulate fulfilled LIMIT_SELL order for trade - trade.update({ - 'id': 'mocked_limit_sell', - 'type': 'LIMIT_SELL', - 'pair': 'mocked', - 'opened': datetime.utcnow(), - 'rate': 0.0802134, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': datetime.utcnow(), - }) + trade.update(limit_sell_order) trade.close_date = datetime.utcnow() trade.is_open = False @@ -185,22 +108,18 @@ def test_profit_handle(conf, update, mocker): assert 'Best Performing:* `BTC_ETH: 10.05%`' in msg_mock.call_args_list[-1][0][0] -def test_forcesell_handle(conf, update, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_forcesell_handle(default_conf, update, ticker, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - })) - init(conf, 'sqlite://') + get_ticker=ticker) + init(default_conf, 'sqlite://') # Create some test data trade = create_trade(15.0) @@ -217,50 +136,28 @@ def test_forcesell_handle(conf, update, mocker): assert '0.07256061 (profit: ~-0.64%)' in msg_mock.call_args_list[-1][0][0] -def test_performance_handle(conf, update, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_performance_handle(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - })) - init(conf, 'sqlite://') + get_ticker=ticker) + init(default_conf, 'sqlite://') # Create some test data trade = create_trade(15.0) assert trade # Simulate fulfilled LIMIT_BUY order for trade - trade.update({ - 'id': 'mocked_limit_buy', - 'type': 'LIMIT_BUY', - 'pair': 'mocked', - 'opened': datetime.utcnow(), - 'rate': 0.07256061, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': datetime.utcnow(), - }) + trade.update(limit_buy_order) # Simulate fulfilled LIMIT_SELL order for trade - trade.update({ - 'id': 'mocked_limit_sell', - 'type': 'LIMIT_SELL', - 'pair': 'mocked', - 'opened': datetime.utcnow(), - 'rate': 0.0802134, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': datetime.utcnow(), - }) + trade.update(limit_sell_order) trade.close_date = datetime.utcnow() trade.is_open = False @@ -273,24 +170,20 @@ def test_performance_handle(conf, update, mocker): assert 'BTC_ETH\t10.05%' in msg_mock.call_args_list[-1][0][0] -def test_count_handle(conf, update, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_count_handle(default_conf, update, ticker, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) msg_mock = MagicMock() mocker.patch.multiple( 'freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - }), + get_ticker=ticker, buy=MagicMock(return_value='mocked_order_id')) - init(conf, 'sqlite://') + init(default_conf, 'sqlite://') # Create some test data trade = create_trade(15.0) @@ -303,20 +196,20 @@ def test_count_handle(conf, update, mocker): _count(bot=MagicBot(), update=update) line = msg_mock.call_args_list[-1][0][0].split("\n") - assert line[2] == '{}/{}'.format(2, conf['max_open_trades']) + assert line[2] == '{}/{}'.format(2, default_conf['max_open_trades']) -def test_start_handle(conf, update, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_start_handle(default_conf, update, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', - _CONF=conf, + _CONF=default_conf, init=MagicMock()) - init(conf, 'sqlite://') + init(default_conf, 'sqlite://') update_state(State.STOPPED) assert get_state() == State.STOPPED @@ -325,17 +218,17 @@ def test_start_handle(conf, update, mocker): assert msg_mock.call_count == 0 -def test_stop_handle(conf, update, mocker): - mocker.patch.dict('freqtrade.main._CONF', conf) +def test_stop_handle(default_conf, update, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', - _CONF=conf, + _CONF=default_conf, init=MagicMock()) - init(conf, 'sqlite://') + init(default_conf, 'sqlite://') update_state(State.RUNNING) assert get_state() == State.RUNNING @@ -345,17 +238,17 @@ def test_stop_handle(conf, update, mocker): assert 'Stopping trader' in msg_mock.call_args_list[0][0][0] -def test_balance_handle(conf, update, mocker): +def test_balance_handle(default_conf, update, mocker): mock_balance = [{ 'Currency': 'BTC', 'Balance': 10.0, 'Available': 12.0, 'Pending': 0.0, 'CryptoAddress': 'XXXX'}] - mocker.patch.dict('freqtrade.main._CONF', conf) + mocker.patch.dict('freqtrade.main._CONF', default_conf) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', - _CONF=conf, + _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange',