2018-01-28 07:38:41 +00:00
|
|
|
# pragma pylint: disable=missing-docstring, C0103
|
2017-10-01 08:02:47 +00:00
|
|
|
import copy
|
2018-01-10 07:51:36 +00:00
|
|
|
import logging
|
2017-11-07 21:27:44 +00:00
|
|
|
from unittest.mock import MagicMock
|
|
|
|
|
2018-01-10 07:51:36 +00:00
|
|
|
import arrow
|
2017-11-07 21:27:44 +00:00
|
|
|
import pytest
|
|
|
|
import requests
|
2017-11-09 22:45:22 +00:00
|
|
|
from sqlalchemy import create_engine
|
2017-09-08 13:51:00 +00:00
|
|
|
|
2018-01-10 07:51:36 +00:00
|
|
|
import freqtrade.main as main
|
2017-11-20 21:15:19 +00:00
|
|
|
from freqtrade import DependencyException, OperationalException
|
2017-11-20 21:26:32 +00:00
|
|
|
from freqtrade.exchange import Exchanges
|
2018-01-10 07:51:36 +00:00
|
|
|
from freqtrade.main import (_process, check_handle_timedout, create_trade,
|
|
|
|
execute_sell, get_target_bid, handle_trade, init)
|
|
|
|
from freqtrade.misc import State, get_state
|
2017-09-28 21:26:28 +00:00
|
|
|
from freqtrade.persistence import Trade
|
2018-02-24 19:18:53 +00:00
|
|
|
from freqtrade.tests.conftest import log_has
|
2018-01-06 10:21:09 +00:00
|
|
|
|
2018-01-10 09:25:45 +00:00
|
|
|
|
2018-01-06 10:21:09 +00:00
|
|
|
def test_parse_args_backtesting(mocker):
|
2018-01-10 09:25:45 +00:00
|
|
|
""" Test that main() can start backtesting or hyperopt.
|
|
|
|
and also ensure we can pass some specific arguments
|
2018-01-11 18:49:33 +00:00
|
|
|
further argument parsing is done in test_misc.py """
|
2018-01-06 10:21:09 +00:00
|
|
|
backtesting_mock = mocker.patch(
|
|
|
|
'freqtrade.optimize.backtesting.start', MagicMock())
|
2018-01-13 12:16:40 +00:00
|
|
|
main.main(['backtesting'])
|
2018-01-06 10:21:09 +00:00
|
|
|
assert backtesting_mock.call_count == 1
|
|
|
|
call_args = backtesting_mock.call_args[0][0]
|
|
|
|
assert call_args.config == 'config.json'
|
|
|
|
assert call_args.live is False
|
|
|
|
assert call_args.loglevel == 20
|
|
|
|
assert call_args.subparser == 'backtesting'
|
|
|
|
assert call_args.func is not None
|
2018-01-30 07:04:28 +00:00
|
|
|
assert call_args.ticker_interval is None
|
2018-01-06 10:21:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_main_start_hyperopt(mocker):
|
|
|
|
hyperopt_mock = mocker.patch(
|
|
|
|
'freqtrade.optimize.hyperopt.start', MagicMock())
|
2018-01-13 12:16:40 +00:00
|
|
|
main.main(['hyperopt'])
|
2018-01-06 10:21:09 +00:00
|
|
|
assert hyperopt_mock.call_count == 1
|
|
|
|
call_args = hyperopt_mock.call_args[0][0]
|
|
|
|
assert call_args.config == 'config.json'
|
|
|
|
assert call_args.loglevel == 20
|
|
|
|
assert call_args.subparser == 'hyperopt'
|
|
|
|
assert call_args.func is not None
|
2017-09-08 13:51:00 +00:00
|
|
|
|
|
|
|
|
2018-01-10 08:09:33 +00:00
|
|
|
def test_process_maybe_execute_buy(default_conf, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
mocker.patch('freqtrade.main.create_trade', return_value=True)
|
2018-01-26 16:48:53 +00:00
|
|
|
assert main.process_maybe_execute_buy(int(default_conf['ticker_interval']))
|
2018-01-10 08:09:33 +00:00
|
|
|
mocker.patch('freqtrade.main.create_trade', return_value=False)
|
2018-01-26 16:48:53 +00:00
|
|
|
assert not main.process_maybe_execute_buy(int(default_conf['ticker_interval']))
|
2018-01-10 08:09:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_process_maybe_execute_sell(default_conf, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
mocker.patch('freqtrade.main.handle_trade', return_value=True)
|
|
|
|
mocker.patch('freqtrade.exchange.get_order', return_value=1)
|
|
|
|
trade = MagicMock()
|
|
|
|
trade.open_order_id = '123'
|
2018-01-20 20:24:28 +00:00
|
|
|
assert not main.process_maybe_execute_sell(trade, int(default_conf['ticker_interval']))
|
2018-01-10 08:09:33 +00:00
|
|
|
trade.is_open = True
|
|
|
|
trade.open_order_id = None
|
|
|
|
# Assert we call handle_trade() if trade is feasible for execution
|
2018-01-20 20:24:28 +00:00
|
|
|
assert main.process_maybe_execute_sell(trade, int(default_conf['ticker_interval']))
|
2018-01-10 08:09:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_process_maybe_execute_buy_exception(default_conf, mocker, caplog):
|
2018-01-31 17:37:38 +00:00
|
|
|
caplog.set_level(logging.INFO)
|
2018-01-10 08:09:33 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
mocker.patch('freqtrade.main.create_trade', MagicMock(side_effect=DependencyException))
|
2018-01-26 16:48:53 +00:00
|
|
|
main.process_maybe_execute_buy(int(default_conf['ticker_interval']))
|
2018-02-24 19:18:53 +00:00
|
|
|
assert log_has('Unable to create trade: ', caplog.record_tuples)
|
2018-01-10 08:09:33 +00:00
|
|
|
|
|
|
|
|
2018-01-04 14:39:01 +00:00
|
|
|
def test_process_trade_creation(default_conf, ticker, limit_buy_order, health, mocker):
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
2017-11-13 20:34:47 +00:00
|
|
|
get_wallet_health=health,
|
2018-01-04 14:39:01 +00:00
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'),
|
|
|
|
get_order=MagicMock(return_value=limit_buy_order))
|
2017-11-09 22:45:22 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2017-11-07 21:27:44 +00:00
|
|
|
|
|
|
|
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
2017-11-18 07:58:55 +00:00
|
|
|
assert not trades
|
2017-11-07 21:27:44 +00:00
|
|
|
|
2018-01-15 21:27:12 +00:00
|
|
|
result = _process(interval=int(default_conf['ticker_interval']))
|
2017-11-07 21:27:44 +00:00
|
|
|
assert result is True
|
|
|
|
|
|
|
|
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
|
|
|
assert len(trades) == 1
|
|
|
|
trade = trades[0]
|
|
|
|
assert trade is not None
|
|
|
|
assert trade.stake_amount == default_conf['stake_amount']
|
|
|
|
assert trade.is_open
|
|
|
|
assert trade.open_date is not None
|
|
|
|
assert trade.exchange == Exchanges.BITTREX.name
|
2017-12-17 21:07:56 +00:00
|
|
|
assert trade.open_rate == 0.00001099
|
|
|
|
assert trade.amount == 90.99181073703367
|
2017-11-07 21:27:44 +00:00
|
|
|
|
|
|
|
|
2017-11-13 20:34:47 +00:00
|
|
|
def test_process_exchange_failures(default_conf, ticker, health, mocker):
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-07 21:27:44 +00:00
|
|
|
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
2017-11-13 20:34:47 +00:00
|
|
|
get_wallet_health=health,
|
2017-11-07 21:27:44 +00:00
|
|
|
buy=MagicMock(side_effect=requests.exceptions.RequestException))
|
2017-11-09 22:45:22 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
result = _process(interval=int(default_conf['ticker_interval']))
|
2017-11-07 21:27:44 +00:00
|
|
|
assert result is False
|
|
|
|
assert sleep_mock.has_calls()
|
|
|
|
|
|
|
|
|
2017-11-20 21:15:19 +00:00
|
|
|
def test_process_operational_exception(default_conf, ticker, health, mocker):
|
2017-11-07 21:27:44 +00:00
|
|
|
msg_mock = MagicMock()
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=msg_mock)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
2017-11-13 20:34:47 +00:00
|
|
|
get_wallet_health=health,
|
2017-11-20 21:15:19 +00:00
|
|
|
buy=MagicMock(side_effect=OperationalException))
|
2017-11-09 22:45:22 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2017-11-07 21:27:44 +00:00
|
|
|
assert get_state() == State.RUNNING
|
|
|
|
|
2018-01-15 21:27:12 +00:00
|
|
|
result = _process(interval=int(default_conf['ticker_interval']))
|
2017-11-07 21:27:44 +00:00
|
|
|
assert result is False
|
|
|
|
assert get_state() == State.STOPPED
|
2017-11-20 21:15:19 +00:00
|
|
|
assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]
|
2017-11-07 21:27:44 +00:00
|
|
|
|
|
|
|
|
2017-11-13 20:34:47 +00:00
|
|
|
def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, mocker):
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
2017-11-13 20:34:47 +00:00
|
|
|
get_wallet_health=health,
|
2017-11-07 21:27:44 +00:00
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'),
|
2017-11-08 21:44:32 +00:00
|
|
|
get_order=MagicMock(return_value=limit_buy_order))
|
2017-11-09 22:45:22 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2017-11-07 21:27:44 +00:00
|
|
|
|
2017-11-08 21:44:32 +00:00
|
|
|
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
2017-11-18 07:58:55 +00:00
|
|
|
assert not trades
|
2018-01-15 21:27:12 +00:00
|
|
|
result = _process(interval=int(default_conf['ticker_interval']))
|
2017-11-07 21:27:44 +00:00
|
|
|
assert result is True
|
2017-11-08 21:44:32 +00:00
|
|
|
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
|
|
|
assert len(trades) == 1
|
2017-11-07 21:27:44 +00:00
|
|
|
|
2018-01-15 21:27:12 +00:00
|
|
|
result = _process(interval=int(default_conf['ticker_interval']))
|
2017-11-07 21:27:44 +00:00
|
|
|
assert result is False
|
|
|
|
|
|
|
|
|
2017-11-07 19:12:56 +00:00
|
|
|
def test_create_trade(default_conf, ticker, limit_buy_order, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2017-10-01 12:25:10 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
2017-10-01 21:28:09 +00:00
|
|
|
validate_pairs=MagicMock(),
|
2017-11-07 19:12:56 +00:00
|
|
|
get_ticker=ticker,
|
2017-10-31 23:26:32 +00:00
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
2017-10-01 12:25:10 +00:00
|
|
|
# Save state of current whitelist
|
2017-11-07 19:12:56 +00:00
|
|
|
whitelist = copy.deepcopy(default_conf['exchange']['pair_whitelist'])
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2017-11-09 22:45:22 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-11-22 19:51:25 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
2017-11-07 21:27:44 +00:00
|
|
|
assert trade is not None
|
2017-12-17 21:07:56 +00:00
|
|
|
assert trade.stake_amount == 0.001
|
2017-11-07 21:27:44 +00:00
|
|
|
assert trade.is_open
|
|
|
|
assert trade.open_date is not None
|
|
|
|
assert trade.exchange == Exchanges.BITTREX.name
|
|
|
|
|
|
|
|
# Simulate fulfilled LIMIT_BUY order for trade
|
|
|
|
trade.update(limit_buy_order)
|
2017-10-31 23:26:32 +00:00
|
|
|
|
2017-12-17 21:07:56 +00:00
|
|
|
assert trade.open_rate == 0.00001099
|
|
|
|
assert trade.amount == 90.99181073
|
2017-11-07 21:27:44 +00:00
|
|
|
|
|
|
|
assert whitelist == default_conf['exchange']['pair_whitelist']
|
|
|
|
|
|
|
|
|
2017-11-22 20:02:36 +00:00
|
|
|
def test_create_trade_minimal_amount(default_conf, ticker, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-16 02:39:47 +00:00
|
|
|
buy_mock = mocker.patch(
|
|
|
|
'freqtrade.main.exchange.buy', MagicMock(return_value='mocked_limit_buy')
|
|
|
|
)
|
2017-11-22 20:02:36 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker)
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
|
|
|
min_stake_amount = 0.0005
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(min_stake_amount, int(default_conf['ticker_interval']))
|
2017-11-22 20:02:36 +00:00
|
|
|
rate, amount = buy_mock.call_args[0][1], buy_mock.call_args[0][2]
|
|
|
|
assert rate * amount >= min_stake_amount
|
|
|
|
|
|
|
|
|
2017-11-07 21:27:44 +00:00
|
|
|
def test_create_trade_no_stake_amount(default_conf, ticker, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'),
|
|
|
|
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5))
|
2017-11-20 21:15:19 +00:00
|
|
|
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(default_conf['stake_amount'], int(default_conf['ticker_interval']))
|
2017-11-07 19:12:56 +00:00
|
|
|
|
2017-10-31 23:26:32 +00:00
|
|
|
|
2017-11-07 21:27:44 +00:00
|
|
|
def test_create_trade_no_pairs(default_conf, ticker, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2018-02-24 17:51:37 +00:00
|
|
|
with pytest.raises(DependencyException, match=r'.*No currency pairs in whitelist.*'):
|
2017-11-07 21:27:44 +00:00
|
|
|
conf = copy.deepcopy(default_conf)
|
|
|
|
conf['exchange']['pair_whitelist'] = []
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', conf)
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(default_conf['stake_amount'], int(default_conf['ticker_interval']))
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2017-10-30 23:36:35 +00:00
|
|
|
|
2017-12-30 13:15:07 +00:00
|
|
|
def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-30 13:15:07 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
|
|
|
|
2018-02-24 17:51:37 +00:00
|
|
|
with pytest.raises(DependencyException, match=r'.*No currency pairs in whitelist.*'):
|
2017-12-30 13:15:07 +00:00
|
|
|
conf = copy.deepcopy(default_conf)
|
|
|
|
conf['exchange']['pair_whitelist'] = ["BTC_ETH"]
|
|
|
|
conf['exchange']['pair_blacklist'] = ["BTC_ETH"]
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', conf)
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(default_conf['stake_amount'], int(default_conf['ticker_interval']))
|
2017-12-30 13:15:07 +00:00
|
|
|
|
|
|
|
|
2018-01-26 16:56:56 +00:00
|
|
|
def test_create_trade_no_signal(default_conf, mocker):
|
2018-01-10 09:29:04 +00:00
|
|
|
default_conf['dry_run'] = True
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-19 07:05:52 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', MagicMock(return_value=(False, False)))
|
2018-01-10 09:29:04 +00:00
|
|
|
mocker.patch.multiple('freqtrade.exchange',
|
|
|
|
get_ticker_history=MagicMock(return_value=20))
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
get_balance=MagicMock(return_value=20))
|
|
|
|
stake_amount = 10
|
|
|
|
Trade.query = MagicMock()
|
|
|
|
Trade.query.filter = MagicMock()
|
2018-01-20 20:24:28 +00:00
|
|
|
assert not create_trade(stake_amount, int(default_conf['ticker_interval']))
|
2018-01-10 09:29:04 +00:00
|
|
|
|
|
|
|
|
2017-11-07 21:27:44 +00:00
|
|
|
def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker):
|
2017-11-07 19:12:56 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2017-10-01 12:25:10 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
2017-10-01 21:28:09 +00:00
|
|
|
validate_pairs=MagicMock(),
|
2017-10-01 12:25:10 +00:00
|
|
|
get_ticker=MagicMock(return_value={
|
2017-12-17 21:07:56 +00:00
|
|
|
'bid': 0.00001172,
|
|
|
|
'ask': 0.00001173,
|
|
|
|
'last': 0.00001172
|
2017-10-01 12:25:10 +00:00
|
|
|
}),
|
2017-11-07 21:27:44 +00:00
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'),
|
2017-10-31 23:26:32 +00:00
|
|
|
sell=MagicMock(return_value='mocked_limit_sell'))
|
2018-03-17 23:42:24 +00:00
|
|
|
mocker.patch.multiple('freqtrade.fiat_convert.Market',
|
|
|
|
ticker=MagicMock(return_value={'price_usd': 15000.0}))
|
2017-11-09 22:45:22 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-11-22 19:51:25 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
2017-10-01 12:25:10 +00:00
|
|
|
assert trade
|
2017-10-31 23:26:32 +00:00
|
|
|
|
2017-11-22 19:51:25 +00:00
|
|
|
trade.update(limit_buy_order)
|
|
|
|
assert trade.is_open is True
|
|
|
|
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-15 21:27:12 +00:00
|
|
|
assert handle_trade(trade, int(default_conf['ticker_interval'])) is True
|
2017-10-31 23:26:32 +00:00
|
|
|
assert trade.open_order_id == 'mocked_limit_sell'
|
|
|
|
|
|
|
|
# Simulate fulfilled LIMIT_SELL order for trade
|
2017-11-07 19:12:56 +00:00
|
|
|
trade.update(limit_sell_order)
|
|
|
|
|
2017-12-17 21:07:56 +00:00
|
|
|
assert trade.close_rate == 0.00001173
|
|
|
|
assert trade.close_profit == 0.06201057
|
|
|
|
assert trade.calc_profit() == 0.00006217
|
2017-10-01 12:25:10 +00:00
|
|
|
assert trade.close_date is not None
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2017-12-29 08:40:24 +00:00
|
|
|
|
2018-01-26 16:56:56 +00:00
|
|
|
def test_handle_overlpapping_signals(default_conf, ticker, mocker):
|
2018-01-16 20:18:43 +00:00
|
|
|
default_conf.update({'experimental': {'use_sell_signal': True}})
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, True))
|
2018-01-16 20:18:43 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
|
|
|
mocker.patch('freqtrade.main.min_roi_reached', return_value=False)
|
|
|
|
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-20 18:25:47 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2018-01-16 20:18:43 +00:00
|
|
|
|
|
|
|
# Buy and Sell triggering, so doing nothing ...
|
|
|
|
trades = Trade.query.all()
|
2018-01-28 07:38:41 +00:00
|
|
|
nb_trades = len(trades)
|
|
|
|
assert nb_trades == 0
|
2018-01-16 20:18:43 +00:00
|
|
|
|
|
|
|
# Buy is triggering, so buying ...
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2018-01-20 18:25:47 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2018-01-16 20:18:43 +00:00
|
|
|
trades = Trade.query.all()
|
2018-01-28 07:38:41 +00:00
|
|
|
nb_trades = len(trades)
|
|
|
|
assert nb_trades == 1
|
2018-01-16 20:18:43 +00:00
|
|
|
assert trades[0].is_open is True
|
|
|
|
|
|
|
|
# Buy and Sell are not triggering, so doing nothing ...
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, False))
|
2018-01-20 18:25:47 +00:00
|
|
|
assert handle_trade(trades[0], int(default_conf['ticker_interval'])) is False
|
2018-01-16 20:18:43 +00:00
|
|
|
trades = Trade.query.all()
|
2018-01-28 07:38:41 +00:00
|
|
|
nb_trades = len(trades)
|
|
|
|
assert nb_trades == 1
|
2018-01-16 20:18:43 +00:00
|
|
|
assert trades[0].is_open is True
|
|
|
|
|
|
|
|
# Buy and Sell are triggering, so doing nothing ...
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, True))
|
2018-01-20 18:25:47 +00:00
|
|
|
assert handle_trade(trades[0], int(default_conf['ticker_interval'])) is False
|
2018-01-16 20:18:43 +00:00
|
|
|
trades = Trade.query.all()
|
2018-01-28 07:38:41 +00:00
|
|
|
nb_trades = len(trades)
|
|
|
|
assert nb_trades == 1
|
2018-01-16 20:18:43 +00:00
|
|
|
assert trades[0].is_open is True
|
|
|
|
|
|
|
|
# Sell is triggering, guess what : we are Selling!
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-16 20:18:43 +00:00
|
|
|
trades = Trade.query.all()
|
2018-01-20 18:25:47 +00:00
|
|
|
assert handle_trade(trades[0], int(default_conf['ticker_interval'])) is True
|
2018-01-16 20:18:43 +00:00
|
|
|
|
|
|
|
|
2018-01-10 08:13:41 +00:00
|
|
|
def test_handle_trade_roi(default_conf, ticker, mocker, caplog):
|
2018-01-31 17:37:38 +00:00
|
|
|
caplog.set_level(logging.DEBUG)
|
2017-12-28 23:00:30 +00:00
|
|
|
default_conf.update({'experimental': {'use_sell_signal': True}})
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-28 23:00:30 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
|
|
|
mocker.patch('freqtrade.main.min_roi_reached', return_value=True)
|
|
|
|
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-12-28 23:00:30 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
trade.is_open = True
|
|
|
|
|
|
|
|
# FIX: sniffing logs, suggest handle_trade should not execute_sell
|
|
|
|
# instead that responsibility should be moved out of handle_trade(),
|
|
|
|
# we might just want to check if we are in a sell condition without
|
|
|
|
# executing
|
|
|
|
# if ROI is reached we must sell
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-15 21:27:12 +00:00
|
|
|
assert handle_trade(trade, interval=int(default_conf['ticker_interval']))
|
2018-02-24 19:18:53 +00:00
|
|
|
assert log_has('Required profit reached. Selling..', caplog.record_tuples)
|
2017-12-28 23:00:30 +00:00
|
|
|
# if ROI is reached we must sell even if sell-signal is not signalled
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-15 21:27:12 +00:00
|
|
|
assert handle_trade(trade, interval=int(default_conf['ticker_interval']))
|
2018-02-24 19:18:53 +00:00
|
|
|
assert log_has('Required profit reached. Selling..', caplog.record_tuples)
|
2017-10-30 23:36:35 +00:00
|
|
|
|
2017-12-29 08:40:24 +00:00
|
|
|
|
2018-01-10 08:13:41 +00:00
|
|
|
def test_handle_trade_experimental(default_conf, ticker, mocker, caplog):
|
2018-01-31 17:37:38 +00:00
|
|
|
caplog.set_level(logging.DEBUG)
|
2017-12-27 10:41:11 +00:00
|
|
|
default_conf.update({'experimental': {'use_sell_signal': True}})
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-27 10:41:11 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
2017-12-28 23:00:30 +00:00
|
|
|
mocker.patch('freqtrade.main.min_roi_reached', return_value=False)
|
2017-12-27 10:41:11 +00:00
|
|
|
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-12-27 10:41:11 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
trade.is_open = True
|
|
|
|
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, False))
|
2018-01-15 21:27:12 +00:00
|
|
|
value_returned = handle_trade(trade, int(default_conf['ticker_interval']))
|
2017-12-27 10:41:11 +00:00
|
|
|
assert value_returned is False
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-15 21:27:12 +00:00
|
|
|
assert handle_trade(trade, int(default_conf['ticker_interval']))
|
2018-02-24 19:18:53 +00:00
|
|
|
assert log_has('Sell signal received. Selling..', caplog.record_tuples)
|
2017-12-27 10:41:11 +00:00
|
|
|
|
|
|
|
|
2017-11-07 21:27:44 +00:00
|
|
|
def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mocker):
|
2017-11-07 19:12:56 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-11-18 20:30:31 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
2017-11-07 21:27:44 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
|
|
|
|
|
|
|
# Create trade and sell it
|
2017-11-09 22:45:22 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-11-22 19:51:25 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
2017-10-01 12:25:10 +00:00
|
|
|
assert trade
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2017-11-22 19:51:25 +00:00
|
|
|
trade.update(limit_buy_order)
|
2017-11-17 19:17:29 +00:00
|
|
|
trade.update(limit_sell_order)
|
2017-11-22 19:51:25 +00:00
|
|
|
assert trade.is_open is False
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2017-11-07 21:27:44 +00:00
|
|
|
with pytest.raises(ValueError, match=r'.*closed trade.*'):
|
2018-01-15 21:27:12 +00:00
|
|
|
handle_trade(trade, int(default_conf['ticker_interval']))
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2017-10-30 23:36:35 +00:00
|
|
|
|
2018-01-10 08:13:41 +00:00
|
|
|
def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mocker):
|
2018-01-03 03:30:10 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
cancel_order_mock = MagicMock()
|
2018-01-16 10:55:15 +00:00
|
|
|
mocker.patch('freqtrade.rpc.init', MagicMock())
|
|
|
|
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
2018-01-03 03:30:10 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
2018-01-04 14:39:01 +00:00
|
|
|
get_order=MagicMock(return_value=limit_buy_order_old),
|
2018-01-03 03:30:10 +00:00
|
|
|
cancel_order=cancel_order_mock)
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
|
|
|
|
2018-01-06 19:21:56 +00:00
|
|
|
trade_buy = Trade(
|
2018-01-03 03:30:10 +00:00
|
|
|
pair='BTC_ETH',
|
2018-01-04 14:39:01 +00:00
|
|
|
open_rate=0.00001099,
|
2018-01-03 03:30:10 +00:00
|
|
|
exchange='BITTREX',
|
|
|
|
open_order_id='123456789',
|
2018-01-04 14:39:01 +00:00
|
|
|
amount=90.99181073,
|
2018-01-03 03:30:10 +00:00
|
|
|
fee=0.0,
|
|
|
|
stake_amount=1,
|
2018-01-04 14:39:01 +00:00
|
|
|
open_date=arrow.utcnow().shift(minutes=-601).datetime,
|
2018-01-03 03:30:10 +00:00
|
|
|
is_open=True
|
|
|
|
)
|
|
|
|
|
2018-01-06 19:21:56 +00:00
|
|
|
Trade.session.add(trade_buy)
|
2018-01-03 03:30:10 +00:00
|
|
|
|
|
|
|
# check it does cancel buy orders over the time limit
|
2018-01-04 14:39:01 +00:00
|
|
|
check_handle_timedout(600)
|
2018-01-03 03:30:10 +00:00
|
|
|
assert cancel_order_mock.call_count == 1
|
2018-01-16 10:55:15 +00:00
|
|
|
assert rpc_mock.call_count == 1
|
2018-01-06 19:21:56 +00:00
|
|
|
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
|
2018-01-28 07:38:41 +00:00
|
|
|
nb_trades = len(trades)
|
|
|
|
assert nb_trades == 0
|
2018-01-03 03:30:10 +00:00
|
|
|
|
|
|
|
|
2018-01-26 16:56:56 +00:00
|
|
|
def test_handle_timedout_limit_buy(mocker):
|
2018-01-10 09:29:04 +00:00
|
|
|
cancel_order = MagicMock()
|
|
|
|
mocker.patch('freqtrade.exchange.cancel_order', cancel_order)
|
|
|
|
Trade.session = MagicMock()
|
|
|
|
trade = MagicMock()
|
2018-01-13 11:52:02 +00:00
|
|
|
order = {'remaining': 1,
|
|
|
|
'amount': 1}
|
2018-01-10 09:29:04 +00:00
|
|
|
assert main.handle_timedout_limit_buy(trade, order)
|
|
|
|
assert cancel_order.call_count == 1
|
|
|
|
order['amount'] = 2
|
|
|
|
assert not main.handle_timedout_limit_buy(trade, order)
|
|
|
|
assert cancel_order.call_count == 2
|
|
|
|
|
|
|
|
|
2018-01-10 08:13:41 +00:00
|
|
|
def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, mocker):
|
2018-01-04 14:39:01 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
cancel_order_mock = MagicMock()
|
2018-01-16 10:55:15 +00:00
|
|
|
mocker.patch('freqtrade.rpc.init', MagicMock())
|
|
|
|
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
2018-01-04 14:39:01 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
|
|
|
get_order=MagicMock(return_value=limit_sell_order_old),
|
|
|
|
cancel_order=cancel_order_mock)
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
|
|
|
|
2018-01-06 19:21:56 +00:00
|
|
|
trade_sell = Trade(
|
2018-01-04 14:39:01 +00:00
|
|
|
pair='BTC_ETH',
|
|
|
|
open_rate=0.00001099,
|
|
|
|
exchange='BITTREX',
|
|
|
|
open_order_id='123456789',
|
|
|
|
amount=90.99181073,
|
|
|
|
fee=0.0,
|
|
|
|
stake_amount=1,
|
|
|
|
open_date=arrow.utcnow().shift(hours=-5).datetime,
|
|
|
|
close_date=arrow.utcnow().shift(minutes=-601).datetime,
|
|
|
|
is_open=False
|
|
|
|
)
|
|
|
|
|
2018-01-06 19:21:56 +00:00
|
|
|
Trade.session.add(trade_sell)
|
2018-01-03 03:30:10 +00:00
|
|
|
|
|
|
|
# check it does cancel sell orders over the time limit
|
2018-01-04 14:39:01 +00:00
|
|
|
check_handle_timedout(600)
|
|
|
|
assert cancel_order_mock.call_count == 1
|
2018-01-16 10:55:15 +00:00
|
|
|
assert rpc_mock.call_count == 1
|
2018-01-06 19:21:56 +00:00
|
|
|
assert trade_sell.is_open is True
|
2018-01-03 03:30:10 +00:00
|
|
|
|
|
|
|
|
2018-01-26 16:56:56 +00:00
|
|
|
def test_handle_timedout_limit_sell(mocker):
|
2018-01-10 09:29:04 +00:00
|
|
|
cancel_order = MagicMock()
|
|
|
|
mocker.patch('freqtrade.exchange.cancel_order', cancel_order)
|
|
|
|
trade = MagicMock()
|
2018-01-13 11:52:02 +00:00
|
|
|
order = {'remaining': 1,
|
|
|
|
'amount': 1}
|
2018-01-10 09:29:04 +00:00
|
|
|
assert main.handle_timedout_limit_sell(trade, order)
|
|
|
|
assert cancel_order.call_count == 1
|
|
|
|
order['amount'] = 2
|
|
|
|
assert not main.handle_timedout_limit_sell(trade, order)
|
|
|
|
# Assert cancel_order was not called (callcount remains unchanged)
|
|
|
|
assert cancel_order.call_count == 1
|
|
|
|
|
|
|
|
|
2018-01-04 14:39:01 +00:00
|
|
|
def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial,
|
2018-01-10 08:13:41 +00:00
|
|
|
mocker):
|
2018-01-03 03:30:10 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
cancel_order_mock = MagicMock()
|
2018-01-16 10:55:15 +00:00
|
|
|
mocker.patch('freqtrade.rpc.init', MagicMock())
|
|
|
|
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
2018-01-03 03:30:10 +00:00
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker,
|
2018-01-04 14:39:01 +00:00
|
|
|
get_order=MagicMock(return_value=limit_buy_order_old_partial),
|
2018-01-03 03:30:10 +00:00
|
|
|
cancel_order=cancel_order_mock)
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
|
|
|
|
2018-01-06 19:21:56 +00:00
|
|
|
trade_buy = Trade(
|
2018-01-03 03:30:10 +00:00
|
|
|
pair='BTC_ETH',
|
2018-01-04 14:39:01 +00:00
|
|
|
open_rate=0.00001099,
|
2018-01-03 03:30:10 +00:00
|
|
|
exchange='BITTREX',
|
|
|
|
open_order_id='123456789',
|
2018-01-04 14:39:01 +00:00
|
|
|
amount=90.99181073,
|
2018-01-03 03:30:10 +00:00
|
|
|
fee=0.0,
|
|
|
|
stake_amount=1,
|
2018-01-04 14:39:01 +00:00
|
|
|
open_date=arrow.utcnow().shift(minutes=-601).datetime,
|
2018-01-03 03:30:10 +00:00
|
|
|
is_open=True
|
|
|
|
)
|
|
|
|
|
2018-01-06 19:21:56 +00:00
|
|
|
Trade.session.add(trade_buy)
|
2018-01-03 03:30:10 +00:00
|
|
|
|
|
|
|
# check it does cancel buy orders over the time limit
|
|
|
|
# note this is for a partially-complete buy order
|
2018-01-04 14:39:01 +00:00
|
|
|
check_handle_timedout(600)
|
2018-01-03 03:30:10 +00:00
|
|
|
assert cancel_order_mock.call_count == 1
|
2018-01-16 10:55:15 +00:00
|
|
|
assert rpc_mock.call_count == 1
|
2018-01-06 19:21:56 +00:00
|
|
|
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
|
2018-01-03 03:30:10 +00:00
|
|
|
assert len(trades) == 1
|
2018-01-04 14:39:01 +00:00
|
|
|
assert trades[0].amount == 23.0
|
2018-01-06 19:21:56 +00:00
|
|
|
assert trades[0].stake_amount == trade_buy.open_rate * trades[0].amount
|
2018-01-03 03:30:10 +00:00
|
|
|
|
|
|
|
|
2017-10-01 12:25:10 +00:00
|
|
|
def test_balance_fully_ask_side(mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 0.0}})
|
|
|
|
assert get_target_bid({'ask': 20, 'last': 10}) == 20
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2017-10-30 23:36:35 +00:00
|
|
|
|
2017-10-01 12:25:10 +00:00
|
|
|
def test_balance_fully_last_side(mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}})
|
|
|
|
assert get_target_bid({'ask': 20, 'last': 10}) == 10
|
2017-10-01 08:02:47 +00:00
|
|
|
|
2017-10-30 23:36:35 +00:00
|
|
|
|
2017-11-07 19:12:56 +00:00
|
|
|
def test_balance_bigger_last_ask(mocker):
|
2017-10-01 12:25:10 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}})
|
|
|
|
assert get_target_bid({'ask': 5, 'last': 10}) == 5
|
2017-12-30 03:52:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-30 03:52:55 +00:00
|
|
|
mocker.patch('freqtrade.rpc.init', MagicMock())
|
|
|
|
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker)
|
2018-01-21 23:32:49 +00:00
|
|
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
2017-12-30 03:52:55 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
|
|
|
|
|
|
|
# Create some test data
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-12-30 03:52:55 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
assert trade
|
|
|
|
|
|
|
|
# Increase the price and sell it
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker_sell_up)
|
|
|
|
|
|
|
|
execute_sell(trade=trade, limit=ticker_sell_up()['bid'])
|
|
|
|
|
|
|
|
assert rpc_mock.call_count == 2
|
2018-01-21 17:33:19 +00:00
|
|
|
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-23 06:55:22 +00:00
|
|
|
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-21 17:33:19 +00:00
|
|
|
assert 'Amount' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-23 06:55:22 +00:00
|
|
|
assert 'Profit' in rpc_mock.call_args_list[-1][0][0]
|
2017-12-30 03:52:55 +00:00
|
|
|
assert '0.00001172' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-01 19:18:38 +00:00
|
|
|
assert 'profit: 6.11%, 0.00006126' in rpc_mock.call_args_list[-1][0][0]
|
2017-12-30 03:52:55 +00:00
|
|
|
assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0]
|
|
|
|
|
|
|
|
|
|
|
|
def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker):
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-30 03:52:55 +00:00
|
|
|
mocker.patch('freqtrade.rpc.init', MagicMock())
|
|
|
|
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.rpc.telegram',
|
|
|
|
_CONF=default_conf,
|
|
|
|
init=MagicMock(),
|
|
|
|
send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker)
|
2018-01-21 23:32:49 +00:00
|
|
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
2017-12-30 03:52:55 +00:00
|
|
|
init(default_conf, create_engine('sqlite://'))
|
|
|
|
|
|
|
|
# Create some test data
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2018-01-12 16:02:35 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
assert trade
|
|
|
|
|
|
|
|
# Decrease the price and sell it
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker_sell_down)
|
|
|
|
|
|
|
|
execute_sell(trade=trade, limit=ticker_sell_down()['bid'])
|
|
|
|
|
|
|
|
assert rpc_mock.call_count == 2
|
2018-01-21 17:33:19 +00:00
|
|
|
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-23 06:55:22 +00:00
|
|
|
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-21 17:33:19 +00:00
|
|
|
assert 'Amount' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-12 16:02:35 +00:00
|
|
|
assert '0.00001044' in rpc_mock.call_args_list[-1][0][0]
|
|
|
|
assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0]
|
|
|
|
assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0]
|
|
|
|
|
|
|
|
|
2018-01-13 08:09:12 +00:00
|
|
|
def test_execute_sell_without_conf_sell_down(default_conf, ticker, ticker_sell_down, mocker):
|
2018-01-12 16:02:35 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2018-01-12 16:02:35 +00:00
|
|
|
mocker.patch('freqtrade.rpc.init', MagicMock())
|
|
|
|
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker)
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
|
|
|
|
|
|
|
# Create some test data
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-12-30 03:52:55 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
assert trade
|
|
|
|
|
|
|
|
# Decrease the price and sell it
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker_sell_down)
|
2018-01-13 08:21:49 +00:00
|
|
|
mocker.patch('freqtrade.main._CONF', {})
|
2017-12-30 03:52:55 +00:00
|
|
|
|
|
|
|
execute_sell(trade=trade, limit=ticker_sell_down()['bid'])
|
|
|
|
|
|
|
|
assert rpc_mock.call_count == 2
|
2018-01-22 07:39:11 +00:00
|
|
|
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-23 06:55:22 +00:00
|
|
|
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
2017-12-30 03:52:55 +00:00
|
|
|
assert '0.00001044' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-01 19:18:38 +00:00
|
|
|
assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0]
|
2017-12-30 03:52:55 +00:00
|
|
|
|
|
|
|
|
2018-01-13 08:09:12 +00:00
|
|
|
def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up, mocker):
|
2017-12-30 03:52:55 +00:00
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-30 03:52:55 +00:00
|
|
|
mocker.patch('freqtrade.rpc.init', MagicMock())
|
|
|
|
rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker)
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
|
|
|
|
|
|
|
# Create some test data
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-12-30 03:52:55 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
assert trade
|
|
|
|
|
|
|
|
# Increase the price and sell it
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=ticker_sell_up)
|
|
|
|
mocker.patch('freqtrade.main._CONF', {})
|
|
|
|
|
|
|
|
execute_sell(trade=trade, limit=ticker_sell_up()['bid'])
|
|
|
|
|
|
|
|
assert rpc_mock.call_count == 2
|
2018-01-21 17:33:19 +00:00
|
|
|
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-23 06:55:22 +00:00
|
|
|
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-21 17:33:19 +00:00
|
|
|
assert 'Amount' in rpc_mock.call_args_list[-1][0][0]
|
2017-12-30 03:52:55 +00:00
|
|
|
assert '0.00001172' in rpc_mock.call_args_list[-1][0][0]
|
2018-01-01 19:18:38 +00:00
|
|
|
assert '(profit: 6.11%, 0.00006126)' in rpc_mock.call_args_list[-1][0][0]
|
2017-12-30 03:52:55 +00:00
|
|
|
assert 'USD' not in rpc_mock.call_args_list[-1][0][0]
|
2017-12-22 03:58:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, mocker):
|
2018-01-06 19:16:45 +00:00
|
|
|
default_conf['experimental'] = {
|
|
|
|
'use_sell_signal': True,
|
|
|
|
'sell_profit_only': True,
|
|
|
|
}
|
2017-12-22 03:58:06 +00:00
|
|
|
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-06 19:16:45 +00:00
|
|
|
mocker.patch('freqtrade.main.min_roi_reached', return_value=False)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-22 03:58:06 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=MagicMock(return_value={
|
|
|
|
'bid': 0.00002172,
|
|
|
|
'ask': 0.00002173,
|
|
|
|
'last': 0.00002172
|
|
|
|
}),
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
|
|
|
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-12-22 03:58:06 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
trade.update(limit_buy_order)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-15 21:27:12 +00:00
|
|
|
assert handle_trade(trade, int(default_conf['ticker_interval'])) is True
|
2017-12-22 03:58:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, mocker):
|
2018-01-06 19:16:45 +00:00
|
|
|
default_conf['experimental'] = {
|
|
|
|
'use_sell_signal': True,
|
|
|
|
'sell_profit_only': False,
|
|
|
|
}
|
2017-12-22 03:58:06 +00:00
|
|
|
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
2018-01-06 19:16:45 +00:00
|
|
|
mocker.patch('freqtrade.main.min_roi_reached', return_value=False)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2017-12-22 03:58:06 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=MagicMock(return_value={
|
|
|
|
'bid': 0.00002172,
|
|
|
|
'ask': 0.00002173,
|
|
|
|
'last': 0.00002172
|
|
|
|
}),
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
|
|
|
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2017-12-22 03:58:06 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
trade.update(limit_buy_order)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-15 21:27:12 +00:00
|
|
|
assert handle_trade(trade, int(default_conf['ticker_interval'])) is True
|
2017-12-22 03:58:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, mocker):
|
2018-01-09 10:59:06 +00:00
|
|
|
default_conf['experimental'] = {
|
|
|
|
'use_sell_signal': True,
|
|
|
|
'sell_profit_only': True,
|
|
|
|
}
|
|
|
|
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
mocker.patch('freqtrade.main.min_roi_reached', return_value=False)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2018-01-09 10:59:06 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=MagicMock(return_value={
|
|
|
|
'bid': 0.00000172,
|
|
|
|
'ask': 0.00000173,
|
|
|
|
'last': 0.00000172
|
|
|
|
}),
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
|
|
|
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2018-01-09 10:59:06 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
trade.update(limit_buy_order)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-15 21:27:12 +00:00
|
|
|
assert handle_trade(trade, int(default_conf['ticker_interval'])) is False
|
2017-12-22 03:58:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, mocker):
|
2018-01-09 10:59:06 +00:00
|
|
|
default_conf['experimental'] = {
|
|
|
|
'use_sell_signal': True,
|
|
|
|
'sell_profit_only': False,
|
|
|
|
}
|
|
|
|
|
|
|
|
mocker.patch.dict('freqtrade.main._CONF', default_conf)
|
|
|
|
mocker.patch('freqtrade.main.min_roi_reached', return_value=False)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))
|
2018-01-09 10:59:06 +00:00
|
|
|
mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock())
|
|
|
|
mocker.patch.multiple('freqtrade.main.exchange',
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
get_ticker=MagicMock(return_value={
|
|
|
|
'bid': 0.00000172,
|
|
|
|
'ask': 0.00000173,
|
|
|
|
'last': 0.00000172
|
|
|
|
}),
|
|
|
|
buy=MagicMock(return_value='mocked_limit_buy'))
|
|
|
|
|
|
|
|
init(default_conf, create_engine('sqlite://'))
|
2018-01-15 21:27:12 +00:00
|
|
|
create_trade(0.001, int(default_conf['ticker_interval']))
|
2018-01-09 10:59:06 +00:00
|
|
|
|
|
|
|
trade = Trade.query.first()
|
|
|
|
trade.update(limit_buy_order)
|
2018-01-20 23:05:01 +00:00
|
|
|
mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (False, True))
|
2018-01-15 21:27:12 +00:00
|
|
|
assert handle_trade(trade, int(default_conf['ticker_interval'])) is True
|