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
|
2019-02-03 12:50:03 +00:00
|
|
|
|
import re
|
2019-05-20 17:35:19 +00:00
|
|
|
|
from copy import deepcopy
|
2017-11-07 19:12:56 +00:00
|
|
|
|
from datetime import datetime
|
2018-01-10 08:09:33 +00:00
|
|
|
|
from functools import reduce
|
2019-06-17 12:34:45 +00:00
|
|
|
|
from pathlib import Path
|
2018-09-10 18:19:28 +00:00
|
|
|
|
from unittest.mock import MagicMock, PropertyMock
|
2018-03-17 21:44:47 +00:00
|
|
|
|
|
2018-01-04 14:39:01 +00:00
|
|
|
|
import arrow
|
2019-07-26 13:30:14 +00:00
|
|
|
|
import numpy as np
|
2019-10-17 17:53:01 +00:00
|
|
|
|
import pytest
|
2018-03-17 21:44:47 +00:00
|
|
|
|
from telegram import Chat, Message, Update
|
2018-02-04 06:42:03 +00:00
|
|
|
|
|
2019-05-11 07:42:30 +00:00
|
|
|
|
from freqtrade import constants, persistence
|
2019-07-11 18:23:23 +00:00
|
|
|
|
from freqtrade.configuration import Arguments
|
2018-12-12 19:16:03 +00:00
|
|
|
|
from freqtrade.data.converter import parse_ticker_dataframe
|
2018-11-25 13:36:02 +00:00
|
|
|
|
from freqtrade.edge import Edge, PairInfo
|
2019-03-30 12:48:30 +00:00
|
|
|
|
from freqtrade.exchange import Exchange
|
2018-02-12 08:37:19 +00:00
|
|
|
|
from freqtrade.freqtradebot import FreqtradeBot
|
2019-10-17 17:53:01 +00:00
|
|
|
|
from freqtrade.persistence import Trade
|
2019-02-19 18:15:22 +00:00
|
|
|
|
from freqtrade.resolvers import ExchangeResolver
|
2019-04-02 21:50:33 +00:00
|
|
|
|
from freqtrade.worker import Worker
|
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
|
|
|
|
|
|
|
|
|
|
2019-07-26 13:30:14 +00:00
|
|
|
|
# Do not mask numpy errors as warnings that no one read, raise the exсeption
|
|
|
|
|
np.seterr(all='raise')
|
|
|
|
|
|
|
|
|
|
|
2018-01-10 08:09:33 +00:00
|
|
|
|
def log_has(line, logs):
|
2018-07-17 09:12:25 +00:00
|
|
|
|
# caplog mocker returns log as a tuple: ('freqtrade.something', logging.WARNING, 'foobar')
|
2018-01-10 08:09:33 +00:00
|
|
|
|
# and we want to match line against foobar in the tuple
|
|
|
|
|
return reduce(lambda a, b: a or b,
|
2019-08-11 18:14:58 +00:00
|
|
|
|
filter(lambda x: x[2] == line, logs.record_tuples),
|
2018-01-10 08:09:33 +00:00
|
|
|
|
False)
|
|
|
|
|
|
|
|
|
|
|
2019-02-03 12:50:03 +00:00
|
|
|
|
def log_has_re(line, logs):
|
|
|
|
|
return reduce(lambda a, b: a or b,
|
2019-08-11 18:14:58 +00:00
|
|
|
|
filter(lambda x: re.match(line, x[2]), logs.record_tuples),
|
2019-02-03 12:50:03 +00:00
|
|
|
|
False)
|
|
|
|
|
|
|
|
|
|
|
2019-07-11 18:23:23 +00:00
|
|
|
|
def get_args(args):
|
2019-09-04 14:38:33 +00:00
|
|
|
|
return Arguments(args).get_parsed_arg()
|
2019-06-16 08:13:12 +00:00
|
|
|
|
|
|
|
|
|
|
2019-07-11 21:39:42 +00:00
|
|
|
|
def patched_configuration_load_config_file(mocker, config) -> None:
|
|
|
|
|
mocker.patch(
|
2019-08-10 15:47:58 +00:00
|
|
|
|
'freqtrade.configuration.configuration.load_config_file',
|
2019-07-11 21:39:42 +00:00
|
|
|
|
lambda *args, **kwargs: config
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2019-10-26 08:08:23 +00:00
|
|
|
|
def patch_exchange(mocker, api_mock=None, id='bittrex', mock_markets=True) -> None:
|
2018-09-11 17:46:18 +00:00
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
2019-03-05 18:45:54 +00:00
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
2018-07-07 12:41:36 +00:00
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
2018-11-17 18:54:55 +00:00
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock())
|
2018-11-22 18:25:26 +00:00
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title()))
|
2019-10-26 08:08:23 +00:00
|
|
|
|
if mock_markets:
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=get_markets()))
|
2018-11-22 18:25:26 +00:00
|
|
|
|
|
2018-06-17 11:32:05 +00:00
|
|
|
|
if api_mock:
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
else:
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock())
|
2018-06-17 19:46:56 +00:00
|
|
|
|
|
|
|
|
|
|
2019-10-26 08:08:23 +00:00
|
|
|
|
def get_patched_exchange(mocker, config, api_mock=None, id='bittrex', mock_markets=True) -> Exchange:
|
|
|
|
|
patch_exchange(mocker, api_mock, id, mock_markets)
|
2019-02-19 18:15:22 +00:00
|
|
|
|
config["exchange"]["name"] = id
|
|
|
|
|
try:
|
2019-06-22 14:52:14 +00:00
|
|
|
|
exchange = ExchangeResolver(id, config).exchange
|
2019-02-19 18:15:22 +00:00
|
|
|
|
except ImportError:
|
|
|
|
|
exchange = Exchange(config)
|
2018-06-17 11:32:05 +00:00
|
|
|
|
return exchange
|
|
|
|
|
|
|
|
|
|
|
2018-11-24 15:37:28 +00:00
|
|
|
|
def patch_wallet(mocker, free=999.9) -> None:
|
|
|
|
|
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(
|
|
|
|
|
return_value=free
|
2018-11-23 09:17:10 +00:00
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
2019-10-26 08:08:23 +00:00
|
|
|
|
def patch_whitelist(mocker, conf) -> None:
|
|
|
|
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot._refresh_whitelist',
|
|
|
|
|
MagicMock(return_value=conf['exchange']['pair_whitelist']))
|
|
|
|
|
|
|
|
|
|
|
2018-10-04 16:05:46 +00:00
|
|
|
|
def patch_edge(mocker) -> None:
|
|
|
|
|
# "ETH/BTC",
|
|
|
|
|
# "LTC/BTC",
|
|
|
|
|
# "XRP/BTC",
|
|
|
|
|
# "NEO/BTC"
|
2018-11-25 13:36:02 +00:00
|
|
|
|
|
2018-10-04 16:05:46 +00:00
|
|
|
|
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
|
2018-11-04 17:43:57 +00:00
|
|
|
|
return_value={
|
2018-11-25 13:36:02 +00:00
|
|
|
|
'NEO/BTC': PairInfo(-0.20, 0.66, 3.71, 0.50, 1.71, 10, 25),
|
|
|
|
|
'LTC/BTC': PairInfo(-0.21, 0.66, 3.71, 0.50, 1.71, 11, 20),
|
2018-11-04 17:43:57 +00:00
|
|
|
|
}
|
2018-10-04 16:07:47 +00:00
|
|
|
|
))
|
2018-11-04 17:57:57 +00:00
|
|
|
|
mocker.patch('freqtrade.edge.Edge.calculate', MagicMock(return_value=True))
|
2018-10-04 16:05:46 +00:00
|
|
|
|
|
2018-10-04 16:07:47 +00:00
|
|
|
|
|
2018-10-04 16:05:46 +00:00
|
|
|
|
def get_patched_edge(mocker, config) -> Edge:
|
|
|
|
|
patch_edge(mocker)
|
2018-11-04 17:57:57 +00:00
|
|
|
|
edge = Edge(config)
|
2018-10-04 16:05:46 +00:00
|
|
|
|
return edge
|
|
|
|
|
|
2018-02-12 08:37:19 +00:00
|
|
|
|
# Functions for recurrent object patching
|
2018-10-04 16:07:47 +00:00
|
|
|
|
|
|
|
|
|
|
2019-03-26 08:07:24 +00:00
|
|
|
|
def patch_freqtradebot(mocker, config) -> None:
|
2018-02-12 08:37:19 +00:00
|
|
|
|
"""
|
|
|
|
|
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.freqtradebot.RPCManager', MagicMock())
|
2019-06-02 13:28:29 +00:00
|
|
|
|
persistence.init(config['db_url'])
|
2019-09-11 06:52:09 +00:00
|
|
|
|
patch_exchange(mocker)
|
2018-02-12 08:37:19 +00:00
|
|
|
|
mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
|
2019-10-26 08:08:23 +00:00
|
|
|
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot._refresh_whitelist',
|
|
|
|
|
MagicMock(return_value=config['exchange']['pair_whitelist']))
|
2018-02-12 08:37:19 +00:00
|
|
|
|
|
2019-03-26 08:07:24 +00:00
|
|
|
|
|
|
|
|
|
def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
2019-06-20 18:30:05 +00:00
|
|
|
|
"""
|
|
|
|
|
This function patches _init_modules() to not call dependencies
|
|
|
|
|
:param mocker: a Mocker object to apply patches
|
|
|
|
|
:param config: Config to pass to the bot
|
|
|
|
|
:return: FreqtradeBot
|
|
|
|
|
"""
|
2019-03-26 08:07:24 +00:00
|
|
|
|
patch_freqtradebot(mocker, config)
|
2018-06-07 03:27:55 +00:00
|
|
|
|
return FreqtradeBot(config)
|
2018-02-12 08:37:19 +00:00
|
|
|
|
|
|
|
|
|
|
2019-03-26 08:07:24 +00:00
|
|
|
|
def get_patched_worker(mocker, config) -> Worker:
|
2019-06-20 18:30:05 +00:00
|
|
|
|
"""
|
|
|
|
|
This function patches _init_modules() to not call dependencies
|
|
|
|
|
:param mocker: a Mocker object to apply patches
|
|
|
|
|
:param config: Config to pass to the bot
|
|
|
|
|
:return: Worker
|
|
|
|
|
"""
|
2019-03-26 08:07:24 +00:00
|
|
|
|
patch_freqtradebot(mocker, config)
|
|
|
|
|
return Worker(args=None, config=config)
|
|
|
|
|
|
|
|
|
|
|
2019-05-11 07:42:30 +00:00
|
|
|
|
def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None:
|
|
|
|
|
"""
|
|
|
|
|
:param mocker: mocker to patch IStrategy class
|
|
|
|
|
:param value: which value IStrategy.get_signal() must return
|
|
|
|
|
:return: None
|
|
|
|
|
"""
|
|
|
|
|
freqtrade.strategy.get_signal = lambda e, s, t: value
|
|
|
|
|
freqtrade.exchange.refresh_latest_ohlcv = lambda p: None
|
|
|
|
|
|
2019-05-11 12:05:25 +00:00
|
|
|
|
|
2019-03-30 12:47:09 +00:00
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
|
def patch_coinmarketcap(mocker) -> None:
|
2018-05-13 18:04:40 +00:00
|
|
|
|
"""
|
|
|
|
|
Mocker to coinmarketcap to speed up tests
|
|
|
|
|
:param mocker: mocker to patch coinmarketcap class
|
|
|
|
|
:return: None
|
|
|
|
|
"""
|
|
|
|
|
|
2018-11-04 17:57:57 +00:00
|
|
|
|
tickermock = MagicMock(return_value={'price_usd': 12345.0})
|
|
|
|
|
listmock = MagicMock(return_value={'data': [{'id': 1, 'name': 'Bitcoin', 'symbol': 'BTC',
|
2018-05-13 18:46:02 +00:00
|
|
|
|
'website_slug': 'bitcoin'},
|
|
|
|
|
{'id': 1027, 'name': 'Ethereum', 'symbol': 'ETH',
|
|
|
|
|
'website_slug': 'ethereum'}
|
|
|
|
|
]})
|
|
|
|
|
mocker.patch.multiple(
|
2018-12-11 19:27:54 +00:00
|
|
|
|
'freqtrade.rpc.fiat_convert.Market',
|
2018-05-13 18:46:02 +00:00
|
|
|
|
ticker=tickermock,
|
|
|
|
|
listings=listmock,
|
2018-05-13 18:04:40 +00:00
|
|
|
|
|
2018-05-13 18:46:02 +00:00
|
|
|
|
)
|
2018-05-13 18:04:40 +00:00
|
|
|
|
|
|
|
|
|
|
2019-06-16 10:54:27 +00:00
|
|
|
|
@pytest.fixture(scope='function')
|
|
|
|
|
def init_persistence(default_conf):
|
|
|
|
|
persistence.init(default_conf['db_url'], default_conf['dry_run'])
|
|
|
|
|
|
|
|
|
|
|
2018-04-07 18:06:53 +00:00
|
|
|
|
@pytest.fixture(scope="function")
|
2019-09-07 19:06:20 +00:00
|
|
|
|
def default_conf(testdatadir):
|
2017-11-07 19:12:56 +00:00
|
|
|
|
""" Returns validated configuration suitable for most tests """
|
2018-11-04 17:57:57 +00:00
|
|
|
|
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-03-25 20:03:26 +00:00
|
|
|
|
"ticker_interval": '5m',
|
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-06-14 01:32:52 +00:00
|
|
|
|
"unfilledtimeout": {
|
|
|
|
|
"buy": 10,
|
|
|
|
|
"sell": 30
|
|
|
|
|
},
|
2017-11-07 19:12:56 +00:00
|
|
|
|
"bid_strategy": {
|
2018-08-29 09:38:43 +00:00
|
|
|
|
"ask_last_balance": 0.0,
|
|
|
|
|
"use_order_book": False,
|
|
|
|
|
"order_book_top": 1,
|
|
|
|
|
"check_depth_of_market": {
|
|
|
|
|
"enabled": False,
|
|
|
|
|
"bids_to_ask_delta": 1
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"ask_strategy": {
|
|
|
|
|
"use_order_book": False,
|
|
|
|
|
"order_book_min": 1,
|
|
|
|
|
"order_book_max": 1
|
2017-11-07 19:12:56 +00:00
|
|
|
|
},
|
|
|
|
|
"exchange": {
|
|
|
|
|
"name": "bittrex",
|
|
|
|
|
"enabled": True,
|
|
|
|
|
"key": "key",
|
|
|
|
|
"secret": "secret",
|
|
|
|
|
"pair_whitelist": [
|
2018-03-24 19:02:13 +00:00
|
|
|
|
"ETH/BTC",
|
2018-04-22 08:29:21 +00:00
|
|
|
|
"LTC/BTC",
|
|
|
|
|
"XRP/BTC",
|
|
|
|
|
"NEO/BTC"
|
2019-03-24 15:09:04 +00:00
|
|
|
|
],
|
|
|
|
|
"pair_blacklist": [
|
|
|
|
|
"DOGE/BTC",
|
|
|
|
|
"HOT/BTC",
|
2017-11-07 19:12:56 +00:00
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"telegram": {
|
|
|
|
|
"enabled": True,
|
|
|
|
|
"token": "token",
|
|
|
|
|
"chat_id": "0"
|
|
|
|
|
},
|
2019-09-07 19:06:20 +00:00
|
|
|
|
"datadir": str(testdatadir),
|
2018-03-02 13:47:00 +00:00
|
|
|
|
"initial_state": "running",
|
2018-06-07 03:27:55 +00:00
|
|
|
|
"db_url": "sqlite://",
|
2019-07-21 14:06:44 +00:00
|
|
|
|
"user_data_dir": Path("user_data"),
|
2019-07-06 23:53:13 +00:00
|
|
|
|
"verbosity": 3,
|
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
|
|
|
|
|
|
|
|
|
|
|
2018-04-15 10:09:12 +00:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def fee():
|
|
|
|
|
return MagicMock(return_value=0.0025)
|
|
|
|
|
|
|
|
|
|
|
2017-11-07 19:12:56 +00:00
|
|
|
|
@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
|
2018-03-25 19:51:41 +00:00
|
|
|
|
def markets():
|
2019-10-26 08:08:23 +00:00
|
|
|
|
return get_markets()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_markets():
|
2019-03-05 18:45:54 +00:00
|
|
|
|
return {
|
|
|
|
|
'ETH/BTC': {
|
2018-03-25 19:51:41 +00:00
|
|
|
|
'id': 'ethbtc',
|
|
|
|
|
'symbol': 'ETH/BTC',
|
2018-04-07 15:05:44 +00:00
|
|
|
|
'base': 'ETH',
|
2018-03-25 19:51:41 +00:00
|
|
|
|
'quote': 'BTC',
|
2018-04-07 15:05:44 +00:00
|
|
|
|
'active': True,
|
2018-03-25 19:51:41 +00:00
|
|
|
|
'precision': {
|
|
|
|
|
'price': 8,
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'cost': 8,
|
|
|
|
|
},
|
|
|
|
|
'lot': 0.00000001,
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.01,
|
|
|
|
|
'max': 1000,
|
|
|
|
|
},
|
|
|
|
|
'price': 500000,
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'cost': {
|
|
|
|
|
'min': 1,
|
|
|
|
|
'max': 500000,
|
|
|
|
|
},
|
2018-03-25 19:51:41 +00:00
|
|
|
|
},
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2018-04-07 15:05:44 +00:00
|
|
|
|
},
|
2019-03-05 18:45:54 +00:00
|
|
|
|
'TKN/BTC': {
|
2018-03-25 19:51:41 +00:00
|
|
|
|
'id': 'tknbtc',
|
|
|
|
|
'symbol': 'TKN/BTC',
|
|
|
|
|
'base': 'TKN',
|
|
|
|
|
'quote': 'BTC',
|
2019-10-17 16:05:50 +00:00
|
|
|
|
# According to ccxt, markets without active item set are also active
|
|
|
|
|
# 'active': True,
|
2018-03-25 19:51:41 +00:00
|
|
|
|
'precision': {
|
|
|
|
|
'price': 8,
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'cost': 8,
|
|
|
|
|
},
|
|
|
|
|
'lot': 0.00000001,
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.01,
|
|
|
|
|
'max': 1000,
|
|
|
|
|
},
|
|
|
|
|
'price': 500000,
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'cost': {
|
|
|
|
|
'min': 1,
|
|
|
|
|
'max': 500000,
|
|
|
|
|
},
|
2018-03-25 19:51:41 +00:00
|
|
|
|
},
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2018-04-07 15:05:44 +00:00
|
|
|
|
},
|
2019-03-05 18:45:54 +00:00
|
|
|
|
'BLK/BTC': {
|
2018-03-25 19:51:41 +00:00
|
|
|
|
'id': 'blkbtc',
|
|
|
|
|
'symbol': 'BLK/BTC',
|
|
|
|
|
'base': 'BLK',
|
|
|
|
|
'quote': 'BTC',
|
2018-04-07 15:05:44 +00:00
|
|
|
|
'active': True,
|
2018-03-25 19:51:41 +00:00
|
|
|
|
'precision': {
|
|
|
|
|
'price': 8,
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'cost': 8,
|
|
|
|
|
},
|
|
|
|
|
'lot': 0.00000001,
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.01,
|
|
|
|
|
'max': 1000,
|
|
|
|
|
},
|
|
|
|
|
'price': 500000,
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'cost': {
|
|
|
|
|
'min': 1,
|
|
|
|
|
'max': 500000,
|
|
|
|
|
},
|
|
|
|
|
},
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2018-06-16 23:23:12 +00:00
|
|
|
|
},
|
2019-03-05 18:45:54 +00:00
|
|
|
|
'LTC/BTC': {
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'id': 'ltcbtc',
|
|
|
|
|
'symbol': 'LTC/BTC',
|
|
|
|
|
'base': 'LTC',
|
|
|
|
|
'quote': 'BTC',
|
2019-10-26 08:08:23 +00:00
|
|
|
|
'active': True,
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'precision': {
|
|
|
|
|
'price': 8,
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'cost': 8,
|
|
|
|
|
},
|
|
|
|
|
'lot': 0.00000001,
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.01,
|
|
|
|
|
'max': 1000,
|
|
|
|
|
},
|
|
|
|
|
'price': 500000,
|
|
|
|
|
'cost': {
|
|
|
|
|
'min': 1,
|
|
|
|
|
'max': 500000,
|
|
|
|
|
},
|
|
|
|
|
},
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2018-06-16 23:23:12 +00:00
|
|
|
|
},
|
2019-03-05 18:45:54 +00:00
|
|
|
|
'XRP/BTC': {
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'id': 'xrpbtc',
|
|
|
|
|
'symbol': 'XRP/BTC',
|
|
|
|
|
'base': 'XRP',
|
|
|
|
|
'quote': 'BTC',
|
2019-10-26 08:08:23 +00:00
|
|
|
|
'active': True,
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'precision': {
|
|
|
|
|
'price': 8,
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'cost': 8,
|
|
|
|
|
},
|
|
|
|
|
'lot': 0.00000001,
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.01,
|
|
|
|
|
'max': 1000,
|
|
|
|
|
},
|
|
|
|
|
'price': 500000,
|
|
|
|
|
'cost': {
|
|
|
|
|
'min': 1,
|
|
|
|
|
'max': 500000,
|
|
|
|
|
},
|
|
|
|
|
},
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2018-06-16 23:23:12 +00:00
|
|
|
|
},
|
2019-03-05 18:45:54 +00:00
|
|
|
|
'NEO/BTC': {
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'id': 'neobtc',
|
|
|
|
|
'symbol': 'NEO/BTC',
|
|
|
|
|
'base': 'NEO',
|
|
|
|
|
'quote': 'BTC',
|
2019-10-26 08:08:23 +00:00
|
|
|
|
'active': True,
|
2018-06-16 23:23:12 +00:00
|
|
|
|
'precision': {
|
|
|
|
|
'price': 8,
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'cost': 8,
|
|
|
|
|
},
|
|
|
|
|
'lot': 0.00000001,
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.01,
|
|
|
|
|
'max': 1000,
|
|
|
|
|
},
|
|
|
|
|
'price': 500000,
|
|
|
|
|
'cost': {
|
|
|
|
|
'min': 1,
|
|
|
|
|
'max': 500000,
|
|
|
|
|
},
|
2018-03-25 19:51:41 +00:00
|
|
|
|
},
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2019-03-02 17:53:42 +00:00
|
|
|
|
},
|
2019-03-05 18:45:54 +00:00
|
|
|
|
'BTT/BTC': {
|
2019-03-02 17:53:42 +00:00
|
|
|
|
'id': 'BTTBTC',
|
|
|
|
|
'symbol': 'BTT/BTC',
|
|
|
|
|
'base': 'BTT',
|
|
|
|
|
'quote': 'BTC',
|
2019-10-26 08:08:23 +00:00
|
|
|
|
'active': False,
|
2019-03-02 17:53:42 +00:00
|
|
|
|
'precision': {
|
|
|
|
|
'base': 8,
|
|
|
|
|
'quote': 8,
|
|
|
|
|
'amount': 0,
|
|
|
|
|
'price': 8
|
|
|
|
|
},
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 1.0,
|
|
|
|
|
'max': 90000000.0
|
|
|
|
|
},
|
|
|
|
|
'price': {
|
|
|
|
|
'min': None,
|
|
|
|
|
'max': None
|
|
|
|
|
},
|
|
|
|
|
'cost': {
|
|
|
|
|
'min': 0.001,
|
|
|
|
|
'max': None
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2019-03-02 17:53:42 +00:00
|
|
|
|
},
|
2019-03-05 18:45:54 +00:00
|
|
|
|
'ETH/USDT': {
|
2019-03-02 17:53:42 +00:00
|
|
|
|
'id': 'USDT-ETH',
|
|
|
|
|
'symbol': 'ETH/USDT',
|
|
|
|
|
'base': 'ETH',
|
|
|
|
|
'quote': 'USDT',
|
|
|
|
|
'precision': {
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'price': 8
|
|
|
|
|
},
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.02214286,
|
|
|
|
|
'max': None
|
|
|
|
|
},
|
|
|
|
|
'price': {
|
|
|
|
|
'min': 1e-08,
|
|
|
|
|
'max': None
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
'active': True,
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2019-03-02 17:53:42 +00:00
|
|
|
|
},
|
2019-03-05 18:45:54 +00:00
|
|
|
|
'LTC/USDT': {
|
2019-03-02 17:53:42 +00:00
|
|
|
|
'id': 'USDT-LTC',
|
|
|
|
|
'symbol': 'LTC/USDT',
|
|
|
|
|
'base': 'LTC',
|
|
|
|
|
'quote': 'USDT',
|
2019-10-26 08:08:23 +00:00
|
|
|
|
'active': False,
|
2019-03-02 17:53:42 +00:00
|
|
|
|
'precision': {
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'price': 8
|
|
|
|
|
},
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.06646786,
|
|
|
|
|
'max': None
|
|
|
|
|
},
|
|
|
|
|
'price': {
|
|
|
|
|
'min': 1e-08,
|
|
|
|
|
'max': None
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-08-03 11:11:51 +00:00
|
|
|
|
'info': {},
|
2019-10-17 19:45:20 +00:00
|
|
|
|
},
|
|
|
|
|
'LTC/USD': {
|
|
|
|
|
'id': 'USD-LTC',
|
|
|
|
|
'symbol': 'LTC/USD',
|
|
|
|
|
'base': 'LTC',
|
|
|
|
|
'quote': 'USD',
|
|
|
|
|
'active': True,
|
|
|
|
|
'precision': {
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'price': 8
|
|
|
|
|
},
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.06646786,
|
|
|
|
|
'max': None
|
|
|
|
|
},
|
|
|
|
|
'price': {
|
|
|
|
|
'min': 1e-08,
|
|
|
|
|
'max': None
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
'info': {},
|
|
|
|
|
},
|
|
|
|
|
'XLTCUSDT': {
|
|
|
|
|
'id': 'xLTCUSDT',
|
|
|
|
|
'symbol': 'XLTCUSDT',
|
|
|
|
|
'base': 'LTC',
|
|
|
|
|
'quote': 'USDT',
|
|
|
|
|
'active': True,
|
|
|
|
|
'precision': {
|
|
|
|
|
'amount': 8,
|
|
|
|
|
'price': 8
|
|
|
|
|
},
|
|
|
|
|
'limits': {
|
|
|
|
|
'amount': {
|
|
|
|
|
'min': 0.06646786,
|
|
|
|
|
'max': None
|
|
|
|
|
},
|
|
|
|
|
'price': {
|
|
|
|
|
'min': 1e-08,
|
|
|
|
|
'max': None
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
'info': {},
|
2018-03-25 19:51:41 +00:00
|
|
|
|
}
|
2019-03-05 18:45:54 +00:00
|
|
|
|
}
|
2018-03-25 19:51:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def markets_empty():
|
|
|
|
|
return MagicMock(return_value=[])
|
2017-11-13 20:34:47 +00:00
|
|
|
|
|
|
|
|
|
|
2018-04-22 09:05:23 +00:00
|
|
|
|
@pytest.fixture(scope='function')
|
2017-11-07 19:12:56 +00:00
|
|
|
|
def limit_buy_order():
|
|
|
|
|
return {
|
|
|
|
|
'id': 'mocked_limit_buy',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'type': 'limit',
|
|
|
|
|
'side': 'buy',
|
2017-11-07 19:12:56 +00:00
|
|
|
|
'pair': 'mocked',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'datetime': arrow.utcnow().isoformat(),
|
|
|
|
|
'price': 0.00001099,
|
2017-12-17 21:07:56 +00:00
|
|
|
|
'amount': 90.99181073,
|
2017-11-07 19:12:56 +00:00
|
|
|
|
'remaining': 0.0,
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'status': 'closed'
|
2018-01-04 14:39:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-12-27 08:31:21 +00:00
|
|
|
|
@pytest.fixture(scope='function')
|
|
|
|
|
def market_buy_order():
|
|
|
|
|
return {
|
|
|
|
|
'id': 'mocked_market_buy',
|
|
|
|
|
'type': 'market',
|
|
|
|
|
'side': 'buy',
|
|
|
|
|
'pair': 'mocked',
|
|
|
|
|
'datetime': arrow.utcnow().isoformat(),
|
|
|
|
|
'price': 0.00004099,
|
|
|
|
|
'amount': 91.99181073,
|
|
|
|
|
'remaining': 0.0,
|
|
|
|
|
'status': 'closed'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def market_sell_order():
|
|
|
|
|
return {
|
|
|
|
|
'id': 'mocked_limit_sell',
|
|
|
|
|
'type': 'market',
|
|
|
|
|
'side': 'sell',
|
|
|
|
|
'pair': 'mocked',
|
|
|
|
|
'datetime': arrow.utcnow().isoformat(),
|
|
|
|
|
'price': 0.00004173,
|
|
|
|
|
'amount': 91.99181073,
|
|
|
|
|
'remaining': 0.0,
|
|
|
|
|
'status': 'closed'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-01-04 14:39:01 +00:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def limit_buy_order_old():
|
|
|
|
|
return {
|
|
|
|
|
'id': 'mocked_limit_buy_old',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'type': 'limit',
|
|
|
|
|
'side': 'buy',
|
|
|
|
|
'pair': 'mocked',
|
|
|
|
|
'datetime': str(arrow.utcnow().shift(minutes=-601).datetime),
|
|
|
|
|
'price': 0.00001099,
|
2018-01-04 14:39:01 +00:00
|
|
|
|
'amount': 90.99181073,
|
|
|
|
|
'remaining': 90.99181073,
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'status': 'open'
|
2018-01-04 14:39:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def limit_sell_order_old():
|
|
|
|
|
return {
|
|
|
|
|
'id': 'mocked_limit_sell_old',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'type': 'limit',
|
|
|
|
|
'side': 'sell',
|
2018-03-24 19:02:13 +00:00
|
|
|
|
'pair': 'ETH/BTC',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
|
|
|
|
'price': 0.00001099,
|
2018-01-04 14:39:01 +00:00
|
|
|
|
'amount': 90.99181073,
|
|
|
|
|
'remaining': 90.99181073,
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'status': 'open'
|
2018-01-04 14:39:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def limit_buy_order_old_partial():
|
|
|
|
|
return {
|
|
|
|
|
'id': 'mocked_limit_buy_old_partial',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'type': 'limit',
|
|
|
|
|
'side': 'buy',
|
2018-03-24 19:02:13 +00:00
|
|
|
|
'pair': 'ETH/BTC',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
|
|
|
|
'price': 0.00001099,
|
2018-01-04 14:39:01 +00:00
|
|
|
|
'amount': 90.99181073,
|
|
|
|
|
'remaining': 67.99181073,
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'status': 'open'
|
2017-11-07 19:12:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-10-18 04:38:07 +00:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def limit_buy_order_old_partial_canceled(limit_buy_order_old_partial):
|
|
|
|
|
res = deepcopy(limit_buy_order_old_partial)
|
|
|
|
|
res['status'] = 'canceled'
|
|
|
|
|
res['fee'] = {'cost': 0.0001, 'currency': 'ETH'}
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
|
2017-11-07 19:12:56 +00:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def limit_sell_order():
|
|
|
|
|
return {
|
|
|
|
|
'id': 'mocked_limit_sell',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'type': 'limit',
|
|
|
|
|
'side': 'sell',
|
2017-11-07 19:12:56 +00:00
|
|
|
|
'pair': 'mocked',
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'datetime': arrow.utcnow().isoformat(),
|
|
|
|
|
'price': 0.00001173,
|
2017-12-17 21:07:56 +00:00
|
|
|
|
'amount': 90.99181073,
|
2017-11-07 19:12:56 +00:00
|
|
|
|
'remaining': 0.0,
|
2018-04-06 07:57:08 +00:00
|
|
|
|
'status': 'closed'
|
2017-11-07 19:12:56 +00:00
|
|
|
|
}
|
2017-12-16 23:24:21 +00:00
|
|
|
|
|
|
|
|
|
|
2018-08-14 10:12:44 +00:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def order_book_l2():
|
|
|
|
|
return MagicMock(return_value={
|
|
|
|
|
'bids': [
|
|
|
|
|
[0.043936, 10.442],
|
|
|
|
|
[0.043935, 31.865],
|
|
|
|
|
[0.043933, 11.212],
|
|
|
|
|
[0.043928, 0.088],
|
|
|
|
|
[0.043925, 10.0],
|
|
|
|
|
[0.043921, 10.0],
|
|
|
|
|
[0.04392, 37.64],
|
|
|
|
|
[0.043899, 0.066],
|
|
|
|
|
[0.043885, 0.676],
|
|
|
|
|
[0.04387, 22.758]
|
|
|
|
|
],
|
|
|
|
|
'asks': [
|
|
|
|
|
[0.043949, 0.346],
|
|
|
|
|
[0.04395, 0.608],
|
|
|
|
|
[0.043951, 3.948],
|
|
|
|
|
[0.043954, 0.288],
|
|
|
|
|
[0.043958, 9.277],
|
|
|
|
|
[0.043995, 1.566],
|
|
|
|
|
[0.044, 0.588],
|
|
|
|
|
[0.044002, 0.992],
|
|
|
|
|
[0.044003, 0.095],
|
|
|
|
|
[0.04402, 37.64]
|
|
|
|
|
],
|
|
|
|
|
'timestamp': None,
|
|
|
|
|
'datetime': None,
|
|
|
|
|
'nonce': 288004540
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
2018-04-06 07:57:08 +00:00
|
|
|
|
@pytest.fixture
|
2018-12-11 18:48:36 +00:00
|
|
|
|
def ticker_history_list():
|
2018-04-06 07:57:08 +00:00
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
1511686200000, # unix timestamp ms
|
|
|
|
|
8.794e-05, # open
|
|
|
|
|
8.948e-05, # high
|
|
|
|
|
8.794e-05, # low
|
|
|
|
|
8.88e-05, # close
|
|
|
|
|
0.0877869, # volume (in quote currency)
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
1511686500000,
|
|
|
|
|
8.88e-05,
|
|
|
|
|
8.942e-05,
|
|
|
|
|
8.88e-05,
|
|
|
|
|
8.893e-05,
|
|
|
|
|
0.05874751,
|
|
|
|
|
],
|
|
|
|
|
[
|
2018-05-02 20:49:55 +00:00
|
|
|
|
1511686800000,
|
2018-04-06 07:57:08 +00:00
|
|
|
|
8.891e-05,
|
|
|
|
|
8.893e-05,
|
|
|
|
|
8.875e-05,
|
|
|
|
|
8.877e-05,
|
|
|
|
|
0.7039405
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2018-12-11 18:48:36 +00:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def ticker_history(ticker_history_list):
|
2019-06-15 11:47:20 +00:00
|
|
|
|
return parse_ticker_dataframe(ticker_history_list, "5m", pair="UNITTEST/BTC", fill_missing=True)
|
2018-12-11 18:48:36 +00:00
|
|
|
|
|
|
|
|
|
|
2018-03-26 09:24:20 +00:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def tickers():
|
|
|
|
|
return MagicMock(return_value={
|
|
|
|
|
'ETH/BTC': {
|
|
|
|
|
'symbol': 'ETH/BTC',
|
|
|
|
|
'timestamp': 1522014806207,
|
|
|
|
|
'datetime': '2018-03-25T21:53:26.207Z',
|
|
|
|
|
'high': 0.061697,
|
|
|
|
|
'low': 0.060531,
|
|
|
|
|
'bid': 0.061588,
|
|
|
|
|
'bidVolume': 3.321,
|
|
|
|
|
'ask': 0.061655,
|
|
|
|
|
'askVolume': 0.212,
|
|
|
|
|
'vwap': 0.06105296,
|
|
|
|
|
'open': 0.060809,
|
|
|
|
|
'close': 0.060761,
|
|
|
|
|
'first': None,
|
|
|
|
|
'last': 0.061588,
|
|
|
|
|
'change': 1.281,
|
|
|
|
|
'percentage': None,
|
|
|
|
|
'average': None,
|
|
|
|
|
'baseVolume': 111649.001,
|
|
|
|
|
'quoteVolume': 6816.50176926,
|
|
|
|
|
'info': {}
|
|
|
|
|
},
|
|
|
|
|
'TKN/BTC': {
|
|
|
|
|
'symbol': 'TKN/BTC',
|
|
|
|
|
'timestamp': 1522014806169,
|
|
|
|
|
'datetime': '2018-03-25T21:53:26.169Z',
|
|
|
|
|
'high': 0.01885,
|
|
|
|
|
'low': 0.018497,
|
|
|
|
|
'bid': 0.018799,
|
|
|
|
|
'bidVolume': 8.38,
|
|
|
|
|
'ask': 0.018802,
|
|
|
|
|
'askVolume': 15.0,
|
|
|
|
|
'vwap': 0.01869197,
|
|
|
|
|
'open': 0.018585,
|
|
|
|
|
'close': 0.018573,
|
2019-03-03 14:31:48 +00:00
|
|
|
|
'last': 0.018799,
|
2018-03-26 09:24:20 +00:00
|
|
|
|
'baseVolume': 81058.66,
|
|
|
|
|
'quoteVolume': 2247.48374509,
|
|
|
|
|
},
|
|
|
|
|
'BLK/BTC': {
|
|
|
|
|
'symbol': 'BLK/BTC',
|
|
|
|
|
'timestamp': 1522014806072,
|
|
|
|
|
'datetime': '2018-03-25T21:53:26.720Z',
|
|
|
|
|
'high': 0.007745,
|
|
|
|
|
'low': 0.007512,
|
|
|
|
|
'bid': 0.007729,
|
|
|
|
|
'bidVolume': 0.01,
|
|
|
|
|
'ask': 0.007743,
|
|
|
|
|
'askVolume': 21.37,
|
|
|
|
|
'vwap': 0.00761466,
|
|
|
|
|
'open': 0.007653,
|
|
|
|
|
'close': 0.007652,
|
|
|
|
|
'first': None,
|
|
|
|
|
'last': 0.007743,
|
|
|
|
|
'change': 1.176,
|
|
|
|
|
'percentage': None,
|
|
|
|
|
'average': None,
|
|
|
|
|
'baseVolume': 295152.26,
|
|
|
|
|
'quoteVolume': 1515.14631229,
|
|
|
|
|
'info': {}
|
|
|
|
|
},
|
2018-04-15 10:19:49 +00:00
|
|
|
|
'LTC/BTC': {
|
|
|
|
|
'symbol': 'LTC/BTC',
|
|
|
|
|
'timestamp': 1523787258992,
|
|
|
|
|
'datetime': '2018-04-15T10:14:19.992Z',
|
|
|
|
|
'high': 0.015978,
|
|
|
|
|
'low': 0.0157,
|
|
|
|
|
'bid': 0.015954,
|
|
|
|
|
'bidVolume': 12.83,
|
|
|
|
|
'ask': 0.015957,
|
|
|
|
|
'askVolume': 0.49,
|
|
|
|
|
'vwap': 0.01581636,
|
|
|
|
|
'open': 0.015823,
|
|
|
|
|
'close': 0.01582,
|
|
|
|
|
'first': None,
|
|
|
|
|
'last': 0.015951,
|
|
|
|
|
'change': 0.809,
|
|
|
|
|
'percentage': None,
|
|
|
|
|
'average': None,
|
|
|
|
|
'baseVolume': 88620.68,
|
|
|
|
|
'quoteVolume': 1401.65697943,
|
|
|
|
|
'info': {}
|
|
|
|
|
},
|
2019-03-02 17:53:42 +00:00
|
|
|
|
'BTT/BTC': {
|
|
|
|
|
'symbol': 'BTT/BTC',
|
|
|
|
|
'timestamp': 1550936557206,
|
|
|
|
|
'datetime': '2019-02-23T15:42:37.206Z',
|
|
|
|
|
'high': 0.00000026,
|
|
|
|
|
'low': 0.00000024,
|
|
|
|
|
'bid': 0.00000024,
|
|
|
|
|
'bidVolume': 2446894197.0,
|
|
|
|
|
'ask': 0.00000025,
|
|
|
|
|
'askVolume': 2447913837.0,
|
|
|
|
|
'vwap': 0.00000025,
|
|
|
|
|
'open': 0.00000026,
|
|
|
|
|
'close': 0.00000024,
|
|
|
|
|
'last': 0.00000024,
|
|
|
|
|
'previousClose': 0.00000026,
|
|
|
|
|
'change': -0.00000002,
|
|
|
|
|
'percentage': -7.692,
|
|
|
|
|
'average': None,
|
|
|
|
|
'baseVolume': 4886464537.0,
|
|
|
|
|
'quoteVolume': 1215.14489611,
|
|
|
|
|
'info': {}
|
|
|
|
|
},
|
2018-03-26 09:24:20 +00:00
|
|
|
|
'ETH/USDT': {
|
|
|
|
|
'symbol': 'ETH/USDT',
|
|
|
|
|
'timestamp': 1522014804118,
|
|
|
|
|
'datetime': '2018-03-25T21:53:24.118Z',
|
|
|
|
|
'high': 530.88,
|
|
|
|
|
'low': 512.0,
|
|
|
|
|
'bid': 529.73,
|
|
|
|
|
'bidVolume': 0.2,
|
|
|
|
|
'ask': 530.21,
|
|
|
|
|
'askVolume': 0.2464,
|
|
|
|
|
'vwap': 521.02438405,
|
|
|
|
|
'open': 527.27,
|
|
|
|
|
'close': 528.42,
|
|
|
|
|
'first': None,
|
|
|
|
|
'last': 530.21,
|
|
|
|
|
'change': 0.558,
|
|
|
|
|
'percentage': None,
|
|
|
|
|
'average': None,
|
|
|
|
|
'baseVolume': 72300.0659,
|
|
|
|
|
'quoteVolume': 37670097.3022171,
|
|
|
|
|
'info': {}
|
|
|
|
|
},
|
|
|
|
|
'TKN/USDT': {
|
|
|
|
|
'symbol': 'TKN/USDT',
|
|
|
|
|
'timestamp': 1522014806198,
|
|
|
|
|
'datetime': '2018-03-25T21:53:26.198Z',
|
|
|
|
|
'high': 8718.0,
|
|
|
|
|
'low': 8365.77,
|
|
|
|
|
'bid': 8603.64,
|
|
|
|
|
'bidVolume': 0.15846,
|
|
|
|
|
'ask': 8603.67,
|
|
|
|
|
'askVolume': 0.069147,
|
|
|
|
|
'vwap': 8536.35621697,
|
|
|
|
|
'open': 8680.0,
|
|
|
|
|
'close': 8680.0,
|
|
|
|
|
'first': None,
|
|
|
|
|
'last': 8603.67,
|
|
|
|
|
'change': -0.879,
|
|
|
|
|
'percentage': None,
|
|
|
|
|
'average': None,
|
|
|
|
|
'baseVolume': 30414.604298,
|
|
|
|
|
'quoteVolume': 259629896.48584127,
|
|
|
|
|
'info': {}
|
|
|
|
|
},
|
|
|
|
|
'BLK/USDT': {
|
|
|
|
|
'symbol': 'BLK/USDT',
|
|
|
|
|
'timestamp': 1522014806145,
|
|
|
|
|
'datetime': '2018-03-25T21:53:26.145Z',
|
|
|
|
|
'high': 66.95,
|
|
|
|
|
'low': 63.38,
|
|
|
|
|
'bid': 66.473,
|
|
|
|
|
'bidVolume': 4.968,
|
|
|
|
|
'ask': 66.54,
|
|
|
|
|
'askVolume': 2.704,
|
|
|
|
|
'vwap': 65.0526901,
|
|
|
|
|
'open': 66.43,
|
|
|
|
|
'close': 66.383,
|
|
|
|
|
'first': None,
|
|
|
|
|
'last': 66.5,
|
|
|
|
|
'change': 0.105,
|
|
|
|
|
'percentage': None,
|
|
|
|
|
'average': None,
|
|
|
|
|
'baseVolume': 294106.204,
|
|
|
|
|
'quoteVolume': 19132399.743954,
|
|
|
|
|
'info': {}
|
2018-04-15 10:19:49 +00:00
|
|
|
|
},
|
|
|
|
|
'LTC/USDT': {
|
|
|
|
|
'symbol': 'LTC/USDT',
|
|
|
|
|
'timestamp': 1523787257812,
|
|
|
|
|
'datetime': '2018-04-15T10:14:18.812Z',
|
|
|
|
|
'high': 129.94,
|
|
|
|
|
'low': 124.0,
|
|
|
|
|
'bid': 129.28,
|
|
|
|
|
'bidVolume': 0.03201,
|
|
|
|
|
'ask': 129.52,
|
|
|
|
|
'askVolume': 0.14529,
|
|
|
|
|
'vwap': 126.92838682,
|
|
|
|
|
'open': 127.0,
|
|
|
|
|
'close': 127.1,
|
|
|
|
|
'first': None,
|
|
|
|
|
'last': 129.28,
|
|
|
|
|
'change': 1.795,
|
|
|
|
|
'percentage': None,
|
|
|
|
|
'average': None,
|
|
|
|
|
'baseVolume': 59698.79897,
|
|
|
|
|
'quoteVolume': 29132399.743954,
|
|
|
|
|
'info': {}
|
2018-03-26 09:24:20 +00:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
2018-01-28 07:38:41 +00:00
|
|
|
|
@pytest.fixture
|
2019-09-08 07:54:15 +00:00
|
|
|
|
def result(testdatadir):
|
|
|
|
|
with (testdatadir / 'UNITTEST_BTC-1m.json').open('r') as data_file:
|
2019-06-24 04:21:08 +00:00
|
|
|
|
return parse_ticker_dataframe(json.load(data_file), '1m', pair="UNITTEST/BTC",
|
|
|
|
|
fill_missing=True)
|
2018-01-28 07:38:41 +00:00
|
|
|
|
|
2018-04-15 17:56:33 +00:00
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
|
|
|
def trades_for_order():
|
|
|
|
|
return [{'info': {'id': 34567,
|
|
|
|
|
'orderId': 123456,
|
|
|
|
|
'price': '0.24544100',
|
|
|
|
|
'qty': '8.00000000',
|
|
|
|
|
'commission': '0.00800000',
|
|
|
|
|
'commissionAsset': 'LTC',
|
|
|
|
|
'time': 1521663363189,
|
|
|
|
|
'isBuyer': True,
|
|
|
|
|
'isMaker': False,
|
|
|
|
|
'isBestMatch': True},
|
|
|
|
|
'timestamp': 1521663363189,
|
|
|
|
|
'datetime': '2018-03-21T20:16:03.189Z',
|
|
|
|
|
'symbol': 'LTC/ETH',
|
|
|
|
|
'id': '34567',
|
|
|
|
|
'order': '123456',
|
|
|
|
|
'type': None,
|
|
|
|
|
'side': 'buy',
|
|
|
|
|
'price': 0.245441,
|
|
|
|
|
'cost': 1.963528,
|
|
|
|
|
'amount': 8.0,
|
|
|
|
|
'fee': {'cost': 0.008, 'currency': 'LTC'}}]
|
|
|
|
|
|
|
|
|
|
|
2019-08-29 14:33:56 +00:00
|
|
|
|
@pytest.fixture(scope="function")
|
|
|
|
|
def trades_history():
|
|
|
|
|
return [{'info': {'a': 126181329,
|
|
|
|
|
'p': '0.01962700',
|
|
|
|
|
'q': '0.04000000',
|
|
|
|
|
'f': 138604155,
|
|
|
|
|
'l': 138604155,
|
|
|
|
|
'T': 1565798399463,
|
|
|
|
|
'm': False,
|
|
|
|
|
'M': True},
|
|
|
|
|
'timestamp': 1565798399463,
|
|
|
|
|
'datetime': '2019-08-14T15:59:59.463Z',
|
|
|
|
|
'symbol': 'ETH/BTC',
|
|
|
|
|
'id': '126181329',
|
|
|
|
|
'order': None,
|
|
|
|
|
'type': None,
|
|
|
|
|
'takerOrMaker': None,
|
|
|
|
|
'side': 'buy',
|
|
|
|
|
'price': 0.019627,
|
|
|
|
|
'amount': 0.04,
|
|
|
|
|
'cost': 0.00078508,
|
|
|
|
|
'fee': None},
|
|
|
|
|
{'info': {'a': 126181330,
|
|
|
|
|
'p': '0.01962700',
|
|
|
|
|
'q': '0.24400000',
|
|
|
|
|
'f': 138604156,
|
|
|
|
|
'l': 138604156,
|
|
|
|
|
'T': 1565798399629,
|
|
|
|
|
'm': False,
|
|
|
|
|
'M': True},
|
|
|
|
|
'timestamp': 1565798399629,
|
|
|
|
|
'datetime': '2019-08-14T15:59:59.629Z',
|
|
|
|
|
'symbol': 'ETH/BTC',
|
|
|
|
|
'id': '126181330',
|
|
|
|
|
'order': None,
|
|
|
|
|
'type': None,
|
|
|
|
|
'takerOrMaker': None,
|
|
|
|
|
'side': 'buy',
|
|
|
|
|
'price': 0.019627,
|
|
|
|
|
'amount': 0.244,
|
|
|
|
|
'cost': 0.004788987999999999,
|
|
|
|
|
'fee': None},
|
|
|
|
|
{'info': {'a': 126181331,
|
|
|
|
|
'p': '0.01962600',
|
|
|
|
|
'q': '0.01100000',
|
|
|
|
|
'f': 138604157,
|
|
|
|
|
'l': 138604157,
|
|
|
|
|
'T': 1565798399752,
|
|
|
|
|
'm': True,
|
|
|
|
|
'M': True},
|
|
|
|
|
'timestamp': 1565798399752,
|
|
|
|
|
'datetime': '2019-08-14T15:59:59.752Z',
|
|
|
|
|
'symbol': 'ETH/BTC',
|
|
|
|
|
'id': '126181331',
|
|
|
|
|
'order': None,
|
|
|
|
|
'type': None,
|
|
|
|
|
'takerOrMaker': None,
|
|
|
|
|
'side': 'sell',
|
|
|
|
|
'price': 0.019626,
|
|
|
|
|
'amount': 0.011,
|
|
|
|
|
'cost': 0.00021588599999999999,
|
2019-10-08 18:16:02 +00:00
|
|
|
|
'fee': None},
|
|
|
|
|
{'info': {'a': 126181332,
|
|
|
|
|
'p': '0.01962600',
|
|
|
|
|
'q': '0.01100000',
|
|
|
|
|
'f': 138604158,
|
|
|
|
|
'l': 138604158,
|
|
|
|
|
'T': 1565798399862,
|
|
|
|
|
'm': True,
|
|
|
|
|
'M': True},
|
|
|
|
|
'timestamp': 1565798399862,
|
|
|
|
|
'datetime': '2019-08-14T15:59:59.862Z',
|
|
|
|
|
'symbol': 'ETH/BTC',
|
|
|
|
|
'id': '126181332',
|
|
|
|
|
'order': None,
|
|
|
|
|
'type': None,
|
|
|
|
|
'takerOrMaker': None,
|
|
|
|
|
'side': 'sell',
|
|
|
|
|
'price': 0.019626,
|
|
|
|
|
'amount': 0.011,
|
|
|
|
|
'cost': 0.00021588599999999999,
|
|
|
|
|
'fee': None},
|
|
|
|
|
{'info': {'a': 126181333,
|
|
|
|
|
'p': '0.01952600',
|
|
|
|
|
'q': '0.01200000',
|
|
|
|
|
'f': 138604158,
|
|
|
|
|
'l': 138604158,
|
|
|
|
|
'T': 1565798399872,
|
|
|
|
|
'm': True,
|
|
|
|
|
'M': True},
|
|
|
|
|
'timestamp': 1565798399872,
|
|
|
|
|
'datetime': '2019-08-14T15:59:59.872Z',
|
|
|
|
|
'symbol': 'ETH/BTC',
|
|
|
|
|
'id': '126181333',
|
|
|
|
|
'order': None,
|
|
|
|
|
'type': None,
|
|
|
|
|
'takerOrMaker': None,
|
|
|
|
|
'side': 'sell',
|
|
|
|
|
'price': 0.019626,
|
|
|
|
|
'amount': 0.011,
|
|
|
|
|
'cost': 0.00021588599999999999,
|
2019-08-29 14:33:56 +00:00
|
|
|
|
'fee': None}]
|
|
|
|
|
|
|
|
|
|
|
2018-04-15 17:56:33 +00:00
|
|
|
|
@pytest.fixture(scope="function")
|
|
|
|
|
def trades_for_order2():
|
|
|
|
|
return [{'info': {'id': 34567,
|
|
|
|
|
'orderId': 123456,
|
|
|
|
|
'price': '0.24544100',
|
|
|
|
|
'qty': '8.00000000',
|
|
|
|
|
'commission': '0.00800000',
|
|
|
|
|
'commissionAsset': 'LTC',
|
|
|
|
|
'time': 1521663363189,
|
|
|
|
|
'isBuyer': True,
|
|
|
|
|
'isMaker': False,
|
|
|
|
|
'isBestMatch': True},
|
|
|
|
|
'timestamp': 1521663363189,
|
|
|
|
|
'datetime': '2018-03-21T20:16:03.189Z',
|
|
|
|
|
'symbol': 'LTC/ETH',
|
|
|
|
|
'id': '34567',
|
|
|
|
|
'order': '123456',
|
|
|
|
|
'type': None,
|
|
|
|
|
'side': 'buy',
|
|
|
|
|
'price': 0.245441,
|
|
|
|
|
'cost': 1.963528,
|
|
|
|
|
'amount': 4.0,
|
|
|
|
|
'fee': {'cost': 0.004, 'currency': 'LTC'}},
|
|
|
|
|
{'info': {'id': 34567,
|
|
|
|
|
'orderId': 123456,
|
|
|
|
|
'price': '0.24544100',
|
|
|
|
|
'qty': '8.00000000',
|
|
|
|
|
'commission': '0.00800000',
|
|
|
|
|
'commissionAsset': 'LTC',
|
|
|
|
|
'time': 1521663363189,
|
|
|
|
|
'isBuyer': True,
|
|
|
|
|
'isMaker': False,
|
|
|
|
|
'isBestMatch': True},
|
|
|
|
|
'timestamp': 1521663363189,
|
|
|
|
|
'datetime': '2018-03-21T20:16:03.189Z',
|
|
|
|
|
'symbol': 'LTC/ETH',
|
|
|
|
|
'id': '34567',
|
|
|
|
|
'order': '123456',
|
|
|
|
|
'type': None,
|
|
|
|
|
'side': 'buy',
|
|
|
|
|
'price': 0.245441,
|
|
|
|
|
'cost': 1.963528,
|
|
|
|
|
'amount': 4.0,
|
|
|
|
|
'fee': {'cost': 0.004, 'currency': 'LTC'}}]
|
2018-04-25 06:51:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def buy_order_fee():
|
|
|
|
|
return {
|
|
|
|
|
'id': 'mocked_limit_buy_old',
|
|
|
|
|
'type': 'limit',
|
|
|
|
|
'side': 'buy',
|
|
|
|
|
'pair': 'mocked',
|
|
|
|
|
'datetime': str(arrow.utcnow().shift(minutes=-601).datetime),
|
|
|
|
|
'price': 0.245441,
|
|
|
|
|
'amount': 8.0,
|
|
|
|
|
'remaining': 90.99181073,
|
2018-04-25 07:08:02 +00:00
|
|
|
|
'status': 'closed',
|
2018-04-25 06:51:31 +00:00
|
|
|
|
'fee': None
|
|
|
|
|
}
|
2018-11-09 19:51:15 +00:00
|
|
|
|
|
2018-11-10 16:20:11 +00:00
|
|
|
|
|
2018-11-09 19:51:15 +00:00
|
|
|
|
@pytest.fixture(scope="function")
|
|
|
|
|
def edge_conf(default_conf):
|
2019-05-20 17:35:19 +00:00
|
|
|
|
conf = deepcopy(default_conf)
|
|
|
|
|
conf['max_open_trades'] = -1
|
|
|
|
|
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
|
|
|
|
conf['edge'] = {
|
2018-11-10 16:20:11 +00:00
|
|
|
|
"enabled": True,
|
2018-11-09 19:51:15 +00:00
|
|
|
|
"process_throttle_secs": 1800,
|
|
|
|
|
"calculate_since_number_of_days": 14,
|
2018-11-27 13:02:34 +00:00
|
|
|
|
"capital_available_percentage": 0.5,
|
2018-11-09 19:51:15 +00:00
|
|
|
|
"allowed_risk": 0.01,
|
|
|
|
|
"stoploss_range_min": -0.01,
|
|
|
|
|
"stoploss_range_max": -0.1,
|
|
|
|
|
"stoploss_range_step": -0.01,
|
|
|
|
|
"maximum_winrate": 0.80,
|
|
|
|
|
"minimum_expectancy": 0.20,
|
|
|
|
|
"min_trade_number": 15,
|
|
|
|
|
"max_trade_duration_minute": 1440,
|
|
|
|
|
"remove_pumps": False
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-20 17:35:19 +00:00
|
|
|
|
return conf
|
2019-05-11 07:10:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def rpc_balance():
|
|
|
|
|
return {
|
|
|
|
|
'BTC': {
|
|
|
|
|
'total': 12.0,
|
|
|
|
|
'free': 12.0,
|
|
|
|
|
'used': 0.0
|
|
|
|
|
},
|
|
|
|
|
'ETH': {
|
|
|
|
|
'total': 0.0,
|
|
|
|
|
'free': 0.0,
|
|
|
|
|
'used': 0.0
|
|
|
|
|
},
|
|
|
|
|
'USDT': {
|
|
|
|
|
'total': 10000.0,
|
|
|
|
|
'free': 10000.0,
|
|
|
|
|
'used': 0.0
|
|
|
|
|
},
|
|
|
|
|
'LTC': {
|
|
|
|
|
'total': 10.0,
|
|
|
|
|
'free': 10.0,
|
|
|
|
|
'used': 0.0
|
|
|
|
|
},
|
|
|
|
|
'XRP': {
|
|
|
|
|
'total': 1.0,
|
|
|
|
|
'free': 1.0,
|
|
|
|
|
'used': 0.0
|
|
|
|
|
},
|
|
|
|
|
'EUR': {
|
|
|
|
|
'total': 10.0,
|
|
|
|
|
'free': 10.0,
|
|
|
|
|
'used': 0.0
|
|
|
|
|
},
|
|
|
|
|
}
|
2019-09-07 18:34:25 +00:00
|
|
|
|
|
|
|
|
|
|
2019-09-07 18:56:03 +00:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def testdatadir() -> Path:
|
2019-09-07 18:34:25 +00:00
|
|
|
|
"""Return the path where testdata files are stored"""
|
|
|
|
|
return (Path(__file__).parent / "testdata").resolve()
|
2019-09-25 09:54:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
|
|
|
def import_fails() -> None:
|
|
|
|
|
# Source of this test-method:
|
|
|
|
|
# https://stackoverflow.com/questions/2481511/mocking-importerror-in-python
|
|
|
|
|
import builtins
|
|
|
|
|
realimport = builtins.__import__
|
|
|
|
|
|
|
|
|
|
def mockedimport(name, *args, **kwargs):
|
|
|
|
|
if name in ["filelock"]:
|
|
|
|
|
raise ImportError(f"No module named '{name}'")
|
|
|
|
|
return realimport(name, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
builtins.__import__ = mockedimport
|
|
|
|
|
|
|
|
|
|
# Run test - then cleanup
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
|
|
# restore previous importfunction
|
|
|
|
|
builtins.__import__ = realimport
|
2019-10-17 17:53:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
|
|
|
def open_trade():
|
|
|
|
|
return Trade(
|
|
|
|
|
pair='ETH/BTC',
|
|
|
|
|
open_rate=0.00001099,
|
|
|
|
|
exchange='bittrex',
|
|
|
|
|
open_order_id='123456789',
|
|
|
|
|
amount=90.99181073,
|
|
|
|
|
fee_open=0.0,
|
|
|
|
|
fee_close=0.0,
|
|
|
|
|
stake_amount=1,
|
|
|
|
|
open_date=arrow.utcnow().shift(minutes=-601).datetime,
|
|
|
|
|
is_open=True
|
|
|
|
|
)
|