Merge branch 'develop' into pair-to-strat
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Dict, Optional
|
||||
from functools import reduce
|
||||
from typing import Dict, Optional
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import arrow
|
||||
@@ -11,8 +11,9 @@ import pytest
|
||||
from jsonschema import validate
|
||||
from telegram import Chat, Message, Update
|
||||
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade import constants
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.freqtradebot import FreqtradeBot
|
||||
|
||||
logging.getLogger('').setLevel(logging.INFO)
|
||||
@@ -26,6 +27,20 @@ def log_has(line, logs):
|
||||
False)
|
||||
|
||||
|
||||
def patch_exchange(mocker, api_mock=None) -> None:
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
if api_mock:
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
else:
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock())
|
||||
|
||||
|
||||
def get_patched_exchange(mocker, config, api_mock=None) -> Exchange:
|
||||
patch_exchange(mocker, api_mock)
|
||||
exchange = Exchange(config)
|
||||
return exchange
|
||||
|
||||
|
||||
# Functions for recurrent object patching
|
||||
def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
||||
"""
|
||||
@@ -39,7 +54,7 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
||||
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())
|
||||
patch_exchange(mocker, None)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.get_signal', MagicMock())
|
||||
@@ -85,7 +100,10 @@ def default_conf():
|
||||
"0": 0.04
|
||||
},
|
||||
"stoploss": -0.10,
|
||||
"unfilledtimeout": 600,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
"sell": 30
|
||||
},
|
||||
"bid_strategy": {
|
||||
"ask_last_balance": 0.0
|
||||
},
|
||||
@@ -174,7 +192,10 @@ def markets():
|
||||
'max': 1000,
|
||||
},
|
||||
'price': 500000,
|
||||
'cost': 500000,
|
||||
'cost': {
|
||||
'min': 1,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': '',
|
||||
},
|
||||
@@ -196,7 +217,10 @@ def markets():
|
||||
'max': 1000,
|
||||
},
|
||||
'price': 500000,
|
||||
'cost': 500000,
|
||||
'cost': {
|
||||
'min': 1,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': '',
|
||||
},
|
||||
@@ -218,7 +242,85 @@ def markets():
|
||||
'max': 1000,
|
||||
},
|
||||
'price': 500000,
|
||||
'cost': 500000,
|
||||
'cost': {
|
||||
'min': 1,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': '',
|
||||
},
|
||||
{
|
||||
'id': 'ltcbtc',
|
||||
'symbol': 'LTC/BTC',
|
||||
'base': 'LTC',
|
||||
'quote': 'BTC',
|
||||
'active': False,
|
||||
'precision': {
|
||||
'price': 8,
|
||||
'amount': 8,
|
||||
'cost': 8,
|
||||
},
|
||||
'lot': 0.00000001,
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': 0.01,
|
||||
'max': 1000,
|
||||
},
|
||||
'price': 500000,
|
||||
'cost': {
|
||||
'min': 1,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': '',
|
||||
},
|
||||
{
|
||||
'id': 'xrpbtc',
|
||||
'symbol': 'XRP/BTC',
|
||||
'base': 'XRP',
|
||||
'quote': 'BTC',
|
||||
'active': False,
|
||||
'precision': {
|
||||
'price': 8,
|
||||
'amount': 8,
|
||||
'cost': 8,
|
||||
},
|
||||
'lot': 0.00000001,
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': 0.01,
|
||||
'max': 1000,
|
||||
},
|
||||
'price': 500000,
|
||||
'cost': {
|
||||
'min': 1,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': '',
|
||||
},
|
||||
{
|
||||
'id': 'neobtc',
|
||||
'symbol': 'NEO/BTC',
|
||||
'base': 'NEO',
|
||||
'quote': 'BTC',
|
||||
'active': False,
|
||||
'precision': {
|
||||
'price': 8,
|
||||
'amount': 8,
|
||||
'cost': 8,
|
||||
},
|
||||
'lot': 0.00000001,
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': 0.01,
|
||||
'max': 1000,
|
||||
},
|
||||
'price': 500000,
|
||||
'cost': {
|
||||
'min': 1,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': '',
|
||||
}
|
||||
|
||||
@@ -2,44 +2,54 @@
|
||||
# pragma pylint: disable=protected-access
|
||||
import logging
|
||||
from copy import deepcopy
|
||||
from datetime import datetime
|
||||
from random import randint
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import ccxt
|
||||
import pytest
|
||||
|
||||
import freqtrade.exchange as exchange
|
||||
from freqtrade import OperationalException, DependencyException, TemporaryError
|
||||
from freqtrade.exchange import (init, validate_pairs, buy, sell, get_balance, get_balances,
|
||||
get_ticker, get_ticker_history, cancel_order, get_name, get_fee,
|
||||
get_id, get_pair_detail_url, get_amount_lots)
|
||||
from freqtrade.tests.conftest import log_has
|
||||
|
||||
API_INIT = False
|
||||
from freqtrade import DependencyException, OperationalException, TemporaryError
|
||||
from freqtrade.exchange import API_RETRY_COUNT, Exchange
|
||||
from freqtrade.tests.conftest import get_patched_exchange, log_has
|
||||
|
||||
|
||||
def maybe_init_api(conf, mocker, force=False):
|
||||
global API_INIT
|
||||
if force or not API_INIT:
|
||||
mocker.patch('freqtrade.exchange.validate_pairs',
|
||||
side_effect=lambda s: True)
|
||||
init(config=conf)
|
||||
API_INIT = True
|
||||
def ccxt_exceptionhandlers(mocker, default_conf, api_mock, fun, mock_ccxt_fun, **kwargs):
|
||||
"""Function to test ccxt exception handling """
|
||||
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
getattr(exchange, fun)(**kwargs)
|
||||
assert api_mock.__dict__[mock_ccxt_fun].call_count == API_RETRY_COUNT + 1
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
getattr(exchange, fun)(**kwargs)
|
||||
assert api_mock.__dict__[mock_ccxt_fun].call_count == 1
|
||||
|
||||
|
||||
def test_init(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
maybe_init_api(default_conf, mocker, True)
|
||||
get_patched_exchange(mocker, default_conf)
|
||||
assert log_has('Instance is running with dry_run enabled', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_init_exception(default_conf):
|
||||
def test_init_exception(default_conf, mocker):
|
||||
default_conf['exchange']['name'] = 'wrong_exchange_name'
|
||||
|
||||
with pytest.raises(
|
||||
OperationalException,
|
||||
match='Exchange {} is not supported'.format(default_conf['exchange']['name'])):
|
||||
init(config=default_conf)
|
||||
Exchange(default_conf)
|
||||
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
with pytest.raises(
|
||||
OperationalException,
|
||||
match='Exchange {} is not supported'.format(default_conf['exchange']['name'])):
|
||||
mocker.patch("ccxt.binance", MagicMock(side_effect=AttributeError))
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_pairs(default_conf, mocker):
|
||||
@@ -50,18 +60,17 @@ def test_validate_pairs(default_conf, mocker):
|
||||
id_mock = PropertyMock(return_value='test_exchange')
|
||||
type(api_mock).id = id_mock
|
||||
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_pairs_not_available(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
api_mock.load_markets = MagicMock(return_value={})
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
|
||||
with pytest.raises(OperationalException, match=r'not available'):
|
||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_pairs_not_compatible(default_conf, mocker):
|
||||
@@ -71,25 +80,27 @@ def test_validate_pairs_not_compatible(default_conf, mocker):
|
||||
})
|
||||
conf = deepcopy(default_conf)
|
||||
conf['stake_currency'] = 'ETH'
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', conf)
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
|
||||
with pytest.raises(OperationalException, match=r'not compatible'):
|
||||
validate_pairs(conf['exchange']['pair_whitelist'])
|
||||
Exchange(conf)
|
||||
|
||||
|
||||
def test_validate_pairs_exception(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
api_mock = MagicMock()
|
||||
api_mock.name = 'Binance'
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='Binance'))
|
||||
|
||||
api_mock.load_markets = MagicMock(return_value={})
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock)
|
||||
|
||||
with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available at Binance'):
|
||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
||||
Exchange(default_conf)
|
||||
|
||||
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError())
|
||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||
Exchange(default_conf)
|
||||
assert log_has('Unable to validate pairs (assuming they are correct). Reason: ',
|
||||
caplog.record_tuples)
|
||||
|
||||
@@ -99,22 +110,35 @@ def test_validate_pairs_stake_exception(default_conf, mocker, caplog):
|
||||
conf = deepcopy(default_conf)
|
||||
conf['stake_currency'] = 'ETH'
|
||||
api_mock = MagicMock()
|
||||
api_mock.name = 'binance'
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', conf)
|
||||
api_mock.name = MagicMock(return_value='binance')
|
||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock)
|
||||
|
||||
with pytest.raises(
|
||||
OperationalException,
|
||||
match=r'Pair ETH/BTC not compatible with stake_currency: ETH'
|
||||
):
|
||||
validate_pairs(default_conf['exchange']['pair_whitelist'])
|
||||
Exchange(conf)
|
||||
|
||||
|
||||
def test_exchangehas(default_conf, mocker):
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
assert not exchange.exchange_has('ASDFASDF')
|
||||
api_mock = MagicMock()
|
||||
|
||||
type(api_mock).has = PropertyMock(return_value={'deadbeef': True})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
assert exchange.exchange_has("deadbeef")
|
||||
|
||||
type(api_mock).has = PropertyMock(return_value={'deadbeef': False})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
assert not exchange.exchange_has("deadbeef")
|
||||
|
||||
|
||||
def test_buy_dry_run(default_conf, mocker):
|
||||
default_conf['dry_run'] = True
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
|
||||
order = buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
order = exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
assert 'id' in order
|
||||
assert 'dry_run_buy_' in order['id']
|
||||
|
||||
@@ -128,12 +152,10 @@ def test_buy_prod(default_conf, mocker):
|
||||
'foo': 'bar'
|
||||
}
|
||||
})
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
order = buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
order = exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
assert 'id' in order
|
||||
assert 'info' in order
|
||||
assert order['id'] == order_id
|
||||
@@ -141,30 +163,30 @@ def test_buy_prod(default_conf, mocker):
|
||||
# test exception handling
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.BaseError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
|
||||
|
||||
|
||||
def test_sell_dry_run(default_conf, mocker):
|
||||
default_conf['dry_run'] = True
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
|
||||
order = sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
order = exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
assert 'id' in order
|
||||
assert 'dry_run_sell_' in order['id']
|
||||
|
||||
@@ -178,12 +200,11 @@ def test_sell_prod(default_conf, mocker):
|
||||
'foo': 'bar'
|
||||
}
|
||||
})
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
|
||||
order = sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
order = exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
assert 'id' in order
|
||||
assert 'info' in order
|
||||
assert order['id'] == order_id
|
||||
@@ -191,53 +212,57 @@ def test_sell_prod(default_conf, mocker):
|
||||
# test exception handling
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.BaseError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
|
||||
|
||||
|
||||
def test_get_balance_dry_run(default_conf, mocker):
|
||||
default_conf['dry_run'] = True
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
|
||||
assert get_balance(currency='BTC') == 999.9
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
assert exchange.get_balance(currency='BTC') == 999.9
|
||||
|
||||
|
||||
def test_get_balance_prod(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}})
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
|
||||
assert get_balance(currency='BTC') == 123.4
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
assert exchange.get_balance(currency='BTC') == 123.4
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
get_balance(currency='BTC')
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
exchange.get_balance(currency='BTC')
|
||||
|
||||
with pytest.raises(TemporaryError, match=r'.*balance due to malformed exchange response:.*'):
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', MagicMock(return_value={}))
|
||||
exchange.get_balance(currency='BTC')
|
||||
|
||||
|
||||
def test_get_balances_dry_run(default_conf, mocker):
|
||||
default_conf['dry_run'] = True
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
|
||||
assert get_balances() == {}
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
assert exchange.get_balances() == {}
|
||||
|
||||
|
||||
def test_get_balances_prod(default_conf, mocker):
|
||||
@@ -253,33 +278,57 @@ def test_get_balances_prod(default_conf, mocker):
|
||||
'2ST': balance_item,
|
||||
'3ST': balance_item
|
||||
})
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
assert len(exchange.get_balances()) == 3
|
||||
assert exchange.get_balances()['1ST']['free'] == 10.0
|
||||
assert exchange.get_balances()['1ST']['total'] == 10.0
|
||||
assert exchange.get_balances()['1ST']['used'] == 0.0
|
||||
|
||||
assert len(get_balances()) == 3
|
||||
assert get_balances()['1ST']['free'] == 10.0
|
||||
assert get_balances()['1ST']['total'] == 10.0
|
||||
assert get_balances()['1ST']['used'] == 0.0
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
"get_balances", "fetch_balance")
|
||||
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.fetch_balance = MagicMock(side_effect=ccxt.NetworkError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
get_balances()
|
||||
assert api_mock.fetch_balance.call_count == exchange.API_RETRY_COUNT + 1
|
||||
|
||||
def test_get_tickers(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
tick = {'ETH/BTC': {
|
||||
'symbol': 'ETH/BTC',
|
||||
'bid': 0.5,
|
||||
'ask': 1,
|
||||
'last': 42,
|
||||
}, 'BCH/BTC': {
|
||||
'symbol': 'BCH/BTC',
|
||||
'bid': 0.6,
|
||||
'ask': 0.5,
|
||||
'last': 41,
|
||||
}
|
||||
}
|
||||
api_mock.fetch_tickers = MagicMock(return_value=tick)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
# retrieve original ticker
|
||||
tickers = exchange.get_tickers()
|
||||
|
||||
assert 'ETH/BTC' in tickers
|
||||
assert 'BCH/BTC' in tickers
|
||||
assert tickers['ETH/BTC']['bid'] == 0.5
|
||||
assert tickers['ETH/BTC']['ask'] == 1
|
||||
assert tickers['BCH/BTC']['bid'] == 0.6
|
||||
assert tickers['BCH/BTC']['ask'] == 0.5
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
"get_tickers", "fetch_tickers")
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
get_balances()
|
||||
assert api_mock.fetch_balance.call_count == 1
|
||||
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NotSupported)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.get_tickers()
|
||||
|
||||
api_mock.fetch_tickers = MagicMock(return_value={})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.get_tickers()
|
||||
|
||||
|
||||
# This test is somewhat redundant with
|
||||
# test_exchange_bittrex.py::test_exchange_bittrex_get_ticker
|
||||
def test_get_ticker(default_conf, mocker):
|
||||
maybe_init_api(default_conf, mocker)
|
||||
api_mock = MagicMock()
|
||||
tick = {
|
||||
'symbol': 'ETH/BTC',
|
||||
@@ -288,10 +337,9 @@ def test_get_ticker(default_conf, mocker):
|
||||
'last': 0.0001,
|
||||
}
|
||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
# retrieve original ticker
|
||||
ticker = get_ticker(pair='ETH/BTC')
|
||||
ticker = exchange.get_ticker(pair='ETH/BTC')
|
||||
|
||||
assert ticker['bid'] == 0.00001098
|
||||
assert ticker['ask'] == 0.00001099
|
||||
@@ -304,38 +352,32 @@ def test_get_ticker(default_conf, mocker):
|
||||
'last': 42,
|
||||
}
|
||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
# if not caching the result we should get the same ticker
|
||||
# if not fetching a new result we should get the cached ticker
|
||||
ticker = get_ticker(pair='ETH/BTC')
|
||||
ticker = exchange.get_ticker(pair='ETH/BTC')
|
||||
|
||||
assert api_mock.fetch_ticker.call_count == 1
|
||||
assert ticker['bid'] == 0.5
|
||||
assert ticker['ask'] == 1
|
||||
|
||||
assert 'ETH/BTC' in exchange._CACHED_TICKER
|
||||
assert exchange._CACHED_TICKER['ETH/BTC']['bid'] == 0.5
|
||||
assert exchange._CACHED_TICKER['ETH/BTC']['ask'] == 1
|
||||
assert 'ETH/BTC' in exchange._cached_ticker
|
||||
assert exchange._cached_ticker['ETH/BTC']['bid'] == 0.5
|
||||
assert exchange._cached_ticker['ETH/BTC']['ask'] == 1
|
||||
|
||||
# Test caching
|
||||
api_mock.fetch_ticker = MagicMock()
|
||||
get_ticker(pair='ETH/BTC', refresh=False)
|
||||
exchange.get_ticker(pair='ETH/BTC', refresh=False)
|
||||
assert api_mock.fetch_ticker.call_count == 0
|
||||
|
||||
with pytest.raises(TemporaryError): # test retrier
|
||||
api_mock.fetch_ticker = MagicMock(side_effect=ccxt.NetworkError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
get_ticker(pair='ETH/BTC', refresh=True)
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_ticker = MagicMock(side_effect=ccxt.BaseError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
get_ticker(pair='ETH/BTC', refresh=True)
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
"get_ticker", "fetch_ticker",
|
||||
pair='ETH/BTC', refresh=True)
|
||||
|
||||
api_mock.fetch_ticker = MagicMock(return_value={})
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
get_ticker(pair='ETH/BTC', refresh=True)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
||||
|
||||
|
||||
def make_fetch_ohlcv_mock(data):
|
||||
@@ -361,10 +403,10 @@ def test_get_ticker_history(default_conf, mocker):
|
||||
]
|
||||
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
||||
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
# retrieve original ticker
|
||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
ticks = exchange.get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
assert ticks[0][0] == 1511686200000
|
||||
assert ticks[0][1] == 1
|
||||
assert ticks[0][2] == 2
|
||||
@@ -384,9 +426,9 @@ def test_get_ticker_history(default_conf, mocker):
|
||||
]
|
||||
]
|
||||
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(new_tick))
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
ticks = exchange.get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
assert ticks[0][0] == 1511686210000
|
||||
assert ticks[0][1] == 6
|
||||
assert ticks[0][2] == 7
|
||||
@@ -394,17 +436,14 @@ def test_get_ticker_history(default_conf, mocker):
|
||||
assert ticks[0][4] == 9
|
||||
assert ticks[0][5] == 10
|
||||
|
||||
with pytest.raises(TemporaryError): # test retrier
|
||||
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NetworkError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
# new symbol to get around cache
|
||||
get_ticker_history('ABCD/BTC', default_conf['ticker_interval'])
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
"get_ticker_history", "fetch_ohlcv",
|
||||
pair='ABCD/BTC', tick_interval=default_conf['ticker_interval'])
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
# new symbol to get around cache
|
||||
get_ticker_history('EFGH/BTC', default_conf['ticker_interval'])
|
||||
with pytest.raises(OperationalException, match=r'Exchange .* does not support.*'):
|
||||
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NotSupported)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.get_ticker_history(pair='ABCD/BTC', tick_interval=default_conf['ticker_interval'])
|
||||
|
||||
|
||||
def test_get_ticker_history_sort(default_conf, mocker):
|
||||
@@ -426,10 +465,11 @@ def test_get_ticker_history_sort(default_conf, mocker):
|
||||
]
|
||||
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
||||
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
# Test the ticker history sort
|
||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
ticks = exchange.get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
assert ticks[0][0] == 1527830400000
|
||||
assert ticks[0][1] == 0.07649
|
||||
assert ticks[0][2] == 0.07651
|
||||
@@ -460,10 +500,9 @@ def test_get_ticker_history_sort(default_conf, mocker):
|
||||
]
|
||||
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
|
||||
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
# Test the ticker history sort
|
||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
ticks = exchange.get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
assert ticks[0][0] == 1527827700000
|
||||
assert ticks[0][1] == 0.07659999
|
||||
assert ticks[0][2] == 0.0766
|
||||
@@ -481,117 +520,159 @@ def test_get_ticker_history_sort(default_conf, mocker):
|
||||
|
||||
def test_cancel_order_dry_run(default_conf, mocker):
|
||||
default_conf['dry_run'] = True
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
|
||||
assert cancel_order(order_id='123', pair='TKN/BTC') is None
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
assert exchange.cancel_order(order_id='123', pair='TKN/BTC') is None
|
||||
|
||||
|
||||
# Ensure that if not dry_run, we should call API
|
||||
def test_cancel_order(default_conf, mocker):
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
api_mock = MagicMock()
|
||||
api_mock.cancel_order = MagicMock(return_value=123)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
assert cancel_order(order_id='_', pair='TKN/BTC') == 123
|
||||
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.cancel_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
cancel_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.cancel_order.call_count == exchange.API_RETRY_COUNT + 1
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == 123
|
||||
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
cancel_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.cancel_order.call_count == exchange.API_RETRY_COUNT + 1
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.cancel_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.cancel_order.call_count == API_RETRY_COUNT + 1
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.cancel_order = MagicMock(side_effect=ccxt.BaseError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
cancel_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.cancel_order.call_count == 1
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
"cancel_order", "cancel_order",
|
||||
order_id='_', pair='TKN/BTC')
|
||||
|
||||
|
||||
def test_get_order(default_conf, mocker):
|
||||
default_conf['dry_run'] = True
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
order = MagicMock()
|
||||
order.myid = 123
|
||||
exchange._DRY_RUN_OPEN_ORDERS['X'] = order
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
exchange._dry_run_open_orders['X'] = order
|
||||
print(exchange.get_order('X', 'TKN/BTC'))
|
||||
assert exchange.get_order('X', 'TKN/BTC').myid == 123
|
||||
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_order = MagicMock(return_value=456)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
assert exchange.get_order('X', 'TKN/BTC') == 456
|
||||
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.fetch_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
exchange.get_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.fetch_order.call_count == exchange.API_RETRY_COUNT + 1
|
||||
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange.get_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.fetch_order.call_count == exchange.API_RETRY_COUNT + 1
|
||||
assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_order = MagicMock(side_effect=ccxt.BaseError)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
exchange.get_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.fetch_order.call_count == 1
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
'get_order', 'fetch_order',
|
||||
order_id='_', pair='TKN/BTC')
|
||||
|
||||
|
||||
def test_get_name(default_conf, mocker):
|
||||
mocker.patch('freqtrade.exchange.validate_pairs',
|
||||
def test_name(default_conf, mocker):
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs',
|
||||
side_effect=lambda s: True)
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
init(default_conf)
|
||||
exchange = Exchange(default_conf)
|
||||
|
||||
assert get_name() == 'Binance'
|
||||
assert exchange.name == 'Binance'
|
||||
|
||||
|
||||
def test_get_id(default_conf, mocker):
|
||||
mocker.patch('freqtrade.exchange.validate_pairs',
|
||||
def test_id(default_conf, mocker):
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs',
|
||||
side_effect=lambda s: True)
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
init(default_conf)
|
||||
|
||||
assert get_id() == 'binance'
|
||||
exchange = Exchange(default_conf)
|
||||
assert exchange.id == 'binance'
|
||||
|
||||
|
||||
def test_get_pair_detail_url(default_conf, mocker):
|
||||
mocker.patch('freqtrade.exchange.validate_pairs',
|
||||
def test_get_pair_detail_url(default_conf, mocker, caplog):
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs',
|
||||
side_effect=lambda s: True)
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
init(default_conf)
|
||||
exchange = Exchange(default_conf)
|
||||
|
||||
url = get_pair_detail_url('TKN/ETH')
|
||||
url = exchange.get_pair_detail_url('TKN/ETH')
|
||||
assert 'TKN' in url
|
||||
assert 'ETH' in url
|
||||
|
||||
url = get_pair_detail_url('LOOONG/BTC')
|
||||
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||
assert 'LOOONG' in url
|
||||
assert 'BTC' in url
|
||||
|
||||
default_conf['exchange']['name'] = 'bittrex'
|
||||
init(default_conf)
|
||||
exchange = Exchange(default_conf)
|
||||
|
||||
url = get_pair_detail_url('TKN/ETH')
|
||||
url = exchange.get_pair_detail_url('TKN/ETH')
|
||||
assert 'TKN' in url
|
||||
assert 'ETH' in url
|
||||
|
||||
url = get_pair_detail_url('LOOONG/BTC')
|
||||
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||
assert 'LOOONG' in url
|
||||
assert 'BTC' in url
|
||||
|
||||
default_conf['exchange']['name'] = 'poloniex'
|
||||
exchange = Exchange(default_conf)
|
||||
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||
assert '' == url
|
||||
assert log_has('Could not get exchange url for Poloniex', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_trades_for_order(default_conf, mocker):
|
||||
order_id = 'ABCD-ABCD'
|
||||
since = datetime(2018, 5, 5)
|
||||
default_conf["dry_run"] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
||||
api_mock = MagicMock()
|
||||
|
||||
api_mock.fetch_my_trades = MagicMock(return_value=[{'id': 'TTR67E-3PFBD-76IISV',
|
||||
'order': 'ABCD-ABCD',
|
||||
'info': {'pair': 'XLTCZBTC',
|
||||
'time': 1519860024.4388,
|
||||
'type': 'buy',
|
||||
'ordertype': 'limit',
|
||||
'price': '20.00000',
|
||||
'cost': '38.62000',
|
||||
'fee': '0.06179',
|
||||
'vol': '5',
|
||||
'id': 'ABCD-ABCD'},
|
||||
'timestamp': 1519860024438,
|
||||
'datetime': '2018-02-28T23:20:24.438Z',
|
||||
'symbol': 'LTC/BTC',
|
||||
'type': 'limit',
|
||||
'side': 'buy',
|
||||
'price': 165.0,
|
||||
'amount': 0.2340606,
|
||||
'fee': {'cost': 0.06179, 'currency': 'BTC'}
|
||||
}])
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
orders = exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
|
||||
assert len(orders) == 1
|
||||
assert orders[0]['price'] == 165
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
'get_trades_for_order', 'fetch_my_trades',
|
||||
order_id=order_id, pair='LTC/BTC', since=since)
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=False))
|
||||
assert exchange.get_trades_for_order(order_id, 'LTC/BTC', since) == []
|
||||
|
||||
|
||||
def test_get_markets(default_conf, mocker, markets):
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_markets = markets
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
ret = exchange.get_markets()
|
||||
assert isinstance(ret, list)
|
||||
assert len(ret) == 6
|
||||
|
||||
assert ret[0]["id"] == "ethbtc"
|
||||
assert ret[0]["symbol"] == "ETH/BTC"
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
'get_markets', 'fetch_markets')
|
||||
|
||||
|
||||
def test_get_fee(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
@@ -601,12 +682,21 @@ def test_get_fee(default_conf, mocker):
|
||||
'rate': 0.025,
|
||||
'cost': 0.05
|
||||
})
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
assert get_fee() == 0.025
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
assert exchange.get_fee() == 0.025
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
'get_fee', 'calculate_fee')
|
||||
|
||||
|
||||
def test_get_amount_lots(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
api_mock.amount_to_lots = MagicMock(return_value=1.0)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
assert get_amount_lots('LTC/BTC', 1.54) == 1
|
||||
api_mock.markets = None
|
||||
marketmock = MagicMock()
|
||||
api_mock.load_markets = marketmock
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
|
||||
assert exchange.get_amount_lots('LTC/BTC', 1.54) == 1
|
||||
assert marketmock.call_count == 1
|
||||
|
||||
@@ -9,13 +9,15 @@ from unittest.mock import MagicMock
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from arrow import Arrow
|
||||
|
||||
from freqtrade import optimize
|
||||
from freqtrade import DependencyException, constants, optimize
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.arguments import Arguments, TimeRange
|
||||
from freqtrade.optimize.backtesting import Backtesting, start, setup_configuration
|
||||
from freqtrade.tests.conftest import log_has
|
||||
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
|
||||
start)
|
||||
from freqtrade.tests.conftest import log_has, patch_exchange
|
||||
|
||||
|
||||
def get_args(args) -> List[str]:
|
||||
@@ -83,7 +85,7 @@ def load_data_test(what):
|
||||
|
||||
|
||||
def simple_backtest(config, contour, num_results, mocker) -> None:
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(config)
|
||||
|
||||
data = load_data_test(contour)
|
||||
@@ -101,7 +103,8 @@ def simple_backtest(config, contour, num_results, mocker) -> None:
|
||||
assert len(results) == num_results
|
||||
|
||||
|
||||
def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=False, timerange=None):
|
||||
def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=False,
|
||||
timerange=None, exchange=None):
|
||||
tickerdata = optimize.load_tickerdata_file(datadir, 'UNITTEST/BTC', '1m', timerange=timerange)
|
||||
pairdata = {'UNITTEST/BTC': tickerdata}
|
||||
return pairdata
|
||||
@@ -118,7 +121,7 @@ def _load_pair_as_ticks(pair, tickfreq):
|
||||
def _make_backtest_conf(mocker, conf=None, pair='UNITTEST/BTC', record=None):
|
||||
data = optimize.load_data(None, ticker_interval='8m', pairs=[pair])
|
||||
data = trim_dictlist(data, -201)
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(conf)
|
||||
return {
|
||||
'stake_amount': conf['stake_amount'],
|
||||
@@ -267,13 +270,35 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
|
||||
)
|
||||
|
||||
|
||||
def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None:
|
||||
"""
|
||||
Test setup_configuration() function
|
||||
"""
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
||||
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(conf)
|
||||
))
|
||||
|
||||
args = [
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'DefaultStrategy',
|
||||
'backtesting'
|
||||
]
|
||||
|
||||
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
||||
setup_configuration(get_args(args))
|
||||
|
||||
|
||||
def test_start(mocker, fee, default_conf, caplog) -> None:
|
||||
"""
|
||||
Test start() function
|
||||
"""
|
||||
start_mock = MagicMock()
|
||||
mocker.patch('freqtrade.exchange.get_fee', fee)
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
patch_exchange(mocker)
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.start', start_mock)
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(default_conf)
|
||||
@@ -296,7 +321,8 @@ def test_backtesting_init(mocker, default_conf) -> None:
|
||||
"""
|
||||
Test Backtesting._init() method
|
||||
"""
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
patch_exchange(mocker)
|
||||
get_fee = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
|
||||
backtesting = Backtesting(default_conf)
|
||||
assert backtesting.config == default_conf
|
||||
assert isinstance(backtesting.analyze, Analyze)
|
||||
@@ -304,13 +330,15 @@ def test_backtesting_init(mocker, default_conf) -> None:
|
||||
assert callable(backtesting.tickerdata_to_dataframe)
|
||||
assert callable(backtesting.populate_buy_trend)
|
||||
assert callable(backtesting.populate_sell_trend)
|
||||
get_fee.assert_called()
|
||||
assert backtesting.fee == 0.5
|
||||
|
||||
|
||||
def test_tickerdata_to_dataframe(default_conf, mocker) -> None:
|
||||
"""
|
||||
Test Backtesting.tickerdata_to_dataframe() method
|
||||
"""
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
patch_exchange(mocker)
|
||||
timerange = TimeRange(None, 'line', 0, -100)
|
||||
tick = optimize.load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
|
||||
tickerlist = {'UNITTEST/BTC': tick}
|
||||
@@ -329,7 +357,7 @@ def test_get_timeframe(default_conf, mocker) -> None:
|
||||
"""
|
||||
Test Backtesting.get_timeframe() method
|
||||
"""
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
data = backtesting.tickerdata_to_dataframe(
|
||||
@@ -348,15 +376,15 @@ def test_generate_text_table(default_conf, mocker):
|
||||
"""
|
||||
Test Backtesting.generate_text_table() method
|
||||
"""
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
'currency': ['ETH/BTC', 'ETH/BTC'],
|
||||
'pair': ['ETH/BTC', 'ETH/BTC'],
|
||||
'profit_percent': [0.1, 0.2],
|
||||
'profit_BTC': [0.2, 0.4],
|
||||
'duration': [10, 30],
|
||||
'profit_abs': [0.2, 0.4],
|
||||
'trade_duration': [10, 30],
|
||||
'profit': [2, 0],
|
||||
'loss': [0, 0]
|
||||
}
|
||||
@@ -385,8 +413,8 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None:
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.load_data', mocked_load_data)
|
||||
mocker.patch('freqtrade.exchange.get_ticker_history')
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.optimize.backtesting.Backtesting',
|
||||
backtest=MagicMock(),
|
||||
@@ -426,8 +454,8 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.load_data', MagicMock(return_value={}))
|
||||
mocker.patch('freqtrade.exchange.get_ticker_history')
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.optimize.backtesting.Backtesting',
|
||||
backtest=MagicMock(),
|
||||
@@ -454,8 +482,8 @@ def test_backtest(default_conf, fee, mocker) -> None:
|
||||
"""
|
||||
Test Backtesting.backtest() method
|
||||
"""
|
||||
mocker.patch('freqtrade.exchange.get_fee', fee)
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
data = optimize.load_data(None, ticker_interval='5m', pairs=['UNITTEST/BTC'])
|
||||
@@ -469,14 +497,15 @@ def test_backtest(default_conf, fee, mocker) -> None:
|
||||
}
|
||||
)
|
||||
assert not results.empty
|
||||
assert len(results) == 2
|
||||
|
||||
|
||||
def test_backtest_1min_ticker_interval(default_conf, fee, mocker) -> None:
|
||||
"""
|
||||
Test Backtesting.backtest() method with 1 min ticker
|
||||
"""
|
||||
mocker.patch('freqtrade.exchange.get_fee', fee)
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
# Run a backtesting for an exiting 5min ticker_interval
|
||||
@@ -491,13 +520,14 @@ def test_backtest_1min_ticker_interval(default_conf, fee, mocker) -> None:
|
||||
}
|
||||
)
|
||||
assert not results.empty
|
||||
assert len(results) == 1
|
||||
|
||||
|
||||
def test_processed(default_conf, mocker) -> None:
|
||||
"""
|
||||
Test Backtesting.backtest() method with offline data
|
||||
"""
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
dict_of_tickerrows = load_data_test('raise')
|
||||
@@ -511,16 +541,16 @@ def test_processed(default_conf, mocker) -> None:
|
||||
|
||||
|
||||
def test_backtest_pricecontours(default_conf, fee, mocker) -> None:
|
||||
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
|
||||
tests = [['raise', 17], ['lower', 0], ['sine', 16]]
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
tests = [['raise', 18], ['lower', 0], ['sine', 16]]
|
||||
for [contour, numres] in tests:
|
||||
simple_backtest(default_conf, contour, numres, mocker)
|
||||
|
||||
|
||||
# Test backtest using offline data (testdata directory)
|
||||
def test_backtest_ticks(default_conf, fee, mocker):
|
||||
mocker.patch('freqtrade.exchange.get_fee', fee)
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
patch_exchange(mocker)
|
||||
ticks = [1, 5]
|
||||
fun = Backtesting(default_conf).populate_buy_trend
|
||||
for _ in ticks:
|
||||
@@ -539,7 +569,6 @@ def test_backtest_clash_buy_sell(mocker, default_conf):
|
||||
sell_value = 1
|
||||
return _trend(dataframe, buy_value, sell_value)
|
||||
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting.populate_buy_trend = fun # Override
|
||||
@@ -555,7 +584,6 @@ def test_backtest_only_sell(mocker, default_conf):
|
||||
sell_value = 1
|
||||
return _trend(dataframe, buy_value, sell_value)
|
||||
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting.populate_buy_trend = fun # Override
|
||||
@@ -565,50 +593,68 @@ def test_backtest_only_sell(mocker, default_conf):
|
||||
|
||||
|
||||
def test_backtest_alternate_buy_sell(default_conf, fee, mocker):
|
||||
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC')
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting.populate_buy_trend = _trend_alternate # Override
|
||||
backtesting.populate_sell_trend = _trend_alternate # Override
|
||||
results = backtesting.backtest(backtest_conf)
|
||||
assert len(results) == 3
|
||||
backtesting._store_backtest_result("test_.json", results)
|
||||
assert len(results) == 4
|
||||
# One trade was force-closed at the end
|
||||
assert len(results.loc[results.open_at_end]) == 1
|
||||
|
||||
|
||||
def test_backtest_record(default_conf, fee, mocker):
|
||||
names = []
|
||||
records = []
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
|
||||
patch_exchange(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch(
|
||||
'freqtrade.optimize.backtesting.file_dump_json',
|
||||
new=lambda n, r: (names.append(n), records.append(r))
|
||||
)
|
||||
backtest_conf = _make_backtest_conf(
|
||||
mocker,
|
||||
conf=default_conf,
|
||||
pair='UNITTEST/BTC',
|
||||
record="trades"
|
||||
)
|
||||
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting.populate_buy_trend = _trend_alternate # Override
|
||||
backtesting.populate_sell_trend = _trend_alternate # Override
|
||||
results = backtesting.backtest(backtest_conf)
|
||||
assert len(results) == 3
|
||||
results = pd.DataFrame({"pair": ["UNITTEST/BTC", "UNITTEST/BTC",
|
||||
"UNITTEST/BTC", "UNITTEST/BTC"],
|
||||
"profit_percent": [0.003312, 0.010801, 0.013803, 0.002780],
|
||||
"profit_abs": [0.000003, 0.000011, 0.000014, 0.000003],
|
||||
"open_time": [Arrow(2017, 11, 14, 19, 32, 00).datetime,
|
||||
Arrow(2017, 11, 14, 21, 36, 00).datetime,
|
||||
Arrow(2017, 11, 14, 22, 12, 00).datetime,
|
||||
Arrow(2017, 11, 14, 22, 44, 00).datetime],
|
||||
"close_time": [Arrow(2017, 11, 14, 21, 35, 00).datetime,
|
||||
Arrow(2017, 11, 14, 22, 10, 00).datetime,
|
||||
Arrow(2017, 11, 14, 22, 43, 00).datetime,
|
||||
Arrow(2017, 11, 14, 22, 58, 00).datetime],
|
||||
"open_rate": [0.002543, 0.003003, 0.003089, 0.003214],
|
||||
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
|
||||
"open_index": [1, 119, 153, 185],
|
||||
"close_index": [118, 151, 184, 199],
|
||||
"trade_duration": [123, 34, 31, 14],
|
||||
"open_at_end": [False, False, False, True]
|
||||
})
|
||||
backtesting._store_backtest_result("backtest-result.json", results)
|
||||
assert len(results) == 4
|
||||
# Assert file_dump_json was only called once
|
||||
assert names == ['backtest-result.json']
|
||||
records = records[0]
|
||||
# Ensure records are of correct type
|
||||
assert len(records) == 3
|
||||
assert len(records) == 4
|
||||
# ('UNITTEST/BTC', 0.00331158, '1510684320', '1510691700', 0, 117)
|
||||
# Below follows just a typecheck of the schema/type of trade-records
|
||||
oix = None
|
||||
for (pair, profit, date_buy, date_sell, buy_index, dur) in records:
|
||||
for (pair, profit, date_buy, date_sell, buy_index, dur,
|
||||
openr, closer, open_at_end) in records:
|
||||
assert pair == 'UNITTEST/BTC'
|
||||
isinstance(profit, float)
|
||||
assert isinstance(profit, float)
|
||||
# FIX: buy/sell should be converted to ints
|
||||
isinstance(date_buy, str)
|
||||
isinstance(date_sell, str)
|
||||
assert isinstance(date_buy, float)
|
||||
assert isinstance(date_sell, float)
|
||||
assert isinstance(openr, float)
|
||||
assert isinstance(closer, float)
|
||||
assert isinstance(open_at_end, bool)
|
||||
isinstance(buy_index, pd._libs.tslib.Timestamp)
|
||||
if oix:
|
||||
assert buy_index > oix
|
||||
@@ -619,9 +665,9 @@ def test_backtest_record(default_conf, fee, mocker):
|
||||
def test_backtest_start_live(default_conf, mocker, caplog):
|
||||
conf = deepcopy(default_conf)
|
||||
conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
|
||||
mocker.patch('freqtrade.exchange.get_ticker_history',
|
||||
new=lambda n, i: _load_pair_as_ticks(n, i))
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history',
|
||||
new=lambda s, n, i: _load_pair_as_ticks(n, i))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock())
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# pragma pylint: disable=missing-docstring,W0212,C0103
|
||||
import os
|
||||
import signal
|
||||
from copy import deepcopy
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
@@ -10,7 +9,7 @@ import pytest
|
||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||
from freqtrade.optimize.hyperopt import Hyperopt, start
|
||||
from freqtrade.strategy.resolver import StrategyResolver
|
||||
from freqtrade.tests.conftest import log_has
|
||||
from freqtrade.tests.conftest import log_has, patch_exchange
|
||||
from freqtrade.tests.optimize.test_backtesting import get_args
|
||||
|
||||
# Avoid to reinit the same object again and again
|
||||
@@ -22,10 +21,7 @@ _HYPEROPT = None
|
||||
def init_hyperopt(default_conf, mocker):
|
||||
global _HYPEROPT_INITIALIZED, _HYPEROPT
|
||||
if not _HYPEROPT_INITIALIZED:
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf',
|
||||
MagicMock(return_value=default_conf))
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
||||
patch_exchange(mocker)
|
||||
_HYPEROPT = Hyperopt(default_conf)
|
||||
_HYPEROPT_INITIALIZED = True
|
||||
|
||||
@@ -43,30 +39,22 @@ def create_trials(mocker) -> None:
|
||||
mocker.patch('freqtrade.optimize.hyperopt.os.path.exists', return_value=False)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.os.path.getsize', return_value=1)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.os.remove', return_value=True)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.pickle.dump', return_value=None)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.dump', return_value=None)
|
||||
|
||||
return mocker.Mock(
|
||||
results=[
|
||||
{
|
||||
'loss': 1,
|
||||
'result': 'foo',
|
||||
'status': 'ok'
|
||||
}
|
||||
],
|
||||
best_trial={'misc': {'vals': {'adx': 999}}}
|
||||
)
|
||||
return [{'loss': 1, 'result': 'foo', 'params': {}}]
|
||||
|
||||
|
||||
# Unit tests
|
||||
def test_start(mocker, default_conf, caplog) -> None:
|
||||
"""
|
||||
Test start() function
|
||||
"""
|
||||
start_mock = MagicMock()
|
||||
mocker.patch(
|
||||
'freqtrade.configuration.Configuration._load_config_file',
|
||||
lambda *args, **kwargs: default_conf
|
||||
)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf',
|
||||
MagicMock(return_value=default_conf))
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
||||
patch_exchange(mocker)
|
||||
|
||||
args = [
|
||||
'--config', 'config.json',
|
||||
@@ -149,159 +137,18 @@ def test_no_log_if_loss_does_not_improve(init_hyperopt, caplog) -> None:
|
||||
assert caplog.record_tuples == []
|
||||
|
||||
|
||||
def test_fmin_best_results(mocker, init_hyperopt, default_conf, caplog) -> None:
|
||||
fmin_result = {
|
||||
"macd_below_zero": 0,
|
||||
"adx": 1,
|
||||
"adx-value": 15.0,
|
||||
"fastd": 1,
|
||||
"fastd-value": 40.0,
|
||||
"green_candle": 1,
|
||||
"mfi": 0,
|
||||
"over_sar": 0,
|
||||
"rsi": 1,
|
||||
"rsi-value": 37.0,
|
||||
"trigger": 2,
|
||||
"uptrend_long_ema": 1,
|
||||
"uptrend_short_ema": 0,
|
||||
"uptrend_sma": 0,
|
||||
"stoploss": -0.1,
|
||||
"roi_t1": 1,
|
||||
"roi_t2": 2,
|
||||
"roi_t3": 3,
|
||||
"roi_p1": 1,
|
||||
"roi_p2": 2,
|
||||
"roi_p3": 3,
|
||||
}
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf.update({'config': 'config.json.example'})
|
||||
conf.update({'epochs': 1})
|
||||
conf.update({'timerange': None})
|
||||
conf.update({'spaces': 'all'})
|
||||
|
||||
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value=fmin_result)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
||||
|
||||
StrategyResolver({'strategy': 'DefaultStrategy'})
|
||||
hyperopt = Hyperopt(conf)
|
||||
hyperopt.trials = create_trials(mocker)
|
||||
hyperopt.tickerdata_to_dataframe = MagicMock()
|
||||
hyperopt.start()
|
||||
|
||||
exists = [
|
||||
'Best parameters:',
|
||||
'"adx": {\n "enabled": true,\n "value": 15.0\n },',
|
||||
'"fastd": {\n "enabled": true,\n "value": 40.0\n },',
|
||||
'"green_candle": {\n "enabled": true\n },',
|
||||
'"macd_below_zero": {\n "enabled": false\n },',
|
||||
'"mfi": {\n "enabled": false\n },',
|
||||
'"over_sar": {\n "enabled": false\n },',
|
||||
'"roi_p1": 1.0,',
|
||||
'"roi_p2": 2.0,',
|
||||
'"roi_p3": 3.0,',
|
||||
'"roi_t1": 1.0,',
|
||||
'"roi_t2": 2.0,',
|
||||
'"roi_t3": 3.0,',
|
||||
'"rsi": {\n "enabled": true,\n "value": 37.0\n },',
|
||||
'"stoploss": -0.1,',
|
||||
'"trigger": {\n "type": "faststoch10"\n },',
|
||||
'"uptrend_long_ema": {\n "enabled": true\n },',
|
||||
'"uptrend_short_ema": {\n "enabled": false\n },',
|
||||
'"uptrend_sma": {\n "enabled": false\n }',
|
||||
'ROI table:\n{0: 6.0, 3.0: 3.0, 5.0: 1.0, 6.0: 0}',
|
||||
'Best Result:\nfoo'
|
||||
]
|
||||
for line in exists:
|
||||
assert line in caplog.text
|
||||
|
||||
|
||||
def test_fmin_throw_value_error(mocker, init_hyperopt, default_conf, caplog) -> None:
|
||||
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.hyperopt.fmin', side_effect=ValueError())
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf.update({'config': 'config.json.example'})
|
||||
conf.update({'epochs': 1})
|
||||
conf.update({'timerange': None})
|
||||
conf.update({'spaces': 'all'})
|
||||
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
||||
|
||||
StrategyResolver({'strategy': 'DefaultStrategy'})
|
||||
hyperopt = Hyperopt(conf)
|
||||
hyperopt.trials = create_trials(mocker)
|
||||
hyperopt.tickerdata_to_dataframe = MagicMock()
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
exists = [
|
||||
'Best Result:',
|
||||
'Sorry, Hyperopt was not able to find good parameters. Please try with more epochs '
|
||||
'(param: -e).',
|
||||
]
|
||||
|
||||
for line in exists:
|
||||
assert line in caplog.text
|
||||
|
||||
|
||||
def test_resuming_previous_hyperopt_results_succeeds(mocker, init_hyperopt, default_conf) -> None:
|
||||
trials = create_trials(mocker)
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf.update({'config': 'config.json.example'})
|
||||
conf.update({'epochs': 1})
|
||||
conf.update({'mongodb': False})
|
||||
conf.update({'timerange': None})
|
||||
conf.update({'spaces': 'all'})
|
||||
|
||||
mocker.patch('freqtrade.optimize.hyperopt.os.path.exists', return_value=True)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.len', return_value=len(trials.results))
|
||||
mock_read = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.read_trials',
|
||||
return_value=trials
|
||||
)
|
||||
mock_save = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.save_trials',
|
||||
return_value=None
|
||||
)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
||||
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
||||
|
||||
StrategyResolver({'strategy': 'DefaultStrategy'})
|
||||
hyperopt = Hyperopt(conf)
|
||||
hyperopt.trials = trials
|
||||
hyperopt.tickerdata_to_dataframe = MagicMock()
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
mock_read.assert_called_once()
|
||||
mock_save.assert_called_once()
|
||||
|
||||
current_tries = hyperopt.current_tries
|
||||
total_tries = hyperopt.total_tries
|
||||
|
||||
assert current_tries == len(trials.results)
|
||||
assert total_tries == (current_tries + len(trials.results))
|
||||
|
||||
|
||||
def test_save_trials_saves_trials(mocker, init_hyperopt, caplog) -> None:
|
||||
create_trials(mocker)
|
||||
mock_dump = mocker.patch('freqtrade.optimize.hyperopt.pickle.dump', return_value=None)
|
||||
trials = create_trials(mocker)
|
||||
mock_dump = mocker.patch('freqtrade.optimize.hyperopt.dump', return_value=None)
|
||||
|
||||
hyperopt = _HYPEROPT
|
||||
mocker.patch('freqtrade.optimize.hyperopt.open', return_value=hyperopt.trials_file)
|
||||
_HYPEROPT.trials = trials
|
||||
|
||||
hyperopt.save_trials()
|
||||
|
||||
trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
|
||||
assert log_has(
|
||||
'Saving Trials to \'{}\''.format(trials_file),
|
||||
'Saving 1 evaluations to \'{}\''.format(trials_file),
|
||||
caplog.record_tuples
|
||||
)
|
||||
mock_dump.assert_called_once()
|
||||
@@ -309,8 +156,7 @@ def test_save_trials_saves_trials(mocker, init_hyperopt, caplog) -> None:
|
||||
|
||||
def test_read_trials_returns_trials_file(mocker, init_hyperopt, caplog) -> None:
|
||||
trials = create_trials(mocker)
|
||||
mock_load = mocker.patch('freqtrade.optimize.hyperopt.pickle.load', return_value=trials)
|
||||
mock_open = mocker.patch('freqtrade.optimize.hyperopt.open', return_value=mock_load)
|
||||
mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=trials)
|
||||
|
||||
hyperopt = _HYPEROPT
|
||||
hyperopt_trial = hyperopt.read_trials()
|
||||
@@ -320,7 +166,6 @@ def test_read_trials_returns_trials_file(mocker, init_hyperopt, caplog) -> None:
|
||||
caplog.record_tuples
|
||||
)
|
||||
assert hyperopt_trial == trials
|
||||
mock_open.assert_called_once()
|
||||
mock_load.assert_called_once()
|
||||
|
||||
|
||||
@@ -338,56 +183,31 @@ def test_roi_table_generation(init_hyperopt) -> None:
|
||||
assert hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0}
|
||||
|
||||
|
||||
def test_start_calls_fmin(mocker, init_hyperopt, default_conf) -> None:
|
||||
trials = create_trials(mocker)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results)
|
||||
def test_start_calls_optimizer(mocker, init_hyperopt, default_conf, caplog) -> None:
|
||||
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
||||
mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf.update({'config': 'config.json.example'})
|
||||
conf.update({'epochs': 1})
|
||||
conf.update({'mongodb': False})
|
||||
conf.update({'timerange': None})
|
||||
conf.update({'spaces': 'all'})
|
||||
|
||||
hyperopt = Hyperopt(conf)
|
||||
hyperopt.trials = trials
|
||||
hyperopt.tickerdata_to_dataframe = MagicMock()
|
||||
|
||||
hyperopt.start()
|
||||
mock_fmin.assert_called_once()
|
||||
|
||||
|
||||
def test_start_uses_mongotrials(mocker, init_hyperopt, default_conf) -> None:
|
||||
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
|
||||
mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
|
||||
mock_mongotrials = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.MongoTrials',
|
||||
return_value=create_trials(mocker)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.multiprocessing.cpu_count', MagicMock(return_value=1))
|
||||
parallel = mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{'loss': 1, 'result': 'foo result', 'params': {}}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf.update({'config': 'config.json.example'})
|
||||
conf.update({'epochs': 1})
|
||||
conf.update({'mongodb': True})
|
||||
conf.update({'timerange': None})
|
||||
conf.update({'spaces': 'all'})
|
||||
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
||||
|
||||
hyperopt = Hyperopt(conf)
|
||||
hyperopt.tickerdata_to_dataframe = MagicMock()
|
||||
|
||||
hyperopt.start()
|
||||
mock_mongotrials.assert_called_once()
|
||||
mock_fmin.assert_called_once()
|
||||
parallel.assert_called_once()
|
||||
|
||||
assert 'Best result:\nfoo result\nwith values:\n{}' in caplog.text
|
||||
assert dumper.called
|
||||
|
||||
# test log_trials_result
|
||||
# test buy_strategy_generator def populate_buy_trend
|
||||
# test optimizer if 'ro_t1' in params
|
||||
|
||||
def test_format_results(init_hyperopt):
|
||||
"""
|
||||
@@ -400,7 +220,7 @@ def test_format_results(init_hyperopt):
|
||||
('LTC/BTC', 1, 1, 123),
|
||||
('XPR/BTC', -1, -2, -246)
|
||||
]
|
||||
labels = ['currency', 'profit_percent', 'profit_BTC', 'duration']
|
||||
labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration']
|
||||
df = pd.DataFrame.from_records(trades, columns=labels)
|
||||
|
||||
result = _HYPEROPT.format_results(df)
|
||||
@@ -419,20 +239,6 @@ def test_format_results(init_hyperopt):
|
||||
assert result.find('Total profit 1.00000000 EUR')
|
||||
|
||||
|
||||
def test_signal_handler(mocker, init_hyperopt):
|
||||
"""
|
||||
Test Hyperopt.signal_handler()
|
||||
"""
|
||||
m = MagicMock()
|
||||
mocker.patch('sys.exit', m)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.save_trials', m)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.log_trials_result', m)
|
||||
|
||||
hyperopt = _HYPEROPT
|
||||
hyperopt.signal_handler(signal.SIGTERM, None)
|
||||
assert m.call_count == 3
|
||||
|
||||
|
||||
def test_has_space(init_hyperopt):
|
||||
"""
|
||||
Test Hyperopt.has_space() method
|
||||
@@ -457,8 +263,8 @@ def test_populate_indicators(init_hyperopt) -> None:
|
||||
|
||||
# Check if some indicators are generated. We will not test all of them
|
||||
assert 'adx' in dataframe
|
||||
assert 'ao' in dataframe
|
||||
assert 'cci' in dataframe
|
||||
assert 'mfi' in dataframe
|
||||
assert 'rsi' in dataframe
|
||||
|
||||
|
||||
def test_buy_strategy_generator(init_hyperopt) -> None:
|
||||
@@ -472,44 +278,15 @@ def test_buy_strategy_generator(init_hyperopt) -> None:
|
||||
|
||||
populate_buy_trend = _HYPEROPT.buy_strategy_generator(
|
||||
{
|
||||
'uptrend_long_ema': {
|
||||
'enabled': True
|
||||
},
|
||||
'macd_below_zero': {
|
||||
'enabled': True
|
||||
},
|
||||
'uptrend_short_ema': {
|
||||
'enabled': True
|
||||
},
|
||||
'mfi': {
|
||||
'enabled': True,
|
||||
'value': 20
|
||||
},
|
||||
'fastd': {
|
||||
'enabled': True,
|
||||
'value': 20
|
||||
},
|
||||
'adx': {
|
||||
'enabled': True,
|
||||
'value': 20
|
||||
},
|
||||
'rsi': {
|
||||
'enabled': True,
|
||||
'value': 20
|
||||
},
|
||||
'over_sar': {
|
||||
'enabled': True,
|
||||
},
|
||||
'green_candle': {
|
||||
'enabled': True,
|
||||
},
|
||||
'uptrend_sma': {
|
||||
'enabled': True,
|
||||
},
|
||||
|
||||
'trigger': {
|
||||
'type': 'lower_bb'
|
||||
}
|
||||
'adx-value': 20,
|
||||
'fastd-value': 20,
|
||||
'mfi-value': 20,
|
||||
'rsi-value': 20,
|
||||
'adx-enabled': True,
|
||||
'fastd-enabled': True,
|
||||
'mfi-enabled': True,
|
||||
'rsi-enabled': True,
|
||||
'trigger': 'bb_lower'
|
||||
}
|
||||
)
|
||||
result = populate_buy_trend(dataframe)
|
||||
@@ -530,43 +307,42 @@ def test_generate_optimizer(mocker, init_hyperopt, default_conf) -> None:
|
||||
trades = [
|
||||
('POWR/BTC', 0.023117, 0.000233, 100)
|
||||
]
|
||||
labels = ['currency', 'profit_percent', 'profit_BTC', 'duration']
|
||||
labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration']
|
||||
backtest_result = pd.DataFrame.from_records(trades, columns=labels)
|
||||
|
||||
mocker.patch(
|
||||
'freqtrade.optimize.hyperopt.Hyperopt.backtest',
|
||||
MagicMock(return_value=backtest_result)
|
||||
)
|
||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
|
||||
patch_exchange(mocker)
|
||||
mocker.patch('freqtrade.optimize.hyperopt.load', MagicMock())
|
||||
|
||||
optimizer_param = {
|
||||
'adx': {'enabled': False},
|
||||
'fastd': {'enabled': True, 'value': 35.0},
|
||||
'green_candle': {'enabled': True},
|
||||
'macd_below_zero': {'enabled': True},
|
||||
'mfi': {'enabled': False},
|
||||
'over_sar': {'enabled': False},
|
||||
'roi_p1': 0.01,
|
||||
'roi_p2': 0.01,
|
||||
'roi_p3': 0.1,
|
||||
'adx-value': 0,
|
||||
'fastd-value': 35,
|
||||
'mfi-value': 0,
|
||||
'rsi-value': 0,
|
||||
'adx-enabled': False,
|
||||
'fastd-enabled': True,
|
||||
'mfi-enabled': False,
|
||||
'rsi-enabled': False,
|
||||
'trigger': 'macd_cross_signal',
|
||||
'roi_t1': 60.0,
|
||||
'roi_t2': 30.0,
|
||||
'roi_t3': 20.0,
|
||||
'rsi': {'enabled': False},
|
||||
'roi_p1': 0.01,
|
||||
'roi_p2': 0.01,
|
||||
'roi_p3': 0.1,
|
||||
'stoploss': -0.4,
|
||||
'trigger': {'type': 'macd_cross_signal'},
|
||||
'uptrend_long_ema': {'enabled': False},
|
||||
'uptrend_short_ema': {'enabled': True},
|
||||
'uptrend_sma': {'enabled': True}
|
||||
}
|
||||
|
||||
response_expected = {
|
||||
'loss': 1.9840569076926293,
|
||||
'result': ' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC '
|
||||
'(0.0231Σ%). Avg duration 100.0 mins.',
|
||||
'status': 'ok'
|
||||
'params': optimizer_param
|
||||
}
|
||||
|
||||
hyperopt = Hyperopt(conf)
|
||||
generate_optimizer_value = hyperopt.generate_optimizer(optimizer_param)
|
||||
generate_optimizer_value = hyperopt.generate_optimizer(list(optimizer_param.values()))
|
||||
assert generate_optimizer_value == response_expected
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# pragma pylint: disable=missing-docstring,W0212
|
||||
|
||||
from user_data.hyperopt_conf import hyperopt_optimize_conf
|
||||
|
||||
|
||||
def test_hyperopt_optimize_conf():
|
||||
hyperopt_conf = hyperopt_optimize_conf()
|
||||
|
||||
assert "max_open_trades" in hyperopt_conf
|
||||
assert "stake_currency" in hyperopt_conf
|
||||
assert "stake_amount" in hyperopt_conf
|
||||
assert "minimal_roi" in hyperopt_conf
|
||||
assert "stoploss" in hyperopt_conf
|
||||
assert "bid_strategy" in hyperopt_conf
|
||||
assert "exchange" in hyperopt_conf
|
||||
assert "pair_whitelist" in hyperopt_conf['exchange']
|
||||
@@ -3,16 +3,19 @@
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
import arrow
|
||||
from shutil import copyfile
|
||||
|
||||
import arrow
|
||||
|
||||
from freqtrade import optimize
|
||||
from freqtrade.misc import file_dump_json
|
||||
from freqtrade.optimize.__init__ import make_testdata_path, download_pairs, \
|
||||
download_backtesting_testdata, load_tickerdata_file, trim_tickerlist, \
|
||||
load_cached_data_for_updating
|
||||
from freqtrade.arguments import TimeRange
|
||||
from freqtrade.tests.conftest import log_has
|
||||
from freqtrade.misc import file_dump_json
|
||||
from freqtrade.optimize.__init__ import (download_backtesting_testdata,
|
||||
download_pairs,
|
||||
load_cached_data_for_updating,
|
||||
load_tickerdata_file,
|
||||
make_testdata_path, trim_tickerlist)
|
||||
from freqtrade.tests.conftest import get_patched_exchange, log_has
|
||||
|
||||
# Change this if modifying UNITTEST/BTC testdatafile
|
||||
_BTC_UNITTEST_LENGTH = 13681
|
||||
@@ -49,12 +52,11 @@ def _clean_test_file(file: str) -> None:
|
||||
os.rename(file_swp, file)
|
||||
|
||||
|
||||
def test_load_data_30min_ticker(ticker_history, mocker, caplog) -> None:
|
||||
def test_load_data_30min_ticker(ticker_history, mocker, caplog, default_conf) -> None:
|
||||
"""
|
||||
Test load_data() with 30 min ticker
|
||||
"""
|
||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-30m.json')
|
||||
_backup_file(file, copy_file=True)
|
||||
optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='30m')
|
||||
@@ -63,11 +65,11 @@ def test_load_data_30min_ticker(ticker_history, mocker, caplog) -> None:
|
||||
_clean_test_file(file)
|
||||
|
||||
|
||||
def test_load_data_5min_ticker(ticker_history, mocker, caplog) -> None:
|
||||
def test_load_data_5min_ticker(ticker_history, mocker, caplog, default_conf) -> None:
|
||||
"""
|
||||
Test load_data() with 5 min ticker
|
||||
"""
|
||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||
|
||||
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-5m.json')
|
||||
_backup_file(file, copy_file=True)
|
||||
@@ -81,7 +83,7 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
|
||||
"""
|
||||
Test load_data() with 1 min ticker
|
||||
"""
|
||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||
|
||||
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json')
|
||||
_backup_file(file, copy_file=True)
|
||||
@@ -91,12 +93,12 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
|
||||
_clean_test_file(file)
|
||||
|
||||
|
||||
def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog) -> None:
|
||||
def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog, default_conf) -> None:
|
||||
"""
|
||||
Test load_data() with 1 min ticker
|
||||
"""
|
||||
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
||||
|
||||
_backup_file(file)
|
||||
@@ -114,6 +116,7 @@ def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog) -> None:
|
||||
optimize.load_data(None,
|
||||
ticker_interval='1m',
|
||||
refresh_pairs=True,
|
||||
exchange=exchange,
|
||||
pairs=['MEME/BTC'])
|
||||
assert os.path.isfile(file) is True
|
||||
assert log_has('Download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
|
||||
@@ -124,9 +127,9 @@ def test_testdata_path() -> None:
|
||||
assert os.path.join('freqtrade', 'tests', 'testdata') in make_testdata_path(None)
|
||||
|
||||
|
||||
def test_download_pairs(ticker_history, mocker) -> None:
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
||||
|
||||
def test_download_pairs(ticker_history, mocker, default_conf) -> None:
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
||||
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
|
||||
file2_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-1m.json')
|
||||
@@ -140,7 +143,8 @@ def test_download_pairs(ticker_history, mocker) -> None:
|
||||
assert os.path.isfile(file1_1) is False
|
||||
assert os.path.isfile(file2_1) is False
|
||||
|
||||
assert download_pairs(None, pairs=['MEME/BTC', 'CFI/BTC'], ticker_interval='1m') is True
|
||||
assert download_pairs(None, exchange,
|
||||
pairs=['MEME/BTC', 'CFI/BTC'], ticker_interval='1m') is True
|
||||
|
||||
assert os.path.isfile(file1_1) is True
|
||||
assert os.path.isfile(file2_1) is True
|
||||
@@ -152,7 +156,8 @@ def test_download_pairs(ticker_history, mocker) -> None:
|
||||
assert os.path.isfile(file1_5) is False
|
||||
assert os.path.isfile(file2_5) is False
|
||||
|
||||
assert download_pairs(None, pairs=['MEME/BTC', 'CFI/BTC'], ticker_interval='5m') is True
|
||||
assert download_pairs(None, exchange,
|
||||
pairs=['MEME/BTC', 'CFI/BTC'], ticker_interval='5m') is True
|
||||
|
||||
assert os.path.isfile(file1_5) is True
|
||||
assert os.path.isfile(file2_5) is True
|
||||
@@ -265,30 +270,32 @@ def test_load_cached_data_for_updating(mocker) -> None:
|
||||
assert start_ts is None
|
||||
|
||||
|
||||
def test_download_pairs_exception(ticker_history, mocker, caplog) -> None:
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
||||
def test_download_pairs_exception(ticker_history, mocker, caplog, default_conf) -> None:
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||
mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata',
|
||||
side_effect=BaseException('File Error'))
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
|
||||
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
|
||||
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
|
||||
_backup_file(file1_1)
|
||||
_backup_file(file1_5)
|
||||
|
||||
download_pairs(None, pairs=['MEME/BTC'], ticker_interval='1m')
|
||||
download_pairs(None, exchange, pairs=['MEME/BTC'], ticker_interval='1m')
|
||||
# clean files freshly downloaded
|
||||
_clean_test_file(file1_1)
|
||||
_clean_test_file(file1_5)
|
||||
assert log_has('Failed to download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_download_backtesting_testdata(ticker_history, mocker) -> None:
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
|
||||
def test_download_backtesting_testdata(ticker_history, mocker, default_conf) -> None:
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=ticker_history)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
|
||||
# Download a 1 min ticker file
|
||||
file1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'XEL_BTC-1m.json')
|
||||
_backup_file(file1)
|
||||
download_backtesting_testdata(None, pair="XEL/BTC", tick_interval='1m')
|
||||
download_backtesting_testdata(None, exchange, pair="XEL/BTC", tick_interval='1m')
|
||||
assert os.path.isfile(file1) is True
|
||||
_clean_test_file(file1)
|
||||
|
||||
@@ -296,21 +303,21 @@ def test_download_backtesting_testdata(ticker_history, mocker) -> None:
|
||||
file2 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'STORJ_BTC-5m.json')
|
||||
_backup_file(file2)
|
||||
|
||||
download_backtesting_testdata(None, pair="STORJ/BTC", tick_interval='5m')
|
||||
download_backtesting_testdata(None, exchange, pair="STORJ/BTC", tick_interval='5m')
|
||||
assert os.path.isfile(file2) is True
|
||||
_clean_test_file(file2)
|
||||
|
||||
|
||||
def test_download_backtesting_testdata2(mocker) -> None:
|
||||
def test_download_backtesting_testdata2(mocker, default_conf) -> None:
|
||||
tick = [
|
||||
[1509836520000, 0.00162008, 0.00162008, 0.00162008, 0.00162008, 108.14853839],
|
||||
[1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199]
|
||||
]
|
||||
json_dump_mock = mocker.patch('freqtrade.misc.file_dump_json', return_value=None)
|
||||
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick)
|
||||
|
||||
download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='1m')
|
||||
download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='3m')
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=tick)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
download_backtesting_testdata(None, exchange, pair="UNITTEST/BTC", tick_interval='1m')
|
||||
download_backtesting_testdata(None, exchange, pair="UNITTEST/BTC", tick_interval='3m')
|
||||
assert json_dump_mock.call_count == 2
|
||||
|
||||
|
||||
@@ -326,10 +333,10 @@ def test_load_tickerdata_file() -> None:
|
||||
|
||||
|
||||
def test_init(default_conf, mocker) -> None:
|
||||
conf = {'exchange': {'pair_whitelist': []}}
|
||||
mocker.patch('freqtrade.optimize.hyperopt_optimize_conf', return_value=conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
assert {} == optimize.load_data(
|
||||
'',
|
||||
exchange=exchange,
|
||||
pairs=[],
|
||||
refresh_pairs=True,
|
||||
ticker_interval=default_conf['ticker_interval']
|
||||
|
||||
@@ -7,11 +7,14 @@ Unit test file for rpc/rpc.py
|
||||
from datetime import datetime
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.freqtradebot import FreqtradeBot
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.rpc.rpc import RPC
|
||||
from freqtrade.rpc.rpc import RPC, RPCException
|
||||
from freqtrade.state import State
|
||||
from freqtrade.tests.test_freqtradebot import patch_get_signal, patch_coinmarketcap
|
||||
from freqtrade.tests.test_freqtradebot import (patch_coinmarketcap,
|
||||
patch_get_signal)
|
||||
|
||||
|
||||
# Functions for recurrent object patching
|
||||
@@ -23,37 +26,35 @@ def prec_satoshi(a, b) -> float:
|
||||
|
||||
|
||||
# Unit tests
|
||||
def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
|
||||
"""
|
||||
Test rpc_trade_status() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
(error, result) = rpc.rpc_trade_status()
|
||||
assert error
|
||||
assert 'trader is not running' in result
|
||||
with pytest.raises(RPCException, match=r'.*trader is not running*'):
|
||||
rpc._rpc_trade_status()
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
(error, result) = rpc.rpc_trade_status()
|
||||
assert error
|
||||
assert 'no active trade' in result
|
||||
with pytest.raises(RPCException, match=r'.*no active trade*'):
|
||||
rpc._rpc_trade_status()
|
||||
|
||||
freqtradebot.create_trade()
|
||||
(error, result) = rpc.rpc_trade_status()
|
||||
assert not error
|
||||
trade = result[0]
|
||||
trades = rpc._rpc_trade_status()
|
||||
trade = trades[0]
|
||||
|
||||
result_message = [
|
||||
'*Trade ID:* `1`\n'
|
||||
@@ -68,57 +69,57 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'*Current Profit:* `-0.59%`\n'
|
||||
'*Open Order:* `(limit buy rem=0.00000000)`'
|
||||
]
|
||||
assert result == result_message
|
||||
assert trades == result_message
|
||||
assert trade.find('[ETH/BTC]') >= 0
|
||||
|
||||
|
||||
def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
||||
def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
|
||||
"""
|
||||
Test rpc_status_table() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
(error, result) = rpc.rpc_status_table()
|
||||
assert error
|
||||
assert '*Status:* `trader is not running`' in result
|
||||
with pytest.raises(RPCException, match=r'.*\*Status:\* `trader is not running``*'):
|
||||
rpc._rpc_status_table()
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
(error, result) = rpc.rpc_status_table()
|
||||
assert error
|
||||
assert '*Status:* `no active order`' in result
|
||||
with pytest.raises(RPCException, match=r'.*\*Status:\* `no active order`*'):
|
||||
rpc._rpc_status_table()
|
||||
|
||||
freqtradebot.create_trade()
|
||||
(error, result) = rpc.rpc_status_table()
|
||||
result = rpc._rpc_status_table()
|
||||
assert 'just now' in result['Since'].all()
|
||||
assert 'ETH/BTC' in result['Pair'].all()
|
||||
assert '-0.59%' in result['Profit'].all()
|
||||
|
||||
|
||||
def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
limit_buy_order, limit_sell_order, markets, mocker) -> None:
|
||||
"""
|
||||
Test rpc_daily_profit() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
@@ -140,8 +141,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
||||
|
||||
# Try valid data
|
||||
update.message.text = '/daily 2'
|
||||
(error, days) = rpc.rpc_daily_profit(7, stake_currency, fiat_display_currency)
|
||||
assert not error
|
||||
days = rpc._rpc_daily_profit(7, stake_currency, fiat_display_currency)
|
||||
assert len(days) == 7
|
||||
for day in days:
|
||||
# [datetime.date(2018, 1, 11), '0.00000000 BTC', '0.000 USD']
|
||||
@@ -154,13 +154,12 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
||||
assert str(days[0][0]) == str(datetime.utcnow().date())
|
||||
|
||||
# Try invalid data
|
||||
(error, days) = rpc.rpc_daily_profit(0, stake_currency, fiat_display_currency)
|
||||
assert error
|
||||
assert days.find('must be an integer greater than 0') >= 0
|
||||
with pytest.raises(RPCException, match=r'.*must be an integer greater than 0*'):
|
||||
rpc._rpc_daily_profit(0, stake_currency, fiat_display_currency)
|
||||
|
||||
|
||||
def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
limit_buy_order, limit_sell_order, markets, mocker) -> None:
|
||||
"""
|
||||
Test rpc_trade_statistics() method
|
||||
"""
|
||||
@@ -170,12 +169,13 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||
)
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
@@ -184,9 +184,8 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
(error, stats) = rpc.rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||
assert error
|
||||
assert stats.find('no closed trade') >= 0
|
||||
with pytest.raises(RPCException, match=r'.*no closed trade*'):
|
||||
rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||
|
||||
# Create some test data
|
||||
freqtradebot.create_trade()
|
||||
@@ -196,7 +195,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
|
||||
# Update the ticker with a market going up
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker_sell_up
|
||||
)
|
||||
@@ -211,7 +210,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
|
||||
# Update the ticker with a market going up
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker_sell_up
|
||||
)
|
||||
@@ -219,8 +218,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
|
||||
(error, stats) = rpc.rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||
assert not error
|
||||
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||
assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05)
|
||||
assert prec_satoshi(stats['profit_closed_percent'], 6.2)
|
||||
assert prec_satoshi(stats['profit_closed_fiat'], 0.93255)
|
||||
@@ -237,7 +235,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
|
||||
# Test that rpc_trade_statistics can handle trades that lacks
|
||||
# trade.open_rate (it is set to None)
|
||||
def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
|
||||
def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets,
|
||||
ticker_sell_up, limit_buy_order, limit_sell_order):
|
||||
"""
|
||||
Test rpc_trade_statistics() method
|
||||
@@ -248,12 +246,13 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
|
||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||
)
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
@@ -269,7 +268,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
|
||||
trade.update(limit_buy_order)
|
||||
# Update the ticker with a market going up
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker_sell_up,
|
||||
get_fee=fee
|
||||
@@ -281,8 +280,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
|
||||
for trade in Trade.query.order_by(Trade.id).all():
|
||||
trade.open_rate = None
|
||||
|
||||
(error, stats) = rpc.rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||
assert not error
|
||||
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||
assert prec_satoshi(stats['profit_closed_coin'], 0)
|
||||
assert prec_satoshi(stats['profit_closed_percent'], 0)
|
||||
assert prec_satoshi(stats['profit_closed_fiat'], 0)
|
||||
@@ -320,9 +318,9 @@ def test_rpc_balance_handle(default_conf, mocker):
|
||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||
)
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_balances=MagicMock(return_value=mock_balance)
|
||||
)
|
||||
@@ -330,18 +328,16 @@ def test_rpc_balance_handle(default_conf, mocker):
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
(error, res) = rpc.rpc_balance(default_conf['fiat_display_currency'])
|
||||
assert not error
|
||||
(trade, x, y, z) = res
|
||||
assert prec_satoshi(x, 12)
|
||||
assert prec_satoshi(z, 180000)
|
||||
assert 'USD' in y
|
||||
assert len(trade) == 1
|
||||
assert 'BTC' in trade[0]['currency']
|
||||
assert prec_satoshi(trade[0]['available'], 10)
|
||||
assert prec_satoshi(trade[0]['balance'], 12)
|
||||
assert prec_satoshi(trade[0]['pending'], 2)
|
||||
assert prec_satoshi(trade[0]['est_btc'], 12)
|
||||
output, total, symbol, value = rpc._rpc_balance(default_conf['fiat_display_currency'])
|
||||
assert prec_satoshi(total, 12)
|
||||
assert prec_satoshi(value, 180000)
|
||||
assert 'USD' in symbol
|
||||
assert len(output) == 1
|
||||
assert 'BTC' in output[0]['currency']
|
||||
assert prec_satoshi(output[0]['available'], 10)
|
||||
assert prec_satoshi(output[0]['balance'], 12)
|
||||
assert prec_satoshi(output[0]['pending'], 2)
|
||||
assert prec_satoshi(output[0]['est_btc'], 12)
|
||||
|
||||
|
||||
def test_rpc_start(mocker, default_conf) -> None:
|
||||
@@ -350,9 +346,9 @@ def test_rpc_start(mocker, default_conf) -> None:
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=MagicMock()
|
||||
)
|
||||
@@ -361,13 +357,11 @@ def test_rpc_start(mocker, default_conf) -> None:
|
||||
rpc = RPC(freqtradebot)
|
||||
freqtradebot.state = State.STOPPED
|
||||
|
||||
(error, result) = rpc.rpc_start()
|
||||
assert not error
|
||||
result = rpc._rpc_start()
|
||||
assert '`Starting trader ...`' in result
|
||||
assert freqtradebot.state == State.RUNNING
|
||||
|
||||
(error, result) = rpc.rpc_start()
|
||||
assert error
|
||||
result = rpc._rpc_start()
|
||||
assert '*Status:* `already running`' in result
|
||||
assert freqtradebot.state == State.RUNNING
|
||||
|
||||
@@ -378,9 +372,9 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=MagicMock()
|
||||
)
|
||||
@@ -389,28 +383,26 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
||||
rpc = RPC(freqtradebot)
|
||||
freqtradebot.state = State.RUNNING
|
||||
|
||||
(error, result) = rpc.rpc_stop()
|
||||
assert not error
|
||||
result = rpc._rpc_stop()
|
||||
assert '`Stopping trader ...`' in result
|
||||
assert freqtradebot.state == State.STOPPED
|
||||
|
||||
(error, result) = rpc.rpc_stop()
|
||||
assert error
|
||||
result = rpc._rpc_stop()
|
||||
assert '*Status:* `already stopped`' in result
|
||||
assert freqtradebot.state == State.STOPPED
|
||||
|
||||
|
||||
def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None:
|
||||
"""
|
||||
Test rpc_forcesell() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
|
||||
cancel_order_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
cancel_order=cancel_order_mock,
|
||||
@@ -422,42 +414,33 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
}
|
||||
),
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
(error, res) = rpc.rpc_forcesell(None)
|
||||
assert error
|
||||
assert res == '`trader is not running`'
|
||||
with pytest.raises(RPCException, match=r'.*`trader is not running`*'):
|
||||
rpc._rpc_forcesell(None)
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
(error, res) = rpc.rpc_forcesell(None)
|
||||
assert error
|
||||
assert res == 'Invalid argument.'
|
||||
with pytest.raises(RPCException, match=r'.*Invalid argument.*'):
|
||||
rpc._rpc_forcesell(None)
|
||||
|
||||
(error, res) = rpc.rpc_forcesell('all')
|
||||
assert not error
|
||||
assert res == ''
|
||||
rpc._rpc_forcesell('all')
|
||||
|
||||
freqtradebot.create_trade()
|
||||
(error, res) = rpc.rpc_forcesell('all')
|
||||
assert not error
|
||||
assert res == ''
|
||||
rpc._rpc_forcesell('all')
|
||||
|
||||
(error, res) = rpc.rpc_forcesell('1')
|
||||
assert not error
|
||||
assert res == ''
|
||||
rpc._rpc_forcesell('1')
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
(error, res) = rpc.rpc_forcesell(None)
|
||||
assert error
|
||||
assert res == '`trader is not running`'
|
||||
with pytest.raises(RPCException, match=r'.*`trader is not running`*'):
|
||||
rpc._rpc_forcesell(None)
|
||||
|
||||
(error, res) = rpc.rpc_forcesell('all')
|
||||
assert error
|
||||
assert res == '`trader is not running`'
|
||||
with pytest.raises(RPCException, match=r'.*`trader is not running`*'):
|
||||
rpc._rpc_forcesell('all')
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
assert cancel_order_mock.call_count == 0
|
||||
@@ -465,7 +448,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
trade = Trade.query.filter(Trade.id == '1').first()
|
||||
filled_amount = trade.amount / 2
|
||||
mocker.patch(
|
||||
'freqtrade.freqtradebot.exchange.get_order',
|
||||
'freqtrade.exchange.Exchange.get_order',
|
||||
return_value={
|
||||
'status': 'open',
|
||||
'type': 'limit',
|
||||
@@ -475,9 +458,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
)
|
||||
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
|
||||
# and trade amount is updated
|
||||
(error, res) = rpc.rpc_forcesell('1')
|
||||
assert not error
|
||||
assert res == ''
|
||||
rpc._rpc_forcesell('1')
|
||||
assert cancel_order_mock.call_count == 1
|
||||
assert trade.amount == filled_amount
|
||||
|
||||
@@ -486,7 +467,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
amount = trade.amount
|
||||
# make an limit-buy open trade, if there is no 'filled', don't sell it
|
||||
mocker.patch(
|
||||
'freqtrade.freqtradebot.exchange.get_order',
|
||||
'freqtrade.exchange.Exchange.get_order',
|
||||
return_value={
|
||||
'status': 'open',
|
||||
'type': 'limit',
|
||||
@@ -495,43 +476,40 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
}
|
||||
)
|
||||
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
|
||||
(error, res) = rpc.rpc_forcesell('2')
|
||||
assert not error
|
||||
assert res == ''
|
||||
rpc._rpc_forcesell('2')
|
||||
assert cancel_order_mock.call_count == 2
|
||||
assert trade.amount == amount
|
||||
|
||||
freqtradebot.create_trade()
|
||||
# make an limit-sell open trade
|
||||
mocker.patch(
|
||||
'freqtrade.freqtradebot.exchange.get_order',
|
||||
'freqtrade.exchange.Exchange.get_order',
|
||||
return_value={
|
||||
'status': 'open',
|
||||
'type': 'limit',
|
||||
'side': 'sell'
|
||||
}
|
||||
)
|
||||
(error, res) = rpc.rpc_forcesell('3')
|
||||
assert not error
|
||||
assert res == ''
|
||||
rpc._rpc_forcesell('3')
|
||||
# status quo, no exchange calls
|
||||
assert cancel_order_mock.call_count == 2
|
||||
|
||||
|
||||
def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
limit_sell_order, mocker) -> None:
|
||||
limit_sell_order, markets, mocker) -> None:
|
||||
"""
|
||||
Test rpc_performance() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_balances=MagicMock(return_value=ticker),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
@@ -550,40 +528,38 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
(error, res) = rpc.rpc_performance()
|
||||
assert not error
|
||||
res = rpc._rpc_performance()
|
||||
assert len(res) == 1
|
||||
assert res[0]['pair'] == 'ETH/BTC'
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
||||
|
||||
|
||||
def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
|
||||
def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None:
|
||||
"""
|
||||
Test rpc_count() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.rpc_manager.Telegram', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_balances=MagicMock(return_value=ticker),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
(error, trades) = rpc.rpc_count()
|
||||
trades = rpc._rpc_count()
|
||||
nb_trades = len(trades)
|
||||
assert not error
|
||||
assert nb_trades == 0
|
||||
|
||||
# Create some test data
|
||||
freqtradebot.create_trade()
|
||||
(error, trades) = rpc.rpc_count()
|
||||
trades = rpc._rpc_count()
|
||||
nb_trades = len(trades)
|
||||
assert not error
|
||||
assert nb_trades == 1
|
||||
|
||||
@@ -7,49 +7,35 @@ from copy import deepcopy
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freqtrade.rpc.rpc_manager import RPCManager
|
||||
from freqtrade.rpc.telegram import Telegram
|
||||
from freqtrade.tests.conftest import log_has, get_patched_freqtradebot
|
||||
from freqtrade.tests.conftest import get_patched_freqtradebot, log_has
|
||||
|
||||
|
||||
def test_rpc_manager_object() -> None:
|
||||
"""
|
||||
Test the Arguments object has the mandatory methods
|
||||
:return: None
|
||||
"""
|
||||
assert hasattr(RPCManager, '_init')
|
||||
""" Test the Arguments object has the mandatory methods """
|
||||
assert hasattr(RPCManager, 'send_msg')
|
||||
assert hasattr(RPCManager, 'cleanup')
|
||||
|
||||
|
||||
def test__init__(mocker, default_conf) -> None:
|
||||
"""
|
||||
Test __init__() method
|
||||
"""
|
||||
init_mock = mocker.patch('freqtrade.rpc.rpc_manager.RPCManager._init', MagicMock())
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
""" Test __init__() method """
|
||||
conf = deepcopy(default_conf)
|
||||
conf['telegram']['enabled'] = False
|
||||
|
||||
rpc_manager = RPCManager(freqtradebot)
|
||||
assert rpc_manager.freqtrade == freqtradebot
|
||||
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, conf))
|
||||
assert rpc_manager.registered_modules == []
|
||||
assert rpc_manager.telegram is None
|
||||
assert init_mock.call_count == 1
|
||||
|
||||
|
||||
def test_init_telegram_disabled(mocker, default_conf, caplog) -> None:
|
||||
"""
|
||||
Test _init() method with Telegram disabled
|
||||
"""
|
||||
""" Test _init() method with Telegram disabled """
|
||||
caplog.set_level(logging.DEBUG)
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['telegram']['enabled'] = False
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, conf)
|
||||
rpc_manager = RPCManager(freqtradebot)
|
||||
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, conf))
|
||||
|
||||
assert not log_has('Enabling rpc.telegram ...', caplog.record_tuples)
|
||||
assert rpc_manager.registered_modules == []
|
||||
assert rpc_manager.telegram is None
|
||||
|
||||
|
||||
def test_init_telegram_enabled(mocker, default_conf, caplog) -> None:
|
||||
@@ -59,14 +45,12 @@ def test_init_telegram_enabled(mocker, default_conf, caplog) -> None:
|
||||
caplog.set_level(logging.DEBUG)
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
rpc_manager = RPCManager(freqtradebot)
|
||||
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
|
||||
|
||||
assert log_has('Enabling rpc.telegram ...', caplog.record_tuples)
|
||||
len_modules = len(rpc_manager.registered_modules)
|
||||
assert len_modules == 1
|
||||
assert 'telegram' in rpc_manager.registered_modules
|
||||
assert isinstance(rpc_manager.telegram, Telegram)
|
||||
assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules]
|
||||
|
||||
|
||||
def test_cleanup_telegram_disabled(mocker, default_conf, caplog) -> None:
|
||||
@@ -99,11 +83,11 @@ def test_cleanup_telegram_enabled(mocker, default_conf, caplog) -> None:
|
||||
rpc_manager = RPCManager(freqtradebot)
|
||||
|
||||
# Check we have Telegram as a registered modules
|
||||
assert 'telegram' in rpc_manager.registered_modules
|
||||
assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules]
|
||||
|
||||
rpc_manager.cleanup()
|
||||
assert log_has('Cleaning up rpc.telegram ...', caplog.record_tuples)
|
||||
assert 'telegram' not in rpc_manager.registered_modules
|
||||
assert 'telegram' not in [mod.name for mod in rpc_manager.registered_modules]
|
||||
assert telegram_mock.call_count == 1
|
||||
|
||||
|
||||
@@ -120,7 +104,7 @@ def test_send_msg_telegram_disabled(mocker, default_conf, caplog) -> None:
|
||||
rpc_manager = RPCManager(freqtradebot)
|
||||
rpc_manager.send_msg('test')
|
||||
|
||||
assert log_has('test', caplog.record_tuples)
|
||||
assert log_has('Sending rpc message: test', caplog.record_tuples)
|
||||
assert telegram_mock.call_count == 0
|
||||
|
||||
|
||||
@@ -135,5 +119,5 @@ def test_send_msg_telegram_enabled(mocker, default_conf, caplog) -> None:
|
||||
rpc_manager = RPCManager(freqtradebot)
|
||||
rpc_manager.send_msg('test')
|
||||
|
||||
assert log_has('test', caplog.record_tuples)
|
||||
assert log_has('Sending rpc message: test', caplog.record_tuples)
|
||||
assert telegram_mock.call_count == 1
|
||||
|
||||
@@ -11,17 +11,18 @@ from datetime import datetime
|
||||
from random import randint
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from telegram import Update, Message, Chat
|
||||
from telegram import Chat, Message, Update
|
||||
from telegram.error import NetworkError
|
||||
|
||||
from freqtrade import __version__
|
||||
from freqtrade.freqtradebot import FreqtradeBot
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.rpc.telegram import Telegram
|
||||
from freqtrade.rpc.telegram import authorized_only
|
||||
from freqtrade.rpc.telegram import Telegram, authorized_only
|
||||
from freqtrade.state import State
|
||||
from freqtrade.tests.conftest import get_patched_freqtradebot, log_has
|
||||
from freqtrade.tests.test_freqtradebot import patch_get_signal, patch_coinmarketcap
|
||||
from freqtrade.tests.conftest import (get_patched_freqtradebot, log_has,
|
||||
patch_exchange)
|
||||
from freqtrade.tests.test_freqtradebot import (patch_coinmarketcap,
|
||||
patch_get_signal)
|
||||
|
||||
|
||||
class DummyCls(Telegram):
|
||||
@@ -32,6 +33,9 @@ class DummyCls(Telegram):
|
||||
super().__init__(freqtrade)
|
||||
self.state = {'called': False}
|
||||
|
||||
def _init(self):
|
||||
pass
|
||||
|
||||
@authorized_only
|
||||
def dummy_handler(self, *args, **kwargs) -> None:
|
||||
"""
|
||||
@@ -60,9 +64,7 @@ def test__init__(default_conf, mocker) -> None:
|
||||
|
||||
|
||||
def test_init(default_conf, mocker, caplog) -> None:
|
||||
"""
|
||||
Test _init() method
|
||||
"""
|
||||
""" Test _init() method """
|
||||
start_polling = MagicMock()
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock(return_value=start_polling))
|
||||
|
||||
@@ -80,21 +82,6 @@ def test_init(default_conf, mocker, caplog) -> None:
|
||||
assert log_has(message_str, caplog.record_tuples)
|
||||
|
||||
|
||||
def test_init_disabled(default_conf, mocker, caplog) -> None:
|
||||
"""
|
||||
Test _init() method when Telegram is disabled
|
||||
"""
|
||||
conf = deepcopy(default_conf)
|
||||
conf['telegram']['enabled'] = False
|
||||
Telegram(get_patched_freqtradebot(mocker, conf))
|
||||
|
||||
message_str = "rpc.telegram is listening for following commands: [['status'], ['profit'], " \
|
||||
"['balance'], ['start'], ['stop'], ['forcesell'], ['performance'], ['daily'], " \
|
||||
"['count'], ['help'], ['version']]"
|
||||
|
||||
assert not log_has(message_str, caplog.record_tuples)
|
||||
|
||||
|
||||
def test_cleanup(default_conf, mocker) -> None:
|
||||
"""
|
||||
Test cleanup() method
|
||||
@@ -103,51 +90,18 @@ def test_cleanup(default_conf, mocker) -> None:
|
||||
updater_mock.stop = MagicMock()
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', updater_mock)
|
||||
|
||||
# not enabled
|
||||
conf = deepcopy(default_conf)
|
||||
conf['telegram']['enabled'] = False
|
||||
telegram = Telegram(get_patched_freqtradebot(mocker, conf))
|
||||
telegram.cleanup()
|
||||
assert telegram._updater is None
|
||||
assert updater_mock.call_count == 0
|
||||
assert not hasattr(telegram._updater, 'stop')
|
||||
assert updater_mock.stop.call_count == 0
|
||||
|
||||
# enabled
|
||||
conf['telegram']['enabled'] = True
|
||||
telegram = Telegram(get_patched_freqtradebot(mocker, conf))
|
||||
telegram = Telegram(get_patched_freqtradebot(mocker, default_conf))
|
||||
telegram.cleanup()
|
||||
assert telegram._updater.stop.call_count == 1
|
||||
|
||||
|
||||
def test_is_enabled(default_conf, mocker) -> None:
|
||||
"""
|
||||
Test is_enabled() method
|
||||
"""
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
|
||||
|
||||
telegram = Telegram(get_patched_freqtradebot(mocker, default_conf))
|
||||
assert telegram.is_enabled()
|
||||
|
||||
|
||||
def test_is_not_enabled(default_conf, mocker) -> None:
|
||||
"""
|
||||
Test is_enabled() method
|
||||
"""
|
||||
conf = deepcopy(default_conf)
|
||||
conf['telegram']['enabled'] = False
|
||||
telegram = Telegram(get_patched_freqtradebot(mocker, conf))
|
||||
|
||||
assert not telegram.is_enabled()
|
||||
|
||||
|
||||
def test_authorized_only(default_conf, mocker, caplog) -> None:
|
||||
"""
|
||||
Test authorized_only() method when we are authorized
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
patch_exchange(mocker, None)
|
||||
|
||||
chat = Chat(0, 0)
|
||||
update = Update(randint(1, 100))
|
||||
@@ -178,8 +132,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
|
||||
patch_exchange(mocker, None)
|
||||
chat = Chat(0xdeadbeef, 0)
|
||||
update = Update(randint(1, 100))
|
||||
update.message = Message(randint(1, 100), 0, datetime.utcnow(), chat)
|
||||
@@ -209,7 +162,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
patch_exchange(mocker)
|
||||
|
||||
update = Update(randint(1, 100))
|
||||
update.message = Message(randint(1, 100), 0, datetime.utcnow(), Chat(0, 0))
|
||||
@@ -233,7 +186,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_status(default_conf, update, mocker, fee, ticker) -> None:
|
||||
def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
|
||||
"""
|
||||
Test _status() method
|
||||
"""
|
||||
@@ -245,20 +198,21 @@ def test_status(default_conf, update, mocker, fee, ticker) -> None:
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_pair_detail_url=MagicMock(),
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
msg_mock = MagicMock()
|
||||
status_table = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
rpc_trade_status=MagicMock(return_value=(False, [1, 2, 3])),
|
||||
_rpc_trade_status=MagicMock(return_value=[1, 2, 3]),
|
||||
_status_table=status_table,
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
@@ -278,17 +232,18 @@ def test_status(default_conf, update, mocker, fee, ticker) -> None:
|
||||
assert status_table.call_count == 1
|
||||
|
||||
|
||||
def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
|
||||
"""
|
||||
Test _status() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
msg_mock = MagicMock()
|
||||
status_table = MagicMock()
|
||||
@@ -296,7 +251,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
_status_table=status_table,
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
@@ -324,24 +279,25 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
assert '[ETH/BTC]' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
|
||||
"""
|
||||
Test _status_table() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
buy=MagicMock(return_value={'id': 'mocked_order_id'}),
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
@@ -377,7 +333,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
|
||||
|
||||
def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
||||
limit_sell_order, mocker) -> None:
|
||||
limit_sell_order, markets, mocker) -> None:
|
||||
"""
|
||||
Test _daily() method
|
||||
"""
|
||||
@@ -388,16 +344,17 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
||||
return_value=15000.0
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
@@ -457,7 +414,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker
|
||||
)
|
||||
@@ -465,7 +422,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
@@ -489,7 +446,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
||||
|
||||
|
||||
def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
limit_buy_order, limit_sell_order, markets, mocker) -> None:
|
||||
"""
|
||||
Test _profit() method
|
||||
"""
|
||||
@@ -497,16 +454,17 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
@@ -531,7 +489,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
msg_mock.reset_mock()
|
||||
|
||||
# Update the ticker with a market going up
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_ticker', ticker_sell_up)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker', ticker_sell_up)
|
||||
trade.update(limit_sell_order)
|
||||
|
||||
trade.close_date = datetime.utcnow()
|
||||
@@ -596,18 +554,17 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
|
||||
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_balances', return_value=mock_balance)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_ticker', side_effect=mock_ticker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=mock_balance)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
|
||||
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._balance(bot=MagicMock(), update=update)
|
||||
@@ -626,18 +583,16 @@ def test_zero_balance_handle(default_conf, update, mocker) -> None:
|
||||
Test _balance() method when the Exchange platform returns nothing
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_balances', return_value={})
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
||||
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._balance(bot=MagicMock(), update=update)
|
||||
@@ -650,41 +605,35 @@ def test_start_handle(default_conf, update, mocker) -> None:
|
||||
"""
|
||||
Test _start() method
|
||||
"""
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
assert freqtradebot.state == State.STOPPED
|
||||
telegram._start(bot=MagicMock(), update=update)
|
||||
assert freqtradebot.state == State.RUNNING
|
||||
assert msg_mock.call_count == 0
|
||||
assert msg_mock.call_count == 1
|
||||
|
||||
|
||||
def test_start_handle_already_running(default_conf, update, mocker) -> None:
|
||||
"""
|
||||
Test _start() method
|
||||
"""
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
@@ -700,16 +649,14 @@ def test_stop_handle(default_conf, update, mocker) -> None:
|
||||
Test _stop() method
|
||||
"""
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
@@ -725,16 +672,14 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None:
|
||||
Test _stop() method
|
||||
"""
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
@@ -748,16 +693,14 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None:
|
||||
def test_reload_conf_handle(default_conf, update, mocker) -> None:
|
||||
""" Test _reload_conf() method """
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
@@ -768,7 +711,8 @@ def test_reload_conf_handle(default_conf, update, mocker) -> None:
|
||||
assert 'Reloading config' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, mocker) -> None:
|
||||
def test_forcesell_handle(default_conf, update, ticker, fee,
|
||||
ticker_sell_up, markets, mocker) -> None:
|
||||
"""
|
||||
Test _forcesell() method
|
||||
"""
|
||||
@@ -778,10 +722,11 @@ def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, moc
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
@@ -794,7 +739,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, moc
|
||||
assert trade
|
||||
|
||||
# Increase the price and sell it
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_ticker', ticker_sell_up)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker', ticker_sell_up)
|
||||
|
||||
update.message.text = '/forcesell 1'
|
||||
telegram._forcesell(bot=MagicMock(), update=update)
|
||||
@@ -808,7 +753,8 @@ def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, moc
|
||||
assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0]
|
||||
|
||||
|
||||
def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_down, mocker) -> None:
|
||||
def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
ticker_sell_down, markets, mocker) -> None:
|
||||
"""
|
||||
Test _forcesell() method
|
||||
"""
|
||||
@@ -818,10 +764,11 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_do
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
@@ -832,7 +779,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_do
|
||||
|
||||
# Decrease the price and sell it
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker_sell_down
|
||||
)
|
||||
@@ -852,7 +799,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_do
|
||||
assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0]
|
||||
|
||||
|
||||
def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
|
||||
"""
|
||||
Test _forcesell() method
|
||||
"""
|
||||
@@ -861,12 +808,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.get_pair_detail_url', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_pair_detail_url', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
@@ -898,9 +846,9 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
@@ -930,7 +878,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
||||
|
||||
|
||||
def test_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
limit_buy_order, limit_sell_order, markets, mocker) -> None:
|
||||
"""
|
||||
Test _performance() method
|
||||
"""
|
||||
@@ -940,13 +888,14 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
get_fee=fee
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
@@ -981,9 +930,9 @@ def test_performance_handle_invalid(default_conf, update, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
@@ -994,7 +943,7 @@ def test_performance_handle_invalid(default_conf, update, mocker) -> None:
|
||||
assert 'not running' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
|
||||
"""
|
||||
Test _count() method
|
||||
"""
|
||||
@@ -1004,15 +953,16 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker,
|
||||
buy=MagicMock(return_value={'id': 'mocked_order_id'})
|
||||
buy=MagicMock(return_value={'id': 'mocked_order_id'}),
|
||||
get_markets=markets
|
||||
)
|
||||
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
@@ -1042,14 +992,14 @@ def test_help_handle(default_conf, update, mocker) -> None:
|
||||
Test _help() method
|
||||
"""
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._help(bot=MagicMock(), update=update)
|
||||
@@ -1062,14 +1012,13 @@ def test_version_handle(default_conf, update, mocker) -> None:
|
||||
Test _version() method
|
||||
"""
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.telegram.Telegram',
|
||||
_init=MagicMock(),
|
||||
send_msg=msg_mock
|
||||
_send_msg=msg_mock
|
||||
)
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._version(bot=MagicMock(), update=update)
|
||||
@@ -1082,20 +1031,14 @@ def test_send_msg(default_conf, mocker) -> None:
|
||||
Test send_msg() method
|
||||
"""
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
conf = deepcopy(default_conf)
|
||||
bot = MagicMock()
|
||||
freqtradebot = FreqtradeBot(conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._config['telegram']['enabled'] = False
|
||||
telegram.send_msg('test', bot)
|
||||
assert not bot.method_calls
|
||||
bot.reset_mock()
|
||||
|
||||
telegram._config['telegram']['enabled'] = True
|
||||
telegram.send_msg('test', bot)
|
||||
telegram._send_msg('test', bot)
|
||||
assert len(bot.method_calls) == 1
|
||||
|
||||
|
||||
@@ -1104,16 +1047,15 @@ def test_send_msg_network_error(default_conf, mocker, caplog) -> None:
|
||||
Test send_msg() method
|
||||
"""
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.init', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
conf = deepcopy(default_conf)
|
||||
bot = MagicMock()
|
||||
bot.send_message = MagicMock(side_effect=NetworkError('Oh snap'))
|
||||
freqtradebot = FreqtradeBot(conf)
|
||||
freqtradebot = get_patched_freqtradebot(mocker, conf)
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._config['telegram']['enabled'] = True
|
||||
telegram.send_msg('test', bot)
|
||||
telegram._send_msg('test', bot)
|
||||
|
||||
# Bot should've tried to send it twice
|
||||
assert len(bot.method_calls) == 2
|
||||
|
||||
@@ -1,14 +1,39 @@
|
||||
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.strategy import import_strategy
|
||||
from freqtrade.strategy.default_strategy import DefaultStrategy
|
||||
from freqtrade.strategy.interface import IStrategy
|
||||
from freqtrade.strategy.resolver import StrategyResolver
|
||||
|
||||
|
||||
def test_import_strategy(caplog):
|
||||
caplog.set_level(logging.DEBUG)
|
||||
|
||||
strategy = DefaultStrategy()
|
||||
strategy.some_method = lambda *args, **kwargs: 42
|
||||
|
||||
assert strategy.__module__ == 'freqtrade.strategy.default_strategy'
|
||||
assert strategy.some_method() == 42
|
||||
|
||||
imported_strategy = import_strategy(strategy)
|
||||
|
||||
assert dir(strategy) == dir(imported_strategy)
|
||||
|
||||
assert imported_strategy.__module__ == 'freqtrade.strategy'
|
||||
assert imported_strategy.some_method() == 42
|
||||
|
||||
assert (
|
||||
'freqtrade.strategy',
|
||||
logging.DEBUG,
|
||||
'Imported strategy freqtrade.strategy.default_strategy.DefaultStrategy '
|
||||
'as freqtrade.strategy.DefaultStrategy',
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_search_strategy():
|
||||
default_location = os.path.join(os.path.dirname(
|
||||
os.path.realpath(__file__)), '..', '..', 'strategy'
|
||||
@@ -20,19 +45,21 @@ def test_search_strategy():
|
||||
|
||||
|
||||
def test_load_strategy(result):
|
||||
resolver = StrategyResolver()
|
||||
resolver._load_strategy('TestStrategy')
|
||||
resolver = StrategyResolver({'strategy': 'TestStrategy'})
|
||||
assert hasattr(resolver.strategy, 'populate_indicators')
|
||||
assert 'adx' in resolver.strategy.populate_indicators(result)
|
||||
|
||||
|
||||
def test_load_strategy_custom_directory(result):
|
||||
def test_load_strategy_invalid_directory(result, caplog):
|
||||
resolver = StrategyResolver()
|
||||
extra_dir = os.path.join('some', 'path')
|
||||
with pytest.raises(
|
||||
FileNotFoundError,
|
||||
match=r".*No such file or directory: '{}'".format(extra_dir)):
|
||||
resolver._load_strategy('TestStrategy', extra_dir)
|
||||
resolver._load_strategy('TestStrategy', extra_dir)
|
||||
|
||||
assert (
|
||||
'freqtrade.strategy.resolver',
|
||||
logging.WARNING,
|
||||
'Path "{}" does not exist'.format(extra_dir),
|
||||
) in caplog.record_tuples
|
||||
|
||||
assert hasattr(resolver.strategy, 'populate_indicators')
|
||||
assert 'adx' in resolver.strategy.populate_indicators(result)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# pragma pylint: disable=missing-docstring,C0103,protected-access
|
||||
|
||||
import freqtrade.tests.conftest as tt # test tools
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import freqtrade.tests.conftest as tt # test tools
|
||||
|
||||
# whitelist, blacklist, filtering, all of that will
|
||||
# eventually become some rules to run on a generic ACL engine
|
||||
# perhaps try to anticipate that by using some python package
|
||||
@@ -32,7 +33,7 @@ def test_refresh_market_pair_not_in_whitelist(mocker, markets):
|
||||
|
||||
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
|
||||
refreshedwhitelist = freqtradebot._refresh_whitelist(
|
||||
conf['exchange']['pair_whitelist'] + ['XXX/BTC']
|
||||
)
|
||||
@@ -46,7 +47,7 @@ def test_refresh_whitelist(mocker, markets):
|
||||
conf = whitelist_conf()
|
||||
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
|
||||
refreshedwhitelist = freqtradebot._refresh_whitelist(conf['exchange']['pair_whitelist'])
|
||||
|
||||
# List ordered by BaseVolume
|
||||
@@ -59,7 +60,7 @@ def test_refresh_whitelist_dynamic(mocker, markets, tickers):
|
||||
conf = whitelist_conf()
|
||||
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
'freqtrade.exchange.Exchange',
|
||||
get_markets=markets,
|
||||
get_tickers=tickers,
|
||||
exchange_has=MagicMock(return_value=True)
|
||||
@@ -78,7 +79,7 @@ def test_refresh_whitelist_dynamic(mocker, markets, tickers):
|
||||
def test_refresh_whitelist_dynamic_empty(mocker, markets_empty):
|
||||
conf = whitelist_conf()
|
||||
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets_empty)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets_empty)
|
||||
|
||||
# argument: use the whitelist dynamically by exchange-volume
|
||||
whitelist = []
|
||||
|
||||
@@ -12,9 +12,9 @@ import arrow
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.analyze import Analyze, SignalType
|
||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||
from freqtrade.arguments import TimeRange
|
||||
from freqtrade.tests.conftest import log_has
|
||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||
from freqtrade.tests.conftest import get_patched_exchange, log_has
|
||||
|
||||
# Avoid to reinit the same object again and again
|
||||
_ANALYZE = Analyze({'strategy': 'DefaultStrategy'})
|
||||
@@ -42,6 +42,7 @@ def test_analyze_object() -> None:
|
||||
assert hasattr(Analyze, 'get_signal')
|
||||
assert hasattr(Analyze, 'should_sell')
|
||||
assert hasattr(Analyze, 'min_roi_reached')
|
||||
assert hasattr(Analyze, 'stop_loss_reached')
|
||||
|
||||
|
||||
def test_dataframe_correct_length(result):
|
||||
@@ -68,16 +69,16 @@ def test_populates_sell_trend(result):
|
||||
assert 'sell' in dataframe.columns
|
||||
|
||||
|
||||
def test_returns_latest_buy_signal(mocker):
|
||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=MagicMock())
|
||||
|
||||
def test_returns_latest_buy_signal(mocker, default_conf):
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
return_value=DataFrame([{'buy': 1, 'sell': 0, 'date': arrow.utcnow()}])
|
||||
)
|
||||
)
|
||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (True, False)
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
|
||||
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
@@ -85,11 +86,12 @@ def test_returns_latest_buy_signal(mocker):
|
||||
return_value=DataFrame([{'buy': 0, 'sell': 1, 'date': arrow.utcnow()}])
|
||||
)
|
||||
)
|
||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (False, True)
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
|
||||
|
||||
|
||||
def test_returns_latest_sell_signal(mocker):
|
||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=MagicMock())
|
||||
def test_returns_latest_sell_signal(mocker, default_conf):
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
@@ -97,7 +99,7 @@ def test_returns_latest_sell_signal(mocker):
|
||||
)
|
||||
)
|
||||
|
||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (False, True)
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
|
||||
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
@@ -105,45 +107,49 @@ def test_returns_latest_sell_signal(mocker):
|
||||
return_value=DataFrame([{'sell': 0, 'buy': 1, 'date': arrow.utcnow()}])
|
||||
)
|
||||
)
|
||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (True, False)
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
|
||||
|
||||
|
||||
def test_get_signal_empty(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=None)
|
||||
assert (False, False) == _ANALYZE.get_signal('foo', default_conf['ticker_interval'])
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=None)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
assert (False, False) == _ANALYZE.get_signal(exchange, 'foo', default_conf['ticker_interval'])
|
||||
assert log_has('Empty ticker history for pair foo', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_signal_exception_valueerror(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=1)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
side_effect=ValueError('xyz')
|
||||
)
|
||||
)
|
||||
assert (False, False) == _ANALYZE.get_signal('foo', default_conf['ticker_interval'])
|
||||
assert (False, False) == _ANALYZE.get_signal(exchange, 'foo', default_conf['ticker_interval'])
|
||||
assert log_has('Unable to analyze ticker for pair foo: xyz', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_signal_empty_dataframe(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=1)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
return_value=DataFrame([])
|
||||
)
|
||||
)
|
||||
assert (False, False) == _ANALYZE.get_signal('xyz', default_conf['ticker_interval'])
|
||||
assert (False, False) == _ANALYZE.get_signal(exchange, 'xyz', default_conf['ticker_interval'])
|
||||
assert log_has('Empty dataframe for pair xyz', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_signal_old_dataframe(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=1)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
# FIX: The get_signal function has hardcoded 10, which we must inturn hardcode
|
||||
oldtime = arrow.utcnow() - datetime.timedelta(minutes=11)
|
||||
ticks = DataFrame([{'buy': 1, 'date': oldtime}])
|
||||
@@ -153,15 +159,16 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog):
|
||||
return_value=DataFrame(ticks)
|
||||
)
|
||||
)
|
||||
assert (False, False) == _ANALYZE.get_signal('xyz', default_conf['ticker_interval'])
|
||||
assert (False, False) == _ANALYZE.get_signal(exchange, 'xyz', default_conf['ticker_interval'])
|
||||
assert log_has(
|
||||
'Outdated history for pair xyz. Last tick is 11 minutes old',
|
||||
caplog.record_tuples
|
||||
)
|
||||
|
||||
|
||||
def test_get_signal_handles_exceptions(mocker):
|
||||
mocker.patch('freqtrade.analyze.get_ticker_history', return_value=MagicMock())
|
||||
def test_get_signal_handles_exceptions(mocker, default_conf):
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
@@ -169,7 +176,7 @@ def test_get_signal_handles_exceptions(mocker):
|
||||
)
|
||||
)
|
||||
|
||||
assert _ANALYZE.get_signal('ETH/BTC', '5m') == (False, False)
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
|
||||
|
||||
|
||||
def test_parse_ticker_dataframe(ticker_history):
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
Unit test file for configuration.py
|
||||
"""
|
||||
import json
|
||||
from argparse import Namespace
|
||||
from copy import deepcopy
|
||||
from unittest.mock import MagicMock
|
||||
from argparse import Namespace
|
||||
|
||||
import pytest
|
||||
from jsonschema import ValidationError
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.arguments import Arguments
|
||||
from freqtrade.configuration import Configuration
|
||||
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
|
||||
from freqtrade.tests.conftest import log_has
|
||||
from freqtrade import OperationalException
|
||||
|
||||
|
||||
def test_configuration_object() -> None:
|
||||
@@ -54,6 +55,18 @@ def test_load_config_missing_attributes(default_conf) -> None:
|
||||
configuration._validate_config(conf)
|
||||
|
||||
|
||||
def test_load_config_incorrect_stake_amount(default_conf) -> None:
|
||||
"""
|
||||
Test the configuration validator with a missing attribute
|
||||
"""
|
||||
conf = deepcopy(default_conf)
|
||||
conf['stake_amount'] = 'fake'
|
||||
|
||||
with pytest.raises(ValidationError, match=r'.*\'fake\' does not match \'unlimited\'.*'):
|
||||
configuration = Configuration(Namespace())
|
||||
configuration._validate_config(conf)
|
||||
|
||||
|
||||
def test_load_config_file(default_conf, mocker, caplog) -> None:
|
||||
"""
|
||||
Test Configuration._load_config_file() method
|
||||
@@ -140,6 +153,43 @@ def test_load_config_with_params(default_conf, mocker) -> None:
|
||||
assert validated_conf.get('strategy_path') == '/some/path'
|
||||
assert validated_conf.get('db_url') == 'sqlite:///someurl'
|
||||
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = False
|
||||
del conf["db_url"]
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(conf)
|
||||
))
|
||||
|
||||
arglist = [
|
||||
'--dynamic-whitelist', '10',
|
||||
'--strategy', 'TestStrategy',
|
||||
'--strategy-path', '/some/path'
|
||||
]
|
||||
args = Arguments(arglist, '').get_parsed_arg()
|
||||
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
assert validated_conf.get('db_url') == DEFAULT_DB_PROD_URL
|
||||
|
||||
# Test dry=run with ProdURL
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = True
|
||||
conf["db_url"] = DEFAULT_DB_PROD_URL
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(conf)
|
||||
))
|
||||
|
||||
arglist = [
|
||||
'--dynamic-whitelist', '10',
|
||||
'--strategy', 'TestStrategy',
|
||||
'--strategy-path', '/some/path'
|
||||
]
|
||||
args = Arguments(arglist, '').get_parsed_arg()
|
||||
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
assert validated_conf.get('db_url') == DEFAULT_DB_DRYRUN_URL
|
||||
|
||||
|
||||
def test_load_custom_strategy(default_conf, mocker) -> None:
|
||||
"""
|
||||
@@ -310,7 +360,6 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
|
||||
arglist = [
|
||||
'hyperopt',
|
||||
'--epochs', '10',
|
||||
'--use-mongodb',
|
||||
'--spaces', 'all',
|
||||
]
|
||||
|
||||
@@ -324,10 +373,6 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
|
||||
assert log_has('Parameter --epochs detected ...', caplog.record_tuples)
|
||||
assert log_has('Will run Hyperopt with for 10 epochs ...', caplog.record_tuples)
|
||||
|
||||
assert 'mongodb' in config
|
||||
assert config['mongodb'] is True
|
||||
assert log_has('Parameter --use-mongodb detected ...', caplog.record_tuples)
|
||||
|
||||
assert 'spaces' in config
|
||||
assert config['spaces'] == ['all']
|
||||
assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog.record_tuples)
|
||||
|
||||
@@ -5,7 +5,6 @@ import time
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
from freqtrade.fiat_convert import CryptoFiat, CryptoToFiatConverter
|
||||
@@ -40,7 +39,8 @@ def test_pair_convertion_object():
|
||||
assert pair_convertion.price == 30000.123
|
||||
|
||||
|
||||
def test_fiat_convert_is_supported():
|
||||
def test_fiat_convert_is_supported(mocker):
|
||||
patch_coinmarketcap(mocker)
|
||||
fiat_convert = CryptoToFiatConverter()
|
||||
assert fiat_convert._is_supported_fiat(fiat='USD') is True
|
||||
assert fiat_convert._is_supported_fiat(fiat='usd') is True
|
||||
@@ -48,7 +48,9 @@ def test_fiat_convert_is_supported():
|
||||
assert fiat_convert._is_supported_fiat(fiat='ABC') is False
|
||||
|
||||
|
||||
def test_fiat_convert_add_pair():
|
||||
def test_fiat_convert_add_pair(mocker):
|
||||
patch_coinmarketcap(mocker)
|
||||
|
||||
fiat_convert = CryptoToFiatConverter()
|
||||
|
||||
pair_len = len(fiat_convert._pairs)
|
||||
@@ -70,11 +72,8 @@ def test_fiat_convert_add_pair():
|
||||
|
||||
|
||||
def test_fiat_convert_find_price(mocker):
|
||||
api_mock = MagicMock(return_value={
|
||||
'price_usd': 12345.0,
|
||||
'price_eur': 13000.2
|
||||
})
|
||||
mocker.patch('freqtrade.fiat_convert.Market.ticker', api_mock)
|
||||
patch_coinmarketcap(mocker)
|
||||
|
||||
fiat_convert = CryptoToFiatConverter()
|
||||
|
||||
with pytest.raises(ValueError, match=r'The fiat ABC is not supported.'):
|
||||
@@ -92,17 +91,15 @@ def test_fiat_convert_find_price(mocker):
|
||||
|
||||
def test_fiat_convert_unsupported_crypto(mocker, caplog):
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._cryptomap', return_value=[])
|
||||
patch_coinmarketcap(mocker)
|
||||
fiat_convert = CryptoToFiatConverter()
|
||||
assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0
|
||||
assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_fiat_convert_get_price(mocker):
|
||||
api_mock = MagicMock(return_value={
|
||||
'price_usd': 28000.0,
|
||||
'price_eur': 15000.0
|
||||
})
|
||||
mocker.patch('freqtrade.fiat_convert.Market.ticker', api_mock)
|
||||
patch_coinmarketcap(mocker)
|
||||
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=28000.0)
|
||||
|
||||
fiat_convert = CryptoToFiatConverter()
|
||||
@@ -172,8 +169,9 @@ def test_fiat_init_network_exception(mocker):
|
||||
assert length_cryptomap == 0
|
||||
|
||||
|
||||
def test_fiat_convert_without_network():
|
||||
def test_fiat_convert_without_network(mocker):
|
||||
# Because CryptoToFiatConverter is a Singleton we reset the value of _coinmarketcap
|
||||
patch_coinmarketcap(mocker)
|
||||
|
||||
fiat_convert = CryptoToFiatConverter()
|
||||
|
||||
@@ -186,6 +184,7 @@ def test_fiat_convert_without_network():
|
||||
|
||||
|
||||
def test_convert_amount(mocker):
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter.get_price', return_value=12345.0)
|
||||
|
||||
fiat_convert = CryptoToFiatConverter()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
import pandas as pd
|
||||
|
||||
from freqtrade.indicator_helpers import went_up, went_down
|
||||
from freqtrade.indicator_helpers import went_down, went_up
|
||||
|
||||
|
||||
def test_went_up():
|
||||
|
||||
@@ -11,9 +11,9 @@ import pytest
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.arguments import Arguments
|
||||
from freqtrade.freqtradebot import FreqtradeBot
|
||||
from freqtrade.main import main, set_loggers, reconfigure
|
||||
from freqtrade.main import main, reconfigure, set_loggers
|
||||
from freqtrade.state import State
|
||||
from freqtrade.tests.conftest import log_has
|
||||
from freqtrade.tests.conftest import log_has, patch_exchange
|
||||
|
||||
|
||||
def test_parse_args_backtesting(mocker) -> None:
|
||||
@@ -70,6 +70,7 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
|
||||
Test main() function
|
||||
In this test we are skipping the while True loop by throwing an exception.
|
||||
"""
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.FreqtradeBot',
|
||||
_init_modules=MagicMock(),
|
||||
@@ -97,6 +98,7 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
|
||||
Test main() function
|
||||
In this test we are skipping the while True loop by throwing an exception.
|
||||
"""
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.FreqtradeBot',
|
||||
_init_modules=MagicMock(),
|
||||
@@ -124,6 +126,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
|
||||
Test main() function
|
||||
In this test we are skipping the while True loop by throwing an exception.
|
||||
"""
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.FreqtradeBot',
|
||||
_init_modules=MagicMock(),
|
||||
@@ -151,6 +154,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None:
|
||||
Test main() function
|
||||
In this test we are skipping the while True loop by throwing an exception.
|
||||
"""
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.FreqtradeBot',
|
||||
_init_modules=MagicMock(),
|
||||
@@ -178,6 +182,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None:
|
||||
|
||||
def test_reconfigure(mocker, default_conf) -> None:
|
||||
""" Test recreate() function """
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.FreqtradeBot',
|
||||
_init_modules=MagicMock(),
|
||||
|
||||
@@ -8,8 +8,8 @@ import datetime
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.misc import (shorten_date, datesarray_to_datetimearray,
|
||||
common_datearray, file_dump_json, format_ms_time)
|
||||
from freqtrade.misc import (common_datearray, datesarray_to_datetimearray,
|
||||
file_dump_json, format_ms_time, shorten_date)
|
||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@ from unittest.mock import MagicMock
|
||||
import pytest
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
from freqtrade import constants, OperationalException
|
||||
from freqtrade.persistence import Trade, init, clean_dry_run_db
|
||||
from freqtrade import OperationalException, constants
|
||||
from freqtrade.persistence import Trade, clean_dry_run_db, init
|
||||
from freqtrade.tests.conftest import log_has
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
@@ -14,9 +15,7 @@ def init_persistence(default_conf):
|
||||
init(default_conf)
|
||||
|
||||
|
||||
def test_init_create_session(default_conf, mocker):
|
||||
mocker.patch.dict('freqtrade.persistence._CONF', default_conf)
|
||||
|
||||
def test_init_create_session(default_conf):
|
||||
# Check if init create a session
|
||||
init(default_conf)
|
||||
assert hasattr(Trade, 'session')
|
||||
@@ -29,20 +28,17 @@ def test_init_custom_db_url(default_conf, mocker):
|
||||
# Update path to a value other than default, but still in-memory
|
||||
conf.update({'db_url': 'sqlite:///tmp/freqtrade2_test.sqlite'})
|
||||
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
||||
mocker.patch.dict('freqtrade.persistence._CONF', conf)
|
||||
|
||||
init(conf)
|
||||
assert create_engine_mock.call_count == 1
|
||||
assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:///tmp/freqtrade2_test.sqlite'
|
||||
|
||||
|
||||
def test_init_invalid_db_url(default_conf, mocker):
|
||||
def test_init_invalid_db_url(default_conf):
|
||||
conf = deepcopy(default_conf)
|
||||
|
||||
# Update path to a value other than default, but still in-memory
|
||||
conf.update({'db_url': 'unknown:///some.url'})
|
||||
mocker.patch.dict('freqtrade.persistence._CONF', conf)
|
||||
|
||||
with pytest.raises(OperationalException, match=r'.*no valid database URL*'):
|
||||
init(conf)
|
||||
|
||||
@@ -53,7 +49,6 @@ def test_init_prod_db(default_conf, mocker):
|
||||
conf.update({'db_url': constants.DEFAULT_DB_PROD_URL})
|
||||
|
||||
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
||||
mocker.patch.dict('freqtrade.persistence._CONF', conf)
|
||||
|
||||
init(conf)
|
||||
assert create_engine_mock.call_count == 1
|
||||
@@ -66,7 +61,6 @@ def test_init_dryrun_db(default_conf, mocker):
|
||||
conf.update({'db_url': constants.DEFAULT_DB_DRYRUN_URL})
|
||||
|
||||
create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
|
||||
mocker.patch.dict('freqtrade.persistence._CONF', conf)
|
||||
|
||||
init(conf)
|
||||
assert create_engine_mock.call_count == 1
|
||||
@@ -407,9 +401,12 @@ def test_migrate_old(mocker, default_conf, fee):
|
||||
assert trade.stake_amount == default_conf.get("stake_amount")
|
||||
assert trade.pair == "ETC/BTC"
|
||||
assert trade.exchange == "bittrex"
|
||||
assert trade.max_rate == 0.0
|
||||
assert trade.stop_loss == 0.0
|
||||
assert trade.initial_stop_loss == 0.0
|
||||
|
||||
|
||||
def test_migrate_new(mocker, default_conf, fee):
|
||||
def test_migrate_new(mocker, default_conf, fee, caplog):
|
||||
"""
|
||||
Test Database migration (starting with new pairformat)
|
||||
"""
|
||||
@@ -446,6 +443,11 @@ def test_migrate_new(mocker, default_conf, fee):
|
||||
# Create table using the old format
|
||||
engine.execute(create_table_old)
|
||||
engine.execute(insert_table_old)
|
||||
|
||||
# fake previous backup
|
||||
engine.execute("create table trades_bak as select * from trades")
|
||||
|
||||
engine.execute("create table trades_bak1 as select * from trades")
|
||||
# Run init to test migration
|
||||
init(default_conf)
|
||||
|
||||
@@ -460,3 +462,54 @@ def test_migrate_new(mocker, default_conf, fee):
|
||||
assert trade.stake_amount == default_conf.get("stake_amount")
|
||||
assert trade.pair == "ETC/BTC"
|
||||
assert trade.exchange == "binance"
|
||||
assert trade.max_rate == 0.0
|
||||
assert trade.stop_loss == 0.0
|
||||
assert trade.initial_stop_loss == 0.0
|
||||
assert log_has("trying trades_bak1", caplog.record_tuples)
|
||||
assert log_has("trying trades_bak2", caplog.record_tuples)
|
||||
|
||||
|
||||
def test_adjust_stop_loss(limit_buy_order, limit_sell_order, fee):
|
||||
trade = Trade(
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
exchange='bittrex',
|
||||
open_rate=1,
|
||||
)
|
||||
|
||||
trade.adjust_stop_loss(trade.open_rate, 0.05, True)
|
||||
assert trade.stop_loss == 0.95
|
||||
assert trade.max_rate == 1
|
||||
assert trade.initial_stop_loss == 0.95
|
||||
|
||||
# Get percent of profit with a lowre rate
|
||||
trade.adjust_stop_loss(0.96, 0.05)
|
||||
assert trade.stop_loss == 0.95
|
||||
assert trade.max_rate == 1
|
||||
assert trade.initial_stop_loss == 0.95
|
||||
|
||||
# Get percent of profit with a custom rate (Higher than open rate)
|
||||
trade.adjust_stop_loss(1.3, -0.1)
|
||||
assert round(trade.stop_loss, 8) == 1.17
|
||||
assert trade.max_rate == 1.3
|
||||
assert trade.initial_stop_loss == 0.95
|
||||
|
||||
# current rate lower again ... should not change
|
||||
trade.adjust_stop_loss(1.2, 0.1)
|
||||
assert round(trade.stop_loss, 8) == 1.17
|
||||
assert trade.max_rate == 1.3
|
||||
assert trade.initial_stop_loss == 0.95
|
||||
|
||||
# current rate higher... should raise stoploss
|
||||
trade.adjust_stop_loss(1.4, 0.1)
|
||||
assert round(trade.stop_loss, 8) == 1.26
|
||||
assert trade.max_rate == 1.4
|
||||
assert trade.initial_stop_loss == 0.95
|
||||
|
||||
# Initial is true but stop_loss set - so doesn't do anything
|
||||
trade.adjust_stop_loss(1.7, 0.1, True)
|
||||
assert round(trade.stop_loss, 8) == 1.26
|
||||
assert trade.max_rate == 1.4
|
||||
assert trade.initial_stop_loss == 0.95
|
||||
|
||||
Reference in New Issue
Block a user