diff --git a/freqtrade/tests/test_analyze.py b/freqtrade/tests/test_analyze.py index d463ba560..e716eb3ad 100644 --- a/freqtrade/tests/test_analyze.py +++ b/freqtrade/tests/test_analyze.py @@ -1,7 +1,5 @@ # pragma pylint: disable=missing-docstring -import unittest -from unittest.mock import patch - +import pytest import arrow from pandas import DataFrame @@ -19,34 +17,31 @@ RESULT_BITTREX = { ] } -class TestAnalyze(unittest.TestCase): - def setUp(self): - self.result = parse_ticker_dataframe(RESULT_BITTREX['result'], arrow.get('2017-08-30T10:00:00')) +@pytest.fixture +def result(): + return parse_ticker_dataframe(RESULT_BITTREX['result'], arrow.get('2017-08-30T10:00:00')) - def test_1_dataframe_has_correct_columns(self): - self.assertEqual(self.result.columns.tolist(), - ['close', 'high', 'low', 'open', 'date', 'volume']) +def test_dataframe_has_correct_columns(result): + assert result.columns.tolist() == \ + ['close', 'high', 'low', 'open', 'date', 'volume'] - def test_2_orders_by_date(self): - self.assertEqual(self.result['date'].tolist(), - ['2017-08-30T10:34:00', - '2017-08-30T10:37:00', - '2017-08-30T10:40:00', - '2017-08-30T10:42:00']) +def test_orders_by_date(result): + assert result['date'].tolist() == \ + ['2017-08-30T10:34:00', + '2017-08-30T10:37:00', + '2017-08-30T10:40:00', + '2017-08-30T10:42:00'] - def test_3_populates_buy_trend(self): - dataframe = populate_buy_trend(populate_indicators(self.result)) - self.assertTrue('buy' in dataframe.columns) - self.assertTrue('buy_price' in dataframe.columns) +def test_populates_buy_trend(result): + dataframe = populate_buy_trend(populate_indicators(result)) + assert 'buy' in dataframe.columns + assert 'buy_price' in dataframe.columns - def test_4_returns_latest_buy_signal(self): - buydf = DataFrame([{'buy': 1, 'date': arrow.utcnow()}]) - with patch('freqtrade.analyze.analyze_ticker', return_value=buydf): - self.assertEqual(get_buy_signal('BTC-ETH'), True) - buydf = DataFrame([{'buy': 0, 'date': arrow.utcnow()}]) - with patch('freqtrade.analyze.analyze_ticker', return_value=buydf): - self.assertEqual(get_buy_signal('BTC-ETH'), False) +def test_returns_latest_buy_signal(mocker): + buydf = DataFrame([{'buy': 1, 'date': arrow.utcnow()}]) + mocker.patch('freqtrade.analyze.analyze_ticker', return_value=buydf) + assert get_buy_signal('BTC-ETH') - -if __name__ == '__main__': - unittest.main() + buydf = DataFrame([{'buy': 0, 'date': arrow.utcnow()}]) + mocker.patch('freqtrade.analyze.analyze_ticker', return_value=buydf) + assert not get_buy_signal('BTC-ETH') diff --git a/freqtrade/tests/test_backtesting.py b/freqtrade/tests/test_backtesting.py index ff8f8a039..dcb572157 100644 --- a/freqtrade/tests/test_backtesting.py +++ b/freqtrade/tests/test_backtesting.py @@ -2,9 +2,8 @@ import json import logging import os -import unittest -from unittest.mock import patch +import pytest import arrow from pandas import DataFrame @@ -12,6 +11,7 @@ from freqtrade.analyze import analyze_ticker from freqtrade.main import should_sell from freqtrade.persistence import Trade +logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot def print_results(results): print('Made {} buys. Average profit {:.2f}%. Total profit was {:.3f}. Average duration {:.1f} mins.'.format( @@ -21,9 +21,14 @@ def print_results(results): results.duration.mean()*5 )) -class TestMain(unittest.TestCase): - pairs = ['btc-neo', 'btc-eth', 'btc-omg', 'btc-edg', 'btc-pay', 'btc-pivx', 'btc-qtum', 'btc-mtl', 'btc-etc', 'btc-ltc'] - conf = { +@pytest.fixture +def pairs(): + return ['btc-neo', 'btc-eth', 'btc-omg', 'btc-edg', 'btc-pay', + 'btc-pivx', 'btc-qtum', 'btc-mtl', 'btc-etc', 'btc-ltc'] + +@pytest.fixture +def conf(): + return { "minimal_roi": { "60": 0.0, "40": 0.01, @@ -33,43 +38,40 @@ class TestMain(unittest.TestCase): "stoploss": -0.40 } - @classmethod - def setUpClass(cls): - logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot - @unittest.skipIf(not os.environ.get('BACKTEST', False), "slow, should be run manually") - def test_backtest(self): - trades = [] - with patch.dict('freqtrade.main._CONF', self.conf): - for pair in self.pairs: - with open('testdata/'+pair+'.json') as data_file: - data = json.load(data_file) +@pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set") +def test_backtest(conf, pairs, mocker): + trades = [] + mocker.patch.dict('freqtrade.main._CONF', conf) + for pair in pairs: + with open('tests/testdata/'+pair+'.json') as data_file: + data = json.load(data_file) - with patch('freqtrade.analyze.get_ticker', return_value=data): - with patch('arrow.utcnow', return_value=arrow.get('2017-08-20T14:50:00')): - ticker = analyze_ticker(pair) - # for each buy point - for index, row in ticker[ticker.buy == 1].iterrows(): - trade = Trade( - open_rate=row['close'], - open_date=arrow.get(row['date']).datetime, - amount=1, - ) - # calculate win/lose forwards from buy point - for index2, row2 in ticker[index:].iterrows(): - if should_sell(trade, row2['close'], arrow.get(row2['date']).datetime): - current_profit = (row2['close'] - trade.open_rate) / trade.open_rate + mocker.patch('freqtrade.analyze.get_ticker', return_value=data) + mocker.patch('arrow.utcnow', return_value=arrow.get('2017-08-20T14:50:00')) + ticker = analyze_ticker(pair) + # for each buy point + for index, row in ticker[ticker.buy == 1].iterrows(): + trade = Trade( + open_rate=row['close'], + open_date=arrow.get(row['date']).datetime, + amount=1, + ) + # calculate win/lose forwards from buy point + for index2, row2 in ticker[index:].iterrows(): + if should_sell(trade, row2['close'], arrow.get(row2['date']).datetime): + current_profit = (row2['close'] - trade.open_rate) / trade.open_rate - trades.append((pair, current_profit, index2 - index)) - break - - labels = ['currency', 'profit', 'duration'] - results = DataFrame.from_records(trades, columns=labels) + trades.append((pair, current_profit, index2 - index)) + break + + labels = ['currency', 'profit', 'duration'] + results = DataFrame.from_records(trades, columns=labels) - print('====================== BACKTESTING REPORT ================================') + print('====================== BACKTESTING REPORT ================================') - for pair in self.pairs: - print('For currency {}:'.format(pair)) - print_results(results[results.currency == pair]) - print('TOTAL OVER ALL TRADES:') - print_results(results) + for pair in pairs: + print('For currency {}:'.format(pair)) + print_results(results[results.currency == pair]) + print('TOTAL OVER ALL TRADES:') + print_results(results) diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index fc8d40c84..eb87de1e3 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -1,7 +1,8 @@ -import unittest -from unittest.mock import patch, MagicMock, call - +# pragma pylint: disable=missing-docstring import copy +from unittest.mock import MagicMock, call + +import pytest from jsonschema import validate from freqtrade import exchange @@ -11,8 +12,9 @@ from freqtrade.misc import CONF_SCHEMA from freqtrade.persistence import Trade -class TestMain(unittest.TestCase): - conf = { +@pytest.fixture +def conf(): + configuration = { "max_open_trades": 3, "stake_currency": "BTC", "stake_amount": 0.05, @@ -42,86 +44,80 @@ class TestMain(unittest.TestCase): "chat_id": "chat_id" } } + validate(configuration, CONF_SCHEMA) + return configuration - def test_1_create_trade(self): - with patch.dict('freqtrade.main._CONF', self.conf): - with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) as buy_signal: - with patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()): - with patch.multiple('freqtrade.main.exchange', - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_order_id')): - # Save state of current whitelist - whitelist = copy.deepcopy(self.conf['bittrex']['pair_whitelist']) +def test_create_trade(conf, mocker): + mocker.patch.dict('freqtrade.main._CONF', 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', + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, + 'last': 0.07256061 + }), + buy=MagicMock(return_value='mocked_order_id')) + # Save state of current whitelist + whitelist = copy.deepcopy(conf['bittrex']['pair_whitelist']) - init(self.conf, 'sqlite://') - for pair in ['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']: - trade = create_trade(15.0, exchange.Exchange.BITTREX) - Trade.session.add(trade) - Trade.session.flush() - self.assertIsNotNone(trade) - self.assertEqual(trade.open_rate, 0.072661) - self.assertEqual(trade.pair, pair) - self.assertEqual(trade.exchange, exchange.Exchange.BITTREX) - self.assertEqual(trade.amount, 206.43811673387373) - self.assertEqual(trade.stake_amount, 15.0) - self.assertEqual(trade.is_open, True) - self.assertIsNotNone(trade.open_date) - self.assertEqual(whitelist, self.conf['bittrex']['pair_whitelist']) + init(conf, 'sqlite://') + for pair in ['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']: + trade = create_trade(15.0, exchange.Exchange.BITTREX) + Trade.session.add(trade) + Trade.session.flush() + assert trade is not None + assert trade.open_rate == 0.072661 + assert trade.pair == pair + assert trade.exchange == exchange.Exchange.BITTREX + assert trade.amount == 206.43811673387373 + assert trade.stake_amount == 15.0 + assert trade.is_open + assert trade.open_date is not None + assert whitelist == conf['bittrex']['pair_whitelist'] - buy_signal.assert_has_calls( - [call('BTC_ETH'), call('BTC_TKN'), call('BTC_TRST'), call('BTC_SWT')] - ) + buy_signal.assert_has_calls( + [call('BTC_ETH'), call('BTC_TKN'), call('BTC_TRST'), call('BTC_SWT')] + ) - def test_2_handle_trade(self): - with patch.dict('freqtrade.main._CONF', self.conf): - with patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()): - with patch.multiple('freqtrade.main.exchange', - get_ticker=MagicMock(return_value={ - 'bid': 0.17256061, - 'ask': 0.172661, - 'last': 0.17256061 - }), - buy=MagicMock(return_value='mocked_order_id')): - trade = Trade.query.filter(Trade.is_open.is_(True)).first() - self.assertTrue(trade) - handle_trade(trade) - self.assertEqual(trade.close_rate, 0.17256061) - self.assertEqual(trade.close_profit, 137.4872490056564) - self.assertIsNotNone(trade.close_date) - self.assertEqual(trade.open_order_id, 'dry_run') +def test_handle_trade(conf, mocker): + mocker.patch.dict('freqtrade.main._CONF', conf) + mocker.patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()) + mocker.patch.multiple('freqtrade.main.exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.17256061, + 'ask': 0.172661, + 'last': 0.17256061 + }), + buy=MagicMock(return_value='mocked_order_id')) + trade = Trade.query.filter(Trade.is_open.is_(True)).first() + assert trade + handle_trade(trade) + assert trade.close_rate == 0.17256061 + assert trade.close_profit == 137.4872490056564 + assert trade.close_date is not None + assert trade.open_order_id == 'dry_run' - def test_3_close_trade(self): - with patch.dict('freqtrade.main._CONF', self.conf): - trade = Trade.query.filter(Trade.is_open.is_(True)).first() - self.assertTrue(trade) +def test_close_trade(conf, mocker): + mocker.patch.dict('freqtrade.main._CONF', conf) + trade = Trade.query.filter(Trade.is_open.is_(True)).first() + assert trade - # Simulate that there is no open order - trade.open_order_id = None + # Simulate that there is no open order + trade.open_order_id = None - closed = close_trade_if_fulfilled(trade) - self.assertTrue(closed) - self.assertEqual(trade.is_open, False) + closed = close_trade_if_fulfilled(trade) + assert closed + assert not trade.is_open - def test_balance_fully_ask_side(self): - with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 0.0}}): - self.assertEqual(get_target_bid({'ask': 20, 'last': 10}), 20) +def test_balance_fully_ask_side(mocker): + mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 0.0}}) + assert get_target_bid({'ask': 20, 'last': 10}) == 20 - def test_balance_fully_last_side(self): - with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}): - self.assertEqual(get_target_bid({'ask': 20, 'last': 10}), 10) +def test_balance_fully_last_side(mocker): + mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}) + assert get_target_bid({'ask': 20, 'last': 10}) == 10 - def test_balance_when_last_bigger_than_ask(self): - with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}): - self.assertEqual(get_target_bid({'ask': 5, 'last': 10}), 5) - - @classmethod - def setUpClass(cls): - validate(cls.conf, CONF_SCHEMA) - - -if __name__ == '__main__': - unittest.main() +def test_balance_when_last_bigger_than_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_persistence.py b/freqtrade/tests/test_persistence.py index 4fd320b93..376825c72 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -1,28 +1,20 @@ -import unittest -from unittest.mock import patch - +# pragma pylint: disable=missing-docstring from freqtrade.exchange import Exchange from freqtrade.persistence import Trade - -class TestTrade(unittest.TestCase): - def test_1_exec_sell_order(self): - with patch('freqtrade.main.exchange.sell', side_effect='mocked_order_id') as api_mock: - trade = Trade( - pair='BTC_ETH', - stake_amount=1.00, - open_rate=0.50, - amount=10.00, - exchange=Exchange.BITTREX, - open_order_id='mocked' - ) - profit = trade.exec_sell_order(1.00, 10.00) - api_mock.assert_called_once_with('BTC_ETH', 1.0, 10.0) - self.assertEqual(profit, 100.0) - self.assertEqual(trade.close_rate, 1.0) - self.assertEqual(trade.close_profit, profit) - self.assertIsNotNone(trade.close_date) - - -if __name__ == '__main__': - unittest.main() +def test_exec_sell_order(mocker): + api_mock = mocker.patch('freqtrade.main.exchange.sell', side_effect='mocked_order_id') + trade = Trade( + pair='BTC_ETH', + stake_amount=1.00, + open_rate=0.50, + amount=10.00, + exchange=Exchange.BITTREX, + open_order_id='mocked' + ) + profit = trade.exec_sell_order(1.00, 10.00) + api_mock.assert_called_once_with('BTC_ETH', 1.0, 10.0) + assert profit == 100.0 + assert trade.close_rate == 1.0 + assert trade.close_profit == profit + assert trade.close_date is not None diff --git a/freqtrade/tests/test_telegram.py b/freqtrade/tests/test_telegram.py index af96a3045..cb67c8c79 100644 --- a/freqtrade/tests/test_telegram.py +++ b/freqtrade/tests/test_telegram.py @@ -1,7 +1,8 @@ -import unittest +# pragma pylint: disable=missing-docstring from datetime import datetime -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock +import pytest from jsonschema import validate from telegram import Bot, Update, Message, Chat @@ -12,13 +13,9 @@ from freqtrade.persistence import Trade from freqtrade.rpc.telegram import _status, _profit, _forcesell, _performance, _start, _stop -class MagicBot(MagicMock, Bot): - pass - - -class TestTelegram(unittest.TestCase): - - conf = { +@pytest.fixture +def conf(): + configuration = { "max_open_trades": 3, "stake_currency": "BTC", "stake_amount": 0.05, @@ -46,150 +43,151 @@ class TestTelegram(unittest.TestCase): }, "initial_state": "running" } + validate(configuration, CONF_SCHEMA) + return configuration - def test_1_status_handle(self): - with patch.dict('freqtrade.main._CONF', self.conf): - with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True): - msg_mock = MagicMock() - with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): - with patch.multiple('freqtrade.main.exchange', - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_order_id')): - init(self.conf, 'sqlite://') - - # Create some test data - trade = create_trade(15.0, exchange.Exchange.BITTREX) - self.assertTrue(trade) - Trade.session.add(trade) - Trade.session.flush() - - _status(bot=MagicBot(), update=self.update) - self.assertEqual(msg_mock.call_count, 2) - self.assertIn('[BTC_ETH]', msg_mock.call_args_list[-1][0][0]) - - def test_2_profit_handle(self): - with patch.dict('freqtrade.main._CONF', self.conf): - with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True): - msg_mock = MagicMock() - with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): - with patch.multiple('freqtrade.main.exchange', - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_order_id')): - init(self.conf, 'sqlite://') - - # Create some test data - trade = create_trade(15.0, exchange.Exchange.BITTREX) - self.assertTrue(trade) - trade.close_rate = 0.07256061 - trade.close_profit = 100.00 - trade.close_date = datetime.utcnow() - trade.open_order_id = None - trade.is_open = False - Trade.session.add(trade) - Trade.session.flush() - - _profit(bot=MagicBot(), update=self.update) - self.assertEqual(msg_mock.call_count, 2) - self.assertIn('(100.00%)', msg_mock.call_args_list[-1][0][0]) - - def test_3_forcesell_handle(self): - with patch.dict('freqtrade.main._CONF', self.conf): - with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True): - msg_mock = MagicMock() - with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): - with patch.multiple('freqtrade.main.exchange', - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_order_id')): - init(self.conf, 'sqlite://') - - # Create some test data - trade = create_trade(15.0, exchange.Exchange.BITTREX) - self.assertTrue(trade) - Trade.session.add(trade) - Trade.session.flush() - - self.update.message.text = '/forcesell 1' - _forcesell(bot=MagicBot(), update=self.update) - - self.assertEqual(msg_mock.call_count, 2) - self.assertIn('Selling [BTC/ETH]', msg_mock.call_args_list[-1][0][0]) - self.assertIn('0.072561', msg_mock.call_args_list[-1][0][0]) - - def test_4_performance_handle(self): - with patch.dict('freqtrade.main._CONF', self.conf): - with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True): - msg_mock = MagicMock() - with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): - with patch.multiple('freqtrade.main.exchange', - get_ticker=MagicMock(return_value={ - 'bid': 0.07256061, - 'ask': 0.072661, - 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_order_id')): - init(self.conf, 'sqlite://') - - # Create some test data - trade = create_trade(15.0, exchange.Exchange.BITTREX) - self.assertTrue(trade) - trade.close_rate = 0.07256061 - trade.close_profit = 100.00 - trade.close_date = datetime.utcnow() - trade.open_order_id = None - trade.is_open = False - Trade.session.add(trade) - Trade.session.flush() - - _performance(bot=MagicBot(), update=self.update) - self.assertEqual(msg_mock.call_count, 2) - self.assertIn('Performance', msg_mock.call_args_list[-1][0][0]) - self.assertIn('BTC_ETH 100.00%', msg_mock.call_args_list[-1][0][0]) - - def test_5_start_handle(self): - with patch.dict('freqtrade.main._CONF', self.conf): - msg_mock = MagicMock() - with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): - init(self.conf, 'sqlite://') - - update_state(State.STOPPED) - self.assertEqual(get_state(), State.STOPPED) - _start(bot=MagicBot(), update=self.update) - self.assertEqual(get_state(), State.RUNNING) - self.assertEqual(msg_mock.call_count, 0) - - def test_6_stop_handle(self): - with patch.dict('freqtrade.main._CONF', self.conf): - msg_mock = MagicMock() - with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): - init(self.conf, 'sqlite://') - - update_state(State.RUNNING) - self.assertEqual(get_state(), State.RUNNING) - _stop(bot=MagicBot(), update=self.update) - self.assertEqual(get_state(), State.STOPPED) - self.assertEqual(msg_mock.call_count, 1) - self.assertIn('Stopping trader', msg_mock.call_args_list[0][0][0]) - - def setUp(self): - self.update = Update(0) - self.update.message = Message(0, 0, datetime.utcnow(), Chat(0, 0)) - - @classmethod - def setUpClass(cls): - validate(cls.conf, CONF_SCHEMA) +@pytest.fixture +def update(): + _update = Update(0) + _update.message = Message(0, 0, datetime.utcnow(), Chat(0, 0)) + return _update -if __name__ == '__main__': - unittest.main() +class MagicBot(MagicMock, Bot): + pass + + +def test_status_handle(conf, update, mocker): + mocker.patch.dict('freqtrade.main._CONF', conf) + mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) + msg_mock = MagicMock() + mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) + mocker.patch.multiple('freqtrade.main.exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, + 'last': 0.07256061 + }), + buy=MagicMock(return_value='mocked_order_id')) + init(conf, 'sqlite://') + + # Create some test data + trade = create_trade(15.0, exchange.Exchange.BITTREX) + assert trade + Trade.session.add(trade) + Trade.session.flush() + + _status(bot=MagicBot(), update=update) + assert msg_mock.call_count == 2 + assert '[BTC_ETH]' in msg_mock.call_args_list[-1][0][0] + +def test_profit_handle(conf, update, mocker): + mocker.patch.dict('freqtrade.main._CONF', conf) + mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) + msg_mock = MagicMock() + mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) + mocker.patch.multiple('freqtrade.main.exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, + 'last': 0.07256061 + }), + buy=MagicMock(return_value='mocked_order_id')) + init(conf, 'sqlite://') + + # Create some test data + trade = create_trade(15.0, exchange.Exchange.BITTREX) + assert trade + trade.close_rate = 0.07256061 + trade.close_profit = 100.00 + trade.close_date = datetime.utcnow() + trade.open_order_id = None + trade.is_open = False + Trade.session.add(trade) + Trade.session.flush() + + _profit(bot=MagicBot(), update=update) + assert msg_mock.call_count == 2 + assert '(100.00%)' in msg_mock.call_args_list[-1][0][0] + +def test_forcesell_handle(conf, update, mocker): + mocker.patch.dict('freqtrade.main._CONF', conf) + mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) + msg_mock = MagicMock() + mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) + mocker.patch.multiple('freqtrade.main.exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, + 'last': 0.07256061 + }), + buy=MagicMock(return_value='mocked_order_id')) + init(conf, 'sqlite://') + + # Create some test data + trade = create_trade(15.0, exchange.Exchange.BITTREX) + assert trade + Trade.session.add(trade) + Trade.session.flush() + + update.message.text = '/forcesell 1' + _forcesell(bot=MagicBot(), update=update) + + assert msg_mock.call_count == 2 + assert 'Selling [BTC/ETH]' in msg_mock.call_args_list[-1][0][0] + assert '0.072561' in msg_mock.call_args_list[-1][0][0] + +def test_performance_handle(conf, update, mocker): + mocker.patch.dict('freqtrade.main._CONF', conf) + mocker.patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) + msg_mock = MagicMock() + mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) + mocker.patch.multiple('freqtrade.main.exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.07256061, + 'ask': 0.072661, + 'last': 0.07256061 + }), + buy=MagicMock(return_value='mocked_order_id')) + init(conf, 'sqlite://') + + # Create some test data + trade = create_trade(15.0, exchange.Exchange.BITTREX) + assert trade + trade.close_rate = 0.07256061 + trade.close_profit = 100.00 + trade.close_date = datetime.utcnow() + trade.open_order_id = None + trade.is_open = False + Trade.session.add(trade) + Trade.session.flush() + + _performance(bot=MagicBot(), update=update) + assert msg_mock.call_count == 2 + assert 'Performance' in msg_mock.call_args_list[-1][0][0] + assert 'BTC_ETH 100.00%' in msg_mock.call_args_list[-1][0][0] + +def test_start_handle(conf, update, mocker): + mocker.patch.dict('freqtrade.main._CONF', conf) + msg_mock = MagicMock() + mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) + init(conf, 'sqlite://') + + update_state(State.STOPPED) + assert get_state() == State.STOPPED + _start(bot=MagicBot(), update=update) + assert get_state() == State.RUNNING + assert msg_mock.call_count == 0 + +def test_stop_handle(conf, update, mocker): + mocker.patch.dict('freqtrade.main._CONF', conf) + msg_mock = MagicMock() + mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) + init(conf, 'sqlite://') + + update_state(State.RUNNING) + assert get_state() == State.RUNNING + _stop(bot=MagicBot(), update=update) + assert get_state() == State.STOPPED + assert msg_mock.call_count == 1 + assert 'Stopping trader' in msg_mock.call_args_list[0][0][0] diff --git a/requirements.txt b/requirements.txt index 7ceb3b850..b070fe0ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,8 @@ scipy==0.19.1 jsonschema==2.6.0 numpy==1.13.3 TA-Lib==0.4.10 +pytest==3.2.2 +pytest-mock==1.6.3 # Required for plotting data #matplotlib==2.0.2 diff --git a/setup.cfg b/setup.cfg index 6a53971b3..c5d8a376a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,5 @@ [aliases] test=pytest +[tool:pytest] +addopts = --pyargs freqtrade diff --git a/setup.py b/setup.py index 2d8d40990..2cd551cb2 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup(name='freqtrade', packages=['freqtrade'], scripts=['bin/freqtrade'], setup_requires=['pytest-runner'], - tests_require=['pytest'], + tests_require=['pytest', 'pytest-mock'], install_requires=[ 'python-bittrex==0.1.3', 'SQLAlchemy==1.1.13',