# pragma pylint: disable=missing-docstring from datetime import datetime from unittest.mock import MagicMock from functools import reduce import logging import json import arrow import pytest from jsonschema import validate from telegram import Chat, Message, Update from sqlalchemy import create_engine from freqtrade.analyze import Analyze from freqtrade.constants import Constants from freqtrade.freqtradebot import FreqtradeBot logging.getLogger('').setLevel(logging.INFO) def log_has(line, logs): # caplog mocker returns log as a tuple: ('freqtrade.analyze', logging.WARNING, 'foobar') # and we want to match line against foobar in the tuple return reduce(lambda a, b: a or b, filter(lambda x: x[2] == line, logs), False) # Functions for recurrent object patching def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: """ This function patch _init_modules() to not call dependencies :param mocker: a Mocker object to apply patches :param config: Config to pass to the bot :return: None """ mocker.patch('freqtrade.fiat_convert.Pymarketcap', {'price_usd': 12345.0}) mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock()) mocker.patch('freqtrade.freqtradebot.Analyze.get_signal', MagicMock()) return FreqtradeBot(config, create_engine('sqlite://')) @pytest.fixture(scope="module") def default_conf(): """ Returns validated configuration suitable for most tests """ configuration = { "max_open_trades": 1, "stake_currency": "BTC", "stake_amount": 0.001, "fiat_display_currency": "USD", "ticker_interval": 5, "dry_run": True, "minimal_roi": { "40": 0.0, "30": 0.01, "20": 0.02, "0": 0.04 }, "stoploss": -0.10, "unfilledtimeout": 600, "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", "BTC_BCC" ] }, "telegram": { "enabled": True, "token": "token", "chat_id": "0" }, "initial_state": "running", "loglevel": logging.DEBUG } validate(configuration, Constants.CONF_SCHEMA) return configuration @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.00001098, 'ask': 0.00001099, 'last': 0.00001098, }) @pytest.fixture def ticker_sell_up(): return MagicMock(return_value={ 'bid': 0.00001172, 'ask': 0.00001173, 'last': 0.00001172, }) @pytest.fixture def ticker_sell_down(): return MagicMock(return_value={ 'bid': 0.00001044, 'ask': 0.00001043, 'last': 0.00001044, }) @pytest.fixture def health(): return MagicMock(return_value=[{ 'Currency': 'BTC', 'IsActive': True, 'LastChecked': '2017-11-13T20:15:00.00', 'Notice': None }, { 'Currency': 'ETH', 'IsActive': True, 'LastChecked': '2017-11-13T20:15:00.00', 'Notice': None }, { 'Currency': 'TRST', 'IsActive': True, 'LastChecked': '2017-11-13T20:15:00.00', 'Notice': None }, { 'Currency': 'SWT', 'IsActive': True, 'LastChecked': '2017-11-13T20:15:00.00', 'Notice': None }, { 'Currency': 'BCC', 'IsActive': False, 'LastChecked': '2017-11-13T20:15:00.00', 'Notice': None }]) @pytest.fixture def limit_buy_order(): return { 'id': 'mocked_limit_buy', 'type': 'LIMIT_BUY', 'pair': 'mocked', 'opened': str(arrow.utcnow().datetime), 'rate': 0.00001099, 'amount': 90.99181073, 'remaining': 0.0, 'closed': str(arrow.utcnow().datetime), } @pytest.fixture def limit_buy_order_old(): return { 'id': 'mocked_limit_buy_old', 'type': 'LIMIT_BUY', 'pair': 'BTC_ETH', 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), 'rate': 0.00001099, 'amount': 90.99181073, 'remaining': 90.99181073, } @pytest.fixture def limit_sell_order_old(): return { 'id': 'mocked_limit_sell_old', 'type': 'LIMIT_SELL', 'pair': 'BTC_ETH', 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), 'rate': 0.00001099, 'amount': 90.99181073, 'remaining': 90.99181073, } @pytest.fixture def limit_buy_order_old_partial(): return { 'id': 'mocked_limit_buy_old_partial', 'type': 'LIMIT_BUY', 'pair': 'BTC_ETH', 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), 'rate': 0.00001099, 'amount': 90.99181073, 'remaining': 67.99181073, } @pytest.fixture def limit_sell_order(): return { 'id': 'mocked_limit_sell', 'type': 'LIMIT_SELL', 'pair': 'mocked', 'opened': str(arrow.utcnow().datetime), 'rate': 0.00001173, 'amount': 90.99181073, 'remaining': 0.0, 'closed': str(arrow.utcnow().datetime), } @pytest.fixture def ticker_history(): return [ { "O": 8.794e-05, "H": 8.948e-05, "L": 8.794e-05, "C": 8.88e-05, "V": 991.09056638, "T": "2017-11-26T08:50:00", "BV": 0.0877869 }, { "O": 8.88e-05, "H": 8.942e-05, "L": 8.88e-05, "C": 8.893e-05, "V": 658.77935965, "T": "2017-11-26T08:55:00", "BV": 0.05874751 }, { "O": 8.891e-05, "H": 8.893e-05, "L": 8.875e-05, "C": 8.877e-05, "V": 7920.73570705, "T": "2017-11-26T09:00:00", "BV": 0.7039405 } ] @pytest.fixture def ticker_history_without_bv(): return [ { "O": 8.794e-05, "H": 8.948e-05, "L": 8.794e-05, "C": 8.88e-05, "V": 991.09056638, "T": "2017-11-26T08:50:00" }, { "O": 8.88e-05, "H": 8.942e-05, "L": 8.88e-05, "C": 8.893e-05, "V": 658.77935965, "T": "2017-11-26T08:55:00" }, { "O": 8.891e-05, "H": 8.893e-05, "L": 8.875e-05, "C": 8.877e-05, "V": 7920.73570705, "T": "2017-11-26T09:00:00" } ] # FIX: Perhaps change result fixture to use BTC_UNITEST instead? @pytest.fixture def result(): with open('freqtrade/tests/testdata/BTC_ETH-1.json') as data_file: return Analyze.parse_ticker_dataframe(json.load(data_file)) # FIX: # Create an fixture/function # that inserts a trade of some type and open-status # return the open-order-id # See tests in rpc/main that could use this @pytest.fixture def get_market_summaries_data(): """ This fixture is a real result from exchange.get_market_summaries() but reduced to only 8 entries. 4 BTC, 4 USTD :return: JSON market summaries """ return [ { 'Ask': 1.316e-05, 'BaseVolume': 5.72599471, 'Bid': 1.3e-05, 'Created': '2014-04-14T00:00:00', 'High': 1.414e-05, 'Last': 1.298e-05, 'Low': 1.282e-05, 'MarketName': 'BTC-XWC', 'OpenBuyOrders': 2000, 'OpenSellOrders': 1484, 'PrevDay': 1.376e-05, 'TimeStamp': '2018-02-05T01:32:40.493', 'Volume': 424041.21418375 }, { 'Ask': 0.00627051, 'BaseVolume': 93.23302388, 'Bid': 0.00618192, 'Created': '2016-10-20T04:48:30.387', 'High': 0.00669897, 'Last': 0.00618192, 'Low': 0.006, 'MarketName': 'BTC-XZC', 'OpenBuyOrders': 343, 'OpenSellOrders': 2037, 'PrevDay': 0.00668229, 'TimeStamp': '2018-02-05T01:32:43.383', 'Volume': 14863.60730702 }, { 'Ask': 0.01137247, 'BaseVolume': 383.55922657, 'Bid': 0.01136006, 'Created': '2016-11-15T20:29:59.73', 'High': 0.012, 'Last': 0.01137247, 'Low': 0.01119883, 'MarketName': 'BTC-ZCL', 'OpenBuyOrders': 1332, 'OpenSellOrders': 5317, 'PrevDay': 0.01179603, 'TimeStamp': '2018-02-05T01:32:42.773', 'Volume': 33308.07358285 }, { 'Ask': 0.04155821, 'BaseVolume': 274.75369074, 'Bid': 0.04130002, 'Created': '2016-10-28T17:13:10.833', 'High': 0.04354429, 'Last': 0.041585, 'Low': 0.0413, 'MarketName': 'BTC-ZEC', 'OpenBuyOrders': 863, 'OpenSellOrders': 5579, 'PrevDay': 0.0429, 'TimeStamp': '2018-02-05T01:32:43.21', 'Volume': 6479.84033259 }, { 'Ask': 210.99999999, 'BaseVolume': 615132.70989532, 'Bid': 210.05503736, 'Created': '2017-07-21T01:08:49.397', 'High': 257.396, 'Last': 211.0, 'Low': 209.05333589, 'MarketName': 'USDT-XMR', 'OpenBuyOrders': 180, 'OpenSellOrders': 1203, 'PrevDay': 247.93528899, 'TimeStamp': '2018-02-05T01:32:43.117', 'Volume': 2688.17410793 }, { 'Ask': 0.79589979, 'BaseVolume': 9349557.01853031, 'Bid': 0.789226, 'Created': '2017-07-14T17:10:10.737', 'High': 0.977, 'Last': 0.79589979, 'Low': 0.781, 'MarketName': 'USDT-XRP', 'OpenBuyOrders': 1075, 'OpenSellOrders': 6508, 'PrevDay': 0.93300218, 'TimeStamp': '2018-02-05T01:32:42.383', 'Volume': 10801663.00788851 }, { 'Ask': 0.05154982, 'BaseVolume': 2311087.71232136, 'Bid': 0.05040107, 'Created': '2017-12-29T19:29:18.357', 'High': 0.06668561, 'Last': 0.0508, 'Low': 0.05006731, 'MarketName': 'USDT-XVG', 'OpenBuyOrders': 655, 'OpenSellOrders': 5544, 'PrevDay': 0.0627, 'TimeStamp': '2018-02-05T01:32:41.507', 'Volume': 40031424.2152716 }, { 'Ask': 332.65500022, 'BaseVolume': 562911.87455665, 'Bid': 330.00000001, 'Created': '2017-07-14T17:10:10.673', 'High': 401.59999999, 'Last': 332.65500019, 'Low': 330.0, 'MarketName': 'USDT-ZEC', 'OpenBuyOrders': 161, 'OpenSellOrders': 1731, 'PrevDay': 391.42, 'TimeStamp': '2018-02-05T01:32:42.947', 'Volume': 1571.09647946 } ]