2017-11-07 17:24:51 +00:00
|
|
|
# pragma pylint: disable=missing-docstring
|
2018-03-17 21:44:47 +00:00
|
|
|
import json
|
|
|
|
import logging
|
2017-11-07 19:12:56 +00:00
|
|
|
from datetime import datetime
|
2018-01-10 08:09:33 +00:00
|
|
|
from functools import reduce
|
2018-03-17 21:44:47 +00:00
|
|
|
from unittest.mock import MagicMock
|
|
|
|
|
2018-01-04 14:39:01 +00:00
|
|
|
import arrow
|
2018-01-10 07:51:36 +00:00
|
|
|
import pytest
|
2017-11-07 19:12:56 +00:00
|
|
|
from jsonschema import validate
|
2018-03-02 13:47:00 +00:00
|
|
|
from sqlalchemy import create_engine
|
2018-03-17 21:44:47 +00:00
|
|
|
from telegram import Chat, Message, Update
|
2018-02-04 06:42:03 +00:00
|
|
|
|
|
|
|
from freqtrade.analyze import Analyze
|
|
|
|
from freqtrade.constants import Constants
|
2018-02-12 08:37:19 +00:00
|
|
|
from freqtrade.freqtradebot import FreqtradeBot
|
2017-11-07 19:12:56 +00:00
|
|
|
|
2018-02-04 06:42:03 +00:00
|
|
|
logging.getLogger('').setLevel(logging.INFO)
|
2017-11-07 15:18:29 +00:00
|
|
|
|
|
|
|
|
2018-01-10 08:09:33 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2018-02-12 08:37:19 +00:00
|
|
|
# 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
|
|
|
|
"""
|
2018-03-17 23:42:24 +00:00
|
|
|
mocker.patch('freqtrade.fiat_convert.Market', {'price_usd': 12345.0})
|
2018-02-12 08:37:19 +00:00
|
|
|
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())
|
|
|
|
|
2018-03-02 13:47:00 +00:00
|
|
|
return FreqtradeBot(config, create_engine('sqlite://'))
|
2018-02-12 08:37:19 +00:00
|
|
|
|
|
|
|
|
2017-11-07 15:18:29 +00:00
|
|
|
@pytest.fixture(scope="module")
|
2017-11-07 19:12:56 +00:00
|
|
|
def default_conf():
|
|
|
|
""" Returns validated configuration suitable for most tests """
|
|
|
|
configuration = {
|
2017-11-07 21:27:44 +00:00
|
|
|
"max_open_trades": 1,
|
2017-11-07 19:12:56 +00:00
|
|
|
"stake_currency": "BTC",
|
2017-12-17 21:07:56 +00:00
|
|
|
"stake_amount": 0.001,
|
2017-12-25 07:51:41 +00:00
|
|
|
"fiat_display_currency": "USD",
|
2018-01-22 08:39:26 +00:00
|
|
|
"ticker_interval": 5,
|
2017-11-07 19:12:56 +00:00
|
|
|
"dry_run": True,
|
|
|
|
"minimal_roi": {
|
2018-01-10 09:29:41 +00:00
|
|
|
"40": 0.0,
|
|
|
|
"30": 0.01,
|
|
|
|
"20": 0.02,
|
|
|
|
"0": 0.04
|
2017-11-07 19:12:56 +00:00
|
|
|
},
|
2017-11-23 16:43:19 +00:00
|
|
|
"stoploss": -0.10,
|
2018-01-03 23:35:57 +00:00
|
|
|
"unfilledtimeout": 600,
|
2017-11-07 19:12:56 +00:00
|
|
|
"bid_strategy": {
|
|
|
|
"ask_last_balance": 0.0
|
|
|
|
},
|
|
|
|
"exchange": {
|
|
|
|
"name": "bittrex",
|
|
|
|
"enabled": True,
|
|
|
|
"key": "key",
|
|
|
|
"secret": "secret",
|
|
|
|
"pair_whitelist": [
|
2018-03-24 19:02:13 +00:00
|
|
|
"ETH/BTC",
|
|
|
|
"TKN/BTC",
|
|
|
|
"TRST/BTC",
|
|
|
|
"SWT/BTC",
|
|
|
|
"BCC/BTC"
|
2017-11-07 19:12:56 +00:00
|
|
|
]
|
|
|
|
},
|
|
|
|
"telegram": {
|
|
|
|
"enabled": True,
|
|
|
|
"token": "token",
|
|
|
|
"chat_id": "0"
|
|
|
|
},
|
2018-03-02 13:47:00 +00:00
|
|
|
"initial_state": "running",
|
|
|
|
"loglevel": logging.DEBUG
|
2017-11-07 19:12:56 +00:00
|
|
|
}
|
2018-02-04 06:42:03 +00:00
|
|
|
validate(configuration, Constants.CONF_SCHEMA)
|
2017-11-07 19:12:56 +00:00
|
|
|
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={
|
2017-12-17 21:07:56 +00:00
|
|
|
'bid': 0.00001098,
|
|
|
|
'ask': 0.00001099,
|
|
|
|
'last': 0.00001098,
|
2017-11-07 19:12:56 +00:00
|
|
|
})
|
|
|
|
|
2017-12-19 05:58:02 +00:00
|
|
|
|
2017-12-17 21:07:56 +00:00
|
|
|
@pytest.fixture
|
|
|
|
def ticker_sell_up():
|
|
|
|
return MagicMock(return_value={
|
|
|
|
'bid': 0.00001172,
|
|
|
|
'ask': 0.00001173,
|
|
|
|
'last': 0.00001172,
|
|
|
|
})
|
|
|
|
|
2017-12-19 05:58:02 +00:00
|
|
|
|
2017-12-17 21:07:56 +00:00
|
|
|
@pytest.fixture
|
|
|
|
def ticker_sell_down():
|
|
|
|
return MagicMock(return_value={
|
|
|
|
'bid': 0.00001044,
|
|
|
|
'ask': 0.00001043,
|
|
|
|
'last': 0.00001044,
|
|
|
|
})
|
2017-11-07 19:12:56 +00:00
|
|
|
|
|
|
|
|
2017-11-13 20:34:47 +00:00
|
|
|
@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
|
|
|
|
}])
|
|
|
|
|
|
|
|
|
2017-11-07 19:12:56 +00:00
|
|
|
@pytest.fixture
|
|
|
|
def limit_buy_order():
|
|
|
|
return {
|
|
|
|
'id': 'mocked_limit_buy',
|
|
|
|
'type': 'LIMIT_BUY',
|
|
|
|
'pair': 'mocked',
|
2018-01-05 04:12:13 +00:00
|
|
|
'opened': str(arrow.utcnow().datetime),
|
2017-12-17 21:07:56 +00:00
|
|
|
'rate': 0.00001099,
|
|
|
|
'amount': 90.99181073,
|
2017-11-07 19:12:56 +00:00
|
|
|
'remaining': 0.0,
|
2018-01-05 04:12:13 +00:00
|
|
|
'closed': str(arrow.utcnow().datetime),
|
2018-01-04 14:39:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def limit_buy_order_old():
|
|
|
|
return {
|
|
|
|
'id': 'mocked_limit_buy_old',
|
|
|
|
'type': 'LIMIT_BUY',
|
2018-03-24 19:02:13 +00:00
|
|
|
'pair': 'ETH/BTC',
|
2018-01-05 04:12:13 +00:00
|
|
|
'opened': str(arrow.utcnow().shift(minutes=-601).datetime),
|
2018-01-04 14:39:01 +00:00
|
|
|
'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',
|
2018-03-24 19:02:13 +00:00
|
|
|
'pair': 'ETH/BTC',
|
2018-01-05 04:12:13 +00:00
|
|
|
'opened': str(arrow.utcnow().shift(minutes=-601).datetime),
|
2018-01-04 14:39:01 +00:00
|
|
|
'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',
|
2018-03-24 19:02:13 +00:00
|
|
|
'pair': 'ETH/BTC',
|
2018-01-05 04:12:13 +00:00
|
|
|
'opened': str(arrow.utcnow().shift(minutes=-601).datetime),
|
2018-01-04 14:39:01 +00:00
|
|
|
'rate': 0.00001099,
|
|
|
|
'amount': 90.99181073,
|
|
|
|
'remaining': 67.99181073,
|
2017-11-07 19:12:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def limit_sell_order():
|
|
|
|
return {
|
|
|
|
'id': 'mocked_limit_sell',
|
|
|
|
'type': 'LIMIT_SELL',
|
|
|
|
'pair': 'mocked',
|
2018-01-05 04:12:13 +00:00
|
|
|
'opened': str(arrow.utcnow().datetime),
|
2017-12-17 21:07:56 +00:00
|
|
|
'rate': 0.00001173,
|
|
|
|
'amount': 90.99181073,
|
2017-11-07 19:12:56 +00:00
|
|
|
'remaining': 0.0,
|
2018-01-05 04:12:13 +00:00
|
|
|
'closed': str(arrow.utcnow().datetime),
|
2017-11-07 19:12:56 +00:00
|
|
|
}
|
2017-12-16 23:24:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def ticker_history():
|
|
|
|
return [
|
2017-12-18 16:36:00 +00:00
|
|
|
{
|
2017-12-16 23:24:21 +00:00
|
|
|
"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",
|
2018-01-21 13:25:15 +00:00
|
|
|
"BV": 0.0877869
|
2017-12-16 23:24:21 +00:00
|
|
|
},
|
2017-12-18 16:36:00 +00:00
|
|
|
{
|
2017-12-16 23:24:21 +00: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",
|
2018-01-21 13:25:15 +00:00
|
|
|
"BV": 0.05874751
|
2017-12-16 23:24:21 +00:00
|
|
|
},
|
2017-12-18 16:36:00 +00:00
|
|
|
{
|
2017-12-16 23:24:21 +00: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",
|
2018-01-21 13:25:15 +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"
|
2017-12-16 23:24:21 +00:00
|
|
|
}
|
2017-12-17 21:07:56 +00:00
|
|
|
]
|
2018-01-28 07:38:41 +00:00
|
|
|
|
|
|
|
|
2018-02-08 19:49:43 +00:00
|
|
|
# FIX: Perhaps change result fixture to use BTC_UNITEST instead?
|
2018-01-28 07:38:41 +00:00
|
|
|
@pytest.fixture
|
|
|
|
def result():
|
|
|
|
with open('freqtrade/tests/testdata/BTC_ETH-1.json') as data_file:
|
2018-02-04 06:42:03 +00:00
|
|
|
return Analyze.parse_ticker_dataframe(json.load(data_file))
|
2018-01-28 07:38:41 +00:00
|
|
|
|
|
|
|
|
2018-02-01 06:05:23 +00:00
|
|
|
# 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
|
2018-02-04 09:21:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
@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
|
|
|
|
}
|
|
|
|
]
|