stable/freqtrade/tests/test_freqtradebot.py

3284 lines
112 KiB
Python
Raw Normal View History

# pragma pylint: disable=missing-docstring, C0103
# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
import logging
import time
from copy import deepcopy
2019-03-05 18:46:03 +00:00
from unittest.mock import MagicMock, PropertyMock
2018-03-17 21:44:47 +00:00
import arrow
import pytest
import requests
2019-05-11 12:05:25 +00:00
from freqtrade import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError, constants)
from freqtrade.data.dataprovider import DataProvider
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade
from freqtrade.rpc import RPCMessageType
from freqtrade.state import State, RunMode
from freqtrade.strategy.interface import SellCheckTuple, SellType
2019-06-20 18:30:05 +00:00
from freqtrade.tests.conftest import (get_patched_freqtradebot,
get_patched_worker, log_has, log_has_re,
patch_edge, patch_exchange,
patch_get_signal, patch_wallet)
from freqtrade.worker import Worker
def patch_RPCManager(mocker) -> MagicMock:
"""
This function mock RPC manager to avoid repeating this code in almost every tests
:param mocker: mocker to patch RPCManager class
:return: RPCManager.send_msg MagicMock to track if this method is called
"""
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
rpc_mock = mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
return rpc_mock
# Unit tests
def test_freqtradebot_state(mocker, default_conf, markets) -> None:
2019-03-05 18:46:03 +00:00
mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets))
freqtrade = get_patched_freqtradebot(mocker, default_conf)
assert freqtrade.state is State.RUNNING
default_conf.pop('initial_state')
freqtrade = FreqtradeBot(default_conf)
assert freqtrade.state is State.STOPPED
def test_worker_state(mocker, default_conf, markets) -> None:
2019-03-05 18:46:03 +00:00
mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets))
2019-03-26 08:07:24 +00:00
worker = get_patched_worker(mocker, default_conf)
assert worker.state is State.RUNNING
default_conf.pop('initial_state')
2019-03-26 08:07:24 +00:00
worker = Worker(args=None, config=default_conf)
assert worker.state is State.STOPPED
def test_cleanup(mocker, default_conf, caplog) -> None:
2018-03-05 08:11:13 +00:00
mock_cleanup = MagicMock()
mocker.patch('freqtrade.persistence.cleanup', mock_cleanup)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
freqtrade.cleanup()
2019-08-11 18:17:39 +00:00
assert log_has('Cleaning up modules ...', caplog)
2018-03-05 08:11:13 +00:00
assert mock_cleanup.call_count == 1
def test_worker_running(mocker, default_conf, caplog) -> None:
mock_throttle = MagicMock()
2019-03-26 08:07:24 +00:00
mocker.patch('freqtrade.worker.Worker._throttle', mock_throttle)
2019-05-20 18:13:01 +00:00
mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', MagicMock())
2018-03-05 08:11:13 +00:00
2019-03-26 08:07:24 +00:00
worker = get_patched_worker(mocker, default_conf)
2018-03-05 08:11:13 +00:00
2019-03-26 08:07:24 +00:00
state = worker._worker(old_state=None)
2018-03-05 08:11:13 +00:00
assert state is State.RUNNING
2019-08-11 18:17:39 +00:00
assert log_has('Changing state to: RUNNING', caplog)
2018-03-05 08:11:13 +00:00
assert mock_throttle.call_count == 1
# Check strategy is loaded, and received a dataprovider object
assert worker.freqtrade.strategy
assert worker.freqtrade.strategy.dp
assert isinstance(worker.freqtrade.strategy.dp, DataProvider)
2018-03-05 08:11:13 +00:00
def test_worker_stopped(mocker, default_conf, caplog) -> None:
mock_throttle = MagicMock()
2019-03-26 08:07:24 +00:00
mocker.patch('freqtrade.worker.Worker._throttle', mock_throttle)
2018-03-05 08:11:13 +00:00
mock_sleep = mocker.patch('time.sleep', return_value=None)
2019-03-26 08:07:24 +00:00
worker = get_patched_worker(mocker, default_conf)
worker.state = State.STOPPED
state = worker._worker(old_state=State.RUNNING)
2018-03-05 08:11:13 +00:00
assert state is State.STOPPED
2019-08-11 18:17:39 +00:00
assert log_has('Changing state to: STOPPED', caplog)
2018-03-05 08:11:13 +00:00
assert mock_throttle.call_count == 0
assert mock_sleep.call_count == 1
def test_throttle(mocker, default_conf, caplog) -> None:
2018-07-30 09:06:16 +00:00
def throttled_func():
return 42
caplog.set_level(logging.DEBUG)
2019-03-26 08:07:24 +00:00
worker = get_patched_worker(mocker, default_conf)
start = time.time()
2019-03-26 08:07:24 +00:00
result = worker._throttle(throttled_func, min_secs=0.1)
end = time.time()
assert result == 42
assert end - start > 0.1
2019-08-11 18:17:39 +00:00
assert log_has('Throttling throttled_func for 0.10 seconds', caplog)
2019-03-26 08:07:24 +00:00
result = worker._throttle(throttled_func, min_secs=-1)
assert result == 42
def test_throttle_with_assets(mocker, default_conf) -> None:
2018-07-30 09:06:16 +00:00
def throttled_func(nb_assets=-1):
return nb_assets
2019-03-26 08:07:24 +00:00
worker = get_patched_worker(mocker, default_conf)
2019-03-26 08:07:24 +00:00
result = worker._throttle(throttled_func, min_secs=0.1, nb_assets=666)
assert result == 666
2019-03-26 08:07:24 +00:00
result = worker._throttle(throttled_func, min_secs=0.1)
assert result == -1
def test_order_dict_dry_run(default_conf, mocker, caplog) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
)
conf = default_conf.copy()
conf['runmode'] = RunMode.DRY_RUN
conf['order_types'] = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': True,
}
freqtrade = FreqtradeBot(conf)
assert log_has("Disabling stoploss_on_exchange during dry-run.", caplog)
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
caplog.clear()
# is left untouched
conf = default_conf.copy()
conf['runmode'] = RunMode.DRY_RUN
conf['order_types'] = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': False,
}
freqtrade = FreqtradeBot(conf)
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
def test_order_dict_live(default_conf, mocker, caplog) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
)
conf = default_conf.copy()
conf['runmode'] = RunMode.LIVE
conf['order_types'] = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': True,
}
freqtrade = FreqtradeBot(conf)
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
assert freqtrade.strategy.order_types['stoploss_on_exchange']
caplog.clear()
# is left untouched
conf = default_conf.copy()
conf['runmode'] = RunMode.LIVE
conf['order_types'] = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': False,
}
freqtrade = FreqtradeBot(conf)
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-23 13:50:27 +00:00
'freqtrade.exchange.Exchange',
2018-05-24 21:46:08 +00:00
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
)
freqtrade = FreqtradeBot(default_conf)
result = freqtrade._get_trade_stake_amount('ETH/BTC')
2018-07-30 08:57:11 +00:00
assert result == default_conf['stake_amount']
def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-11-23 09:17:10 +00:00
patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5)
freqtrade = FreqtradeBot(default_conf)
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
freqtrade._get_trade_stake_amount('ETH/BTC')
def test_get_trade_stake_amount_unlimited_amount(default_conf,
ticker,
limit_buy_order,
fee,
2018-06-16 23:23:12 +00:00
markets,
mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-11-23 09:17:10 +00:00
patch_wallet(mocker, free=default_conf['stake_amount'])
mocker.patch.multiple(
2018-06-23 13:50:27 +00:00
'freqtrade.exchange.Exchange',
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
2019-03-05 18:46:03 +00:00
get_fee=fee
)
conf = deepcopy(default_conf)
2018-05-25 14:04:08 +00:00
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
conf['max_open_trades'] = 2
freqtrade = FreqtradeBot(conf)
patch_get_signal(freqtrade)
# no open trades, order amount should be 'balance / max_open_trades'
result = freqtrade._get_trade_stake_amount('ETH/BTC')
assert result == default_conf['stake_amount'] / conf['max_open_trades']
# create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)'
freqtrade.create_trade()
result = freqtrade._get_trade_stake_amount('LTC/BTC')
assert result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1)
# create 2 trades, order amount should be None
2018-06-03 22:48:26 +00:00
freqtrade.create_trade()
result = freqtrade._get_trade_stake_amount('XRP/BTC')
assert result is None
2018-06-03 22:48:26 +00:00
# set max_open_trades = None, so do not trade
2018-06-05 21:14:28 +00:00
conf['max_open_trades'] = 0
freqtrade = FreqtradeBot(conf)
result = freqtrade._get_trade_stake_amount('NEO/BTC')
assert result is None
2018-06-05 21:14:28 +00:00
2018-11-10 17:03:46 +00:00
def test_edge_called_in_process(mocker, edge_conf) -> None:
patch_RPCManager(mocker)
patch_edge(mocker)
2018-11-10 17:09:32 +00:00
2018-11-10 17:03:46 +00:00
def _refresh_whitelist(list):
return ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC']
patch_exchange(mocker)
freqtrade = FreqtradeBot(edge_conf)
2018-12-03 19:38:15 +00:00
freqtrade.pairlists._validate_whitelist = _refresh_whitelist
2018-11-10 17:03:46 +00:00
patch_get_signal(freqtrade)
2019-03-26 08:07:24 +00:00
freqtrade.process()
2018-11-10 17:03:46 +00:00
assert freqtrade.active_pair_whitelist == ['NEO/BTC', 'LTC/BTC']
2018-11-10 16:20:11 +00:00
def test_edge_overrides_stake_amount(mocker, edge_conf) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
patch_edge(mocker)
2018-11-10 16:20:11 +00:00
freqtrade = FreqtradeBot(edge_conf)
2018-11-27 13:02:34 +00:00
assert freqtrade._get_trade_stake_amount('NEO/BTC') == (999.9 * 0.5 * 0.01) / 0.20
assert freqtrade._get_trade_stake_amount('LTC/BTC') == (999.9 * 0.5 * 0.01) / 0.21
2018-11-10 16:20:11 +00:00
def test_edge_overrides_stoploss(limit_buy_order, fee, markets, caplog, mocker, edge_conf) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
patch_edge(mocker)
# Strategy stoploss is -0.1 but Edge imposes a stoploss at -0.2
# Thus, if price falls 21%, stoploss should be triggered
#
# mocking the ticker: price is falling ...
buy_price = limit_buy_order['price']
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': buy_price * 0.79,
'ask': buy_price * 0.79,
'last': buy_price * 0.79
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
#############################################
# Create a trade with "limit_buy_order" price
2018-11-10 16:20:11 +00:00
freqtrade = FreqtradeBot(edge_conf)
freqtrade.active_pair_whitelist = ['NEO/BTC']
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
#############################################
# stoploss shoud be hit
assert freqtrade.handle_trade(trade) is True
2019-08-11 18:17:39 +00:00
assert log_has('executed sell, reason: SellType.STOP_LOSS', caplog)
assert trade.sell_reason == SellType.STOP_LOSS.value
def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee, markets,
2018-11-10 16:20:11 +00:00
mocker, edge_conf) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
patch_edge(mocker)
# Strategy stoploss is -0.1 but Edge imposes a stoploss at -0.2
# Thus, if price falls 15%, stoploss should not be triggered
#
# mocking the ticker: price is falling ...
buy_price = limit_buy_order['price']
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': buy_price * 0.85,
'ask': buy_price * 0.85,
'last': buy_price * 0.85
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
)
#############################################
# Create a trade with "limit_buy_order" price
2018-11-10 16:20:11 +00:00
freqtrade = FreqtradeBot(edge_conf)
freqtrade.active_pair_whitelist = ['NEO/BTC']
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
#############################################
2018-11-10 16:20:11 +00:00
# stoploss shoud not be hit
assert freqtrade.handle_trade(trade) is False
2018-10-04 16:07:47 +00:00
def test_total_open_trades_stakes(mocker, default_conf, ticker,
limit_buy_order, fee, markets) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
default_conf['stake_amount'] = 0.0000098751
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
assert trade is not None
assert trade.stake_amount == 0.0000098751
assert trade.is_open
assert trade.open_date is not None
freqtrade.create_trade()
trade = Trade.query.order_by(Trade.id.desc()).first()
assert trade is not None
assert trade.stake_amount == 0.0000098751
assert trade.is_open
assert trade.open_date is not None
assert Trade.total_open_trades_stakes() == 1.97502e-05
2018-06-16 23:23:12 +00:00
def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
freqtrade = FreqtradeBot(default_conf)
freqtrade.strategy.stoploss = -0.05
2019-03-05 18:46:03 +00:00
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}}
2018-06-16 23:23:12 +00:00
# no pair found
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
with pytest.raises(ValueError, match=r'.*get market information.*'):
freqtrade._get_min_pair_stake_amount('BNB/BTC', 1)
# no 'limits' section
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1)
assert result is None
# empty 'limits' section
2019-03-05 18:46:03 +00:00
markets["ETH/BTC"]["limits"] = {}
2018-06-16 23:23:12 +00:00
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1)
assert result is None
2018-06-28 17:48:05 +00:00
# no cost Min
2019-03-05 18:46:03 +00:00
markets["ETH/BTC"]["limits"] = {
'cost': {"min": None},
'amount': {}
}
2018-06-28 17:48:05 +00:00
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-28 17:48:05 +00:00
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1)
assert result is None
# no amount Min
2019-03-05 18:46:03 +00:00
markets["ETH/BTC"]["limits"] = {
'cost': {},
'amount': {"min": None}
}
2018-06-28 17:48:05 +00:00
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-28 17:48:05 +00:00
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1)
assert result is None
2018-06-16 23:23:12 +00:00
# empty 'cost'/'amount' section
2019-03-05 18:46:03 +00:00
markets["ETH/BTC"]["limits"] = {
'cost': {},
'amount': {}
}
2018-06-16 23:23:12 +00:00
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1)
assert result is None
# min cost is set
2019-03-05 18:46:03 +00:00
markets["ETH/BTC"]["limits"] = {
'cost': {'min': 2},
'amount': {}
}
2018-06-16 23:23:12 +00:00
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1)
assert result == 2 / 0.9
2018-06-16 23:23:12 +00:00
# min amount is set
2019-03-05 18:46:03 +00:00
markets["ETH/BTC"]["limits"] = {
'cost': {},
'amount': {'min': 2}
}
2018-06-16 23:23:12 +00:00
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2)
assert result == 2 * 2 / 0.9
# min amount and cost are set (cost is minimal)
2019-03-05 18:46:03 +00:00
markets["ETH/BTC"]["limits"] = {
'cost': {'min': 2},
'amount': {'min': 2}
}
2018-06-16 23:23:12 +00:00
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2)
assert result == min(2, 2 * 2) / 0.9
# min amount and cost are set (amount is minial)
2019-03-05 18:46:03 +00:00
markets["ETH/BTC"]["limits"] = {
'cost': {'min': 8},
'amount': {'min': 2}
}
2018-06-16 23:23:12 +00:00
mocker.patch(
2019-03-05 18:46:03 +00:00
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2)
assert result == min(8, 2 * 2) / 0.9
def test_create_trade(default_conf, ticker, limit_buy_order, fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
# Save state of current whitelist
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
assert trade is not None
assert trade.stake_amount == 0.001
assert trade.is_open
assert trade.open_date is not None
assert trade.exchange == 'bittrex'
# Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order)
assert trade.open_rate == 0.00001099
assert trade.amount == 90.99181073
assert whitelist == default_conf['exchange']['pair_whitelist']
2018-06-16 23:23:12 +00:00
def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order,
fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-11-23 09:17:10 +00:00
patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5)
2018-06-16 23:23:12 +00:00
mocker.patch.multiple(
2018-06-23 13:50:27 +00:00
'freqtrade.exchange.Exchange',
2018-06-16 23:23:12 +00:00
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-06-16 23:23:12 +00:00
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
freqtrade.create_trade()
def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2018-04-21 17:39:18 +00:00
buy=buy_mock,
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['stake_amount'] = 0.0005
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trade()
2018-11-15 18:31:24 +00:00
rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount']
assert rate * amount >= default_conf['stake_amount']
2018-06-16 23:23:12 +00:00
def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order,
fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-16 23:23:12 +00:00
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
mocker.patch.multiple(
2018-06-23 13:50:27 +00:00
'freqtrade.exchange.Exchange',
2018-06-16 23:23:12 +00:00
get_ticker=ticker,
buy=buy_mock,
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-06-16 23:23:12 +00:00
)
default_conf['stake_amount'] = 0.000000005
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-06-16 23:23:12 +00:00
2019-04-01 11:08:41 +00:00
assert not freqtrade.create_trade()
2018-06-16 23:23:12 +00:00
def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order,
fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_balance=MagicMock(return_value=default_conf['stake_amount']),
2018-04-21 17:39:18 +00:00
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['max_open_trades'] = 0
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2019-04-01 11:08:41 +00:00
assert not freqtrade.create_trade()
assert freqtrade._get_trade_stake_amount('ETH/BTC') is None
def test_create_trade_no_pairs_let(default_conf, ticker, limit_buy_order, fee,
markets, mocker, caplog) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['exchange']['pair_whitelist'] = ["ETH/BTC"]
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
assert freqtrade.create_trade()
2019-04-01 11:08:41 +00:00
assert not freqtrade.create_trade()
2019-08-11 18:17:39 +00:00
assert log_has("No currency pair in whitelist, but checking to sell open trades.", caplog)
def test_create_trade_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee,
markets, mocker, caplog) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['exchange']['pair_whitelist'] = []
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2019-04-01 11:08:41 +00:00
assert not freqtrade.create_trade()
2019-08-11 18:17:39 +00:00
assert log_has("Whitelist is empty.", caplog)
2018-04-21 17:39:18 +00:00
def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
default_conf['dry_run'] = True
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
2018-04-21 17:39:18 +00:00
get_balance=MagicMock(return_value=20),
get_fee=fee,
)
default_conf['stake_amount'] = 10
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade, value=(False, False))
Trade.query = MagicMock()
Trade.query.filter = MagicMock()
assert not freqtrade.create_trade()
def test_process_trade_creation(default_conf, ticker, limit_buy_order,
2018-04-21 17:39:18 +00:00
markets, fee, mocker, caplog) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
2018-04-21 17:39:18 +00:00
get_order=MagicMock(return_value=limit_buy_order),
get_fee=fee,
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
assert not trades
2019-03-26 08:07:24 +00:00
result = freqtrade.process()
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 == 'bittrex'
assert trade.open_rate == 0.00001099
assert trade.amount == 90.99181073703367
assert log_has(
2019-08-11 18:17:39 +00:00
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
)
def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
buy=MagicMock(side_effect=TemporaryError)
)
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
worker = Worker(args=None, config=default_conf)
2019-03-26 08:07:24 +00:00
patch_get_signal(worker.freqtrade)
2019-03-26 15:34:50 +00:00
result = worker._process()
assert result is False
assert sleep_mock.has_calls()
def test_process_operational_exception(default_conf, ticker, markets, mocker) -> None:
msg_mock = patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
buy=MagicMock(side_effect=OperationalException)
)
worker = Worker(args=None, config=default_conf)
2019-03-26 08:07:24 +00:00
patch_get_signal(worker.freqtrade)
2019-03-26 08:07:24 +00:00
assert worker.state == State.RUNNING
2019-03-26 15:34:50 +00:00
result = worker._process()
assert result is False
2019-03-26 08:07:24 +00:00
assert worker.state == State.STOPPED
2018-06-24 22:04:27 +00:00
assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status']
2018-04-21 17:39:18 +00:00
def test_process_trade_handling(
default_conf, ticker, limit_buy_order, markets, fee, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
2018-04-21 17:39:18 +00:00
get_order=MagicMock(return_value=limit_buy_order),
get_fee=fee,
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
assert not trades
2019-03-26 08:07:24 +00:00
result = freqtrade.process()
assert result is True
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
assert len(trades) == 1
2019-03-26 08:07:24 +00:00
result = freqtrade.process()
assert result is False
2018-10-29 18:23:56 +00:00
def test_process_trade_no_whitelist_pair(
default_conf, ticker, limit_buy_order, markets, fee, mocker) -> None:
2019-03-26 08:07:24 +00:00
""" Test process with trade not in pair list """
2018-10-29 18:23:56 +00:00
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2018-10-29 18:23:56 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order),
get_fee=fee,
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
pair = 'NOCLUE/BTC'
# create open trade not in whitelist
Trade.session.add(Trade(
pair=pair,
stake_amount=0.001,
fee_open=fee.return_value,
fee_close=fee.return_value,
is_open=True,
amount=20,
open_rate=0.01,
exchange='bittrex',
))
Trade.session.add(Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee_open=fee.return_value,
fee_close=fee.return_value,
is_open=True,
amount=12,
open_rate=0.001,
exchange='bittrex',
))
assert pair not in freqtrade.active_pair_whitelist
2019-03-26 08:07:24 +00:00
result = freqtrade.process()
2018-10-29 18:23:56 +00:00
assert pair in freqtrade.active_pair_whitelist
# Make sure each pair is only in the list once
assert len(freqtrade.active_pair_whitelist) == len(set(freqtrade.active_pair_whitelist))
assert result is True
2019-01-26 19:05:49 +00:00
def test_process_informative_pairs_added(default_conf, ticker, markets, mocker) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
def _refresh_whitelist(list):
return ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC']
refresh_mock = MagicMock()
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2019-01-26 19:05:49 +00:00
buy=MagicMock(side_effect=TemporaryError),
refresh_latest_ohlcv=refresh_mock,
)
inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")])
mocker.patch('time.sleep', return_value=None)
freqtrade = FreqtradeBot(default_conf)
freqtrade.pairlists._validate_whitelist = _refresh_whitelist
freqtrade.strategy.informative_pairs = inf_pairs
# patch_get_signal(freqtrade)
2019-03-26 08:07:24 +00:00
freqtrade.process()
2019-01-26 19:05:49 +00:00
assert inf_pairs.call_count == 1
assert refresh_mock.call_count == 1
assert ("BTC/ETH", "1m") in refresh_mock.call_args[0][0]
assert ("ETH/USDT", "1h") in refresh_mock.call_args[0][0]
assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0]
2018-06-17 19:11:10 +00:00
def test_balance_fully_ask_side(mocker, default_conf) -> None:
default_conf['bid_strategy']['ask_last_balance'] = 0.0
freqtrade = get_patched_freqtradebot(mocker, default_conf)
2019-02-14 06:27:13 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={'ask': 20, 'last': 10}))
2019-02-14 06:27:13 +00:00
assert freqtrade.get_target_bid('ETH/BTC') == 20
2018-06-17 19:11:10 +00:00
def test_balance_fully_last_side(mocker, default_conf) -> None:
default_conf['bid_strategy']['ask_last_balance'] = 1.0
freqtrade = get_patched_freqtradebot(mocker, default_conf)
2019-02-14 06:27:13 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={'ask': 20, 'last': 10}))
2019-02-14 06:27:13 +00:00
assert freqtrade.get_target_bid('ETH/BTC') == 10
2018-06-17 19:11:10 +00:00
def test_balance_bigger_last_ask(mocker, default_conf) -> None:
default_conf['bid_strategy']['ask_last_balance'] = 1.0
freqtrade = get_patched_freqtradebot(mocker, default_conf)
2019-02-14 06:27:13 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={'ask': 5, 'last': 10}))
assert freqtrade.get_target_bid('ETH/BTC') == 5
2018-10-09 05:06:11 +00:00
def test_execute_buy(mocker, default_conf, fee, markets, limit_buy_order) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
freqtrade = FreqtradeBot(default_conf)
stake_amount = 2
bid = 0.11
get_bid = MagicMock(return_value=bid)
mocker.patch.multiple(
'freqtrade.freqtradebot.FreqtradeBot',
get_target_bid=get_bid,
_get_min_pair_stake_amount=MagicMock(return_value=1)
)
buy_mm = MagicMock(return_value={'id': limit_buy_order['id']})
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
'last': 0.00001172
}),
buy=buy_mm,
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-10-09 05:06:11 +00:00
)
pair = 'ETH/BTC'
print(buy_mm.call_args_list)
assert freqtrade.execute_buy(pair, stake_amount)
assert get_bid.call_count == 1
assert buy_mm.call_count == 1
2018-11-15 18:31:24 +00:00
call_args = buy_mm.call_args_list[0][1]
assert call_args['pair'] == pair
assert call_args['rate'] == bid
assert call_args['amount'] == stake_amount / bid
2018-10-09 05:06:11 +00:00
2018-12-12 12:05:55 +00:00
# Should create an open trade with an open order id
# As the order is not fulfilled yet
trade = Trade.query.first()
assert trade
assert trade.is_open is True
assert trade.open_order_id == limit_buy_order['id']
2018-10-09 05:06:11 +00:00
# Test calling with price
fix_price = 0.06
assert freqtrade.execute_buy(pair, stake_amount, fix_price)
# Make sure get_target_bid wasn't called again
assert get_bid.call_count == 1
assert buy_mm.call_count == 2
2018-11-15 18:31:24 +00:00
call_args = buy_mm.call_args_list[1][1]
assert call_args['pair'] == pair
assert call_args['rate'] == fix_price
assert call_args['amount'] == stake_amount / fix_price
2018-10-09 05:06:11 +00:00
2018-12-12 12:05:55 +00:00
# In case of closed order
limit_buy_order['status'] = 'closed'
limit_buy_order['price'] = 10
limit_buy_order['cost'] = 100
mocker.patch('freqtrade.exchange.Exchange.buy', MagicMock(return_value=limit_buy_order))
assert freqtrade.execute_buy(pair, stake_amount)
trade = Trade.query.all()[2]
assert trade
assert trade.open_order_id is None
assert trade.open_rate == 10
assert trade.stake_amount == 100
# In case of rejected or expired order and partially filled
limit_buy_order['status'] = 'expired'
limit_buy_order['amount'] = 90.99181073
limit_buy_order['filled'] = 80.99181073
limit_buy_order['remaining'] = 10.00
limit_buy_order['price'] = 0.5
limit_buy_order['cost'] = 40.495905365
mocker.patch('freqtrade.exchange.Exchange.buy', MagicMock(return_value=limit_buy_order))
assert freqtrade.execute_buy(pair, stake_amount)
trade = Trade.query.all()[3]
assert trade
assert trade.open_order_id is None
assert trade.open_rate == 0.5
assert trade.stake_amount == 40.495905365
# In case of the order is rejected and not filled at all
limit_buy_order['status'] = 'rejected'
limit_buy_order['amount'] = 90.99181073
limit_buy_order['filled'] = 0.0
limit_buy_order['remaining'] = 90.99181073
limit_buy_order['price'] = 0.5
limit_buy_order['cost'] = 0.0
mocker.patch('freqtrade.exchange.Exchange.buy', MagicMock(return_value=limit_buy_order))
assert not freqtrade.execute_buy(pair, stake_amount)
2018-10-09 05:06:11 +00:00
2018-11-23 19:28:01 +00:00
def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
2018-11-23 19:28:01 +00:00
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
return_value=limit_buy_order['amount'])
stoploss_limit = MagicMock(return_value={'id': 13434334})
2018-11-23 19:28:01 +00:00
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
2018-11-23 19:28:01 +00:00
freqtrade = FreqtradeBot(default_conf)
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
2018-11-23 19:28:01 +00:00
trade = MagicMock()
trade.open_order_id = None
trade.stoploss_order_id = None
trade.is_open = True
2018-11-23 19:28:01 +00:00
freqtrade.process_maybe_execute_sell(trade)
2018-11-23 14:17:36 +00:00
assert trade.stoploss_order_id == '13434334'
2018-11-23 19:28:01 +00:00
assert stoploss_limit.call_count == 1
assert trade.is_open is True
2018-10-09 05:06:11 +00:00
2018-11-22 18:27:32 +00:00
def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
markets, limit_buy_order, limit_sell_order) -> None:
stoploss_limit = MagicMock(return_value={'id': 13434334})
2018-11-24 18:12:00 +00:00
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
'last': 0.00001172
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2018-11-24 18:12:00 +00:00
stoploss_limit=stoploss_limit
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# First case: when stoploss is not yet set but the order is open
# should get the stoploss order id immediately
# and should return false as no trade actually happened
trade = MagicMock()
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = None
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert stoploss_limit.call_count == 1
assert trade.stoploss_order_id == "13434334"
# Second case: when stoploss is set but it is not yet hit
# should do nothing and return false
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = 100
hanging_stoploss_order = MagicMock(return_value={'status': 'open'})
mocker.patch('freqtrade.exchange.Exchange.get_order', hanging_stoploss_order)
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert trade.stoploss_order_id == 100
2019-04-04 15:23:21 +00:00
# Third case: when stoploss was set but it was canceled for some reason
# should set a stoploss immediately and return False
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = 100
canceled_stoploss_order = MagicMock(return_value={'status': 'canceled'})
mocker.patch('freqtrade.exchange.Exchange.get_order', canceled_stoploss_order)
stoploss_limit.reset_mock()
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert stoploss_limit.call_count == 1
assert trade.stoploss_order_id == "13434334"
# Fourth case: when stoploss is set and it is hit
# should unset stoploss_order_id and return true
# as a trade actually happened
freqtrade.create_trade()
trade = Trade.query.first()
2018-11-24 18:12:00 +00:00
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = 100
assert trade
2018-11-24 18:12:00 +00:00
stoploss_order_hit = MagicMock(return_value={
'status': 'closed',
'type': 'stop_loss_limit',
'price': 3,
'average': 2
2018-11-24 18:12:00 +00:00
})
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hit)
assert freqtrade.handle_stoploss_on_exchange(trade) is True
2019-08-11 18:17:39 +00:00
assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog)
2019-01-09 15:23:13 +00:00
assert trade.stoploss_order_id is None
assert trade.is_open is False
mocker.patch(
'freqtrade.exchange.Exchange.stoploss_limit',
side_effect=DependencyException()
)
freqtrade.handle_stoploss_on_exchange(trade)
2019-08-11 18:17:39 +00:00
assert log_has('Unable to place a stoploss order on exchange: ', caplog)
2019-04-05 18:20:41 +00:00
# Fifth case: get_order returns InvalidOrder
2019-04-05 18:20:16 +00:00
# It should try to add stoploss order
trade.stoploss_order_id = 100
stoploss_limit.reset_mock()
mocker.patch('freqtrade.exchange.Exchange.get_order', side_effect=InvalidOrderException())
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
freqtrade.handle_stoploss_on_exchange(trade)
assert stoploss_limit.call_count == 1
2019-01-09 15:23:13 +00:00
def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
markets, limit_buy_order, limit_sell_order) -> None:
# When trailing stoploss is set
stoploss_limit = MagicMock(return_value={'id': 13434334})
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
'last': 0.00001172
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2019-01-09 15:23:13 +00:00
stoploss_limit=stoploss_limit
)
2019-01-15 10:04:32 +00:00
# enabling TSL
2019-01-09 15:23:13 +00:00
default_conf['trailing_stop'] = True
2019-01-15 10:04:32 +00:00
# disabling ROI
default_conf['minimal_roi']['0'] = 999999999
2019-01-09 15:23:13 +00:00
freqtrade = FreqtradeBot(default_conf)
2019-01-15 10:04:32 +00:00
# enabling stoploss on exchange
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
2019-01-15 10:04:32 +00:00
# setting stoploss
2019-01-09 15:23:13 +00:00
freqtrade.strategy.stoploss = -0.05
2019-01-15 10:04:32 +00:00
2019-01-16 10:51:54 +00:00
# setting stoploss_on_exchange_interval to 60 seconds
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
2019-01-15 10:04:32 +00:00
2019-01-09 15:23:13 +00:00
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = 100
stoploss_order_hanging = MagicMock(return_value={
'id': 100,
'status': 'open',
'type': 'stop_loss_limit',
'price': 3,
'average': 2,
2019-01-15 10:10:28 +00:00
'info': {
'stopPrice': '0.000011134'
2019-01-09 15:23:13 +00:00
}
})
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging)
# stoploss initially at 5%
assert freqtrade.handle_trade(trade) is False
2019-01-16 10:22:25 +00:00
assert freqtrade.handle_stoploss_on_exchange(trade) is False
2019-01-09 15:23:13 +00:00
# price jumped 2x
2019-01-15 10:04:32 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(return_value={
'bid': 0.00002344,
'ask': 0.00002346,
'last': 0.00002344
}))
cancel_order_mock = MagicMock()
stoploss_order_mock = MagicMock()
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock)
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_order_mock)
2019-01-09 15:23:13 +00:00
2019-01-16 10:51:54 +00:00
# stoploss should not be updated as the interval is 60 seconds
assert freqtrade.handle_trade(trade) is False
assert freqtrade.handle_stoploss_on_exchange(trade) is False
cancel_order_mock.assert_not_called()
stoploss_order_mock.assert_not_called()
assert freqtrade.handle_trade(trade) is False
assert trade.stop_loss == 0.00002344 * 0.95
# setting stoploss_on_exchange_interval to 0 seconds
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
2019-01-09 15:23:13 +00:00
assert freqtrade.handle_stoploss_on_exchange(trade) is False
cancel_order_mock.assert_called_once_with(100, 'ETH/BTC')
stoploss_order_mock.assert_called_once_with(amount=85.25149190110828,
pair='ETH/BTC',
rate=0.00002344 * 0.95 * 0.99,
stop_price=0.00002344 * 0.95)
def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog,
markets, limit_buy_order,
limit_sell_order) -> None:
# When trailing stoploss is set
stoploss_limit = MagicMock(return_value={'id': 13434334})
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
'last': 0.00001172
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
get_fee=fee,
markets=PropertyMock(return_value=markets),
stoploss_limit=stoploss_limit
)
# enabling TSL
default_conf['trailing_stop'] = True
freqtrade = get_patched_freqtradebot(mocker, default_conf)
# enabling stoploss on exchange
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
# setting stoploss
freqtrade.strategy.stoploss = -0.05
# setting stoploss_on_exchange_interval to 60 seconds
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = "abcd"
trade.stop_loss = 0.2
trade.stoploss_last_update = arrow.utcnow().shift(minutes=-601).datetime.replace(tzinfo=None)
stoploss_order_hanging = {
'id': "abcd",
'status': 'open',
'type': 'stop_loss_limit',
'price': 3,
'average': 2,
'info': {
'stopPrice': '0.1'
}
}
mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException())
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging)
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
2019-08-11 18:17:39 +00:00
assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", caplog)
# Still try to create order
assert stoploss_limit.call_count == 1
# Fail creating stoploss order
caplog.clear()
cancel_mock = mocker.patch("freqtrade.exchange.Exchange.cancel_order", MagicMock())
mocker.patch("freqtrade.exchange.Exchange.stoploss_limit", side_effect=DependencyException())
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
assert cancel_mock.call_count == 1
2019-08-11 18:17:39 +00:00
assert log_has_re(r"Could create trailing stoploss order for pair ETH/BTC\..*", caplog)
2019-01-16 17:38:20 +00:00
def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
markets, limit_buy_order, limit_sell_order) -> None:
# When trailing stoploss is set
stoploss_limit = MagicMock(return_value={'id': 13434334})
patch_RPCManager(mocker)
patch_exchange(mocker)
patch_edge(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
'last': 0.00001172
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2019-01-16 17:38:20 +00:00
stoploss_limit=stoploss_limit
)
# enabling TSL
edge_conf['trailing_stop'] = True
edge_conf['trailing_stop_positive'] = 0.01
edge_conf['trailing_stop_positive_offset'] = 0.011
# disabling ROI
edge_conf['minimal_roi']['0'] = 999999999
freqtrade = FreqtradeBot(edge_conf)
# enabling stoploss on exchange
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
# setting stoploss
freqtrade.strategy.stoploss = -0.02
# setting stoploss_on_exchange_interval to 0 second
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
patch_get_signal(freqtrade)
freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist)
freqtrade.create_trade()
trade = Trade.query.first()
trade.is_open = True
trade.open_order_id = None
trade.stoploss_order_id = 100
stoploss_order_hanging = MagicMock(return_value={
'id': 100,
'status': 'open',
'type': 'stop_loss_limit',
'price': 3,
'average': 2,
'info': {
'stopPrice': '0.000009384'
}
})
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging)
# stoploss initially at 20% as edge dictated it.
assert freqtrade.handle_trade(trade) is False
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert trade.stop_loss == 0.000009384
cancel_order_mock = MagicMock()
stoploss_order_mock = MagicMock()
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock)
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_order_mock)
# price goes down 5%
mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(return_value={
'bid': 0.00001172 * 0.95,
'ask': 0.00001173 * 0.95,
'last': 0.00001172 * 0.95
}))
assert freqtrade.handle_trade(trade) is False
assert freqtrade.handle_stoploss_on_exchange(trade) is False
# stoploss should remain the same
assert trade.stop_loss == 0.000009384
# stoploss on exchange should not be canceled
cancel_order_mock.assert_not_called()
# price jumped 2x
mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(return_value={
'bid': 0.00002344,
'ask': 0.00002346,
'last': 0.00002344
}))
assert freqtrade.handle_trade(trade) is False
assert freqtrade.handle_stoploss_on_exchange(trade) is False
# stoploss should be set to 1% as trailing is on
assert trade.stop_loss == 0.00002344 * 0.99
cancel_order_mock.assert_called_once_with(100, 'NEO/BTC')
stoploss_order_mock.assert_called_once_with(amount=2131074.168797954,
pair='NEO/BTC',
rate=0.00002344 * 0.99 * 0.99,
stop_price=0.00002344 * 0.99)
def test_process_maybe_execute_buy(mocker, default_conf) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade', MagicMock(return_value=True))
assert freqtrade.process_maybe_execute_buy()
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade', MagicMock(return_value=False))
assert not freqtrade.process_maybe_execute_buy()
def test_process_maybe_execute_buy_exception(mocker, default_conf, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch(
'freqtrade.freqtradebot.FreqtradeBot.create_trade',
MagicMock(side_effect=DependencyException)
)
freqtrade.process_maybe_execute_buy()
2019-08-11 18:17:39 +00:00
assert log_has('Unable to create trade: ', caplog)
2018-04-22 09:05:23 +00:00
def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
2018-04-22 09:05:23 +00:00
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
return_value=limit_buy_order['amount'])
trade = MagicMock()
trade.open_order_id = '123'
2018-04-22 09:05:23 +00:00
trade.open_fee = 0.001
assert not freqtrade.process_maybe_execute_sell(trade)
2018-04-22 09:05:23 +00:00
# Test amount not modified by fee-logic
assert not log_has(
2019-08-11 18:17:39 +00:00
'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), caplog
)
2018-04-22 09:05:23 +00:00
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
# test amount modified by fee-logic
assert not freqtrade.process_maybe_execute_sell(trade)
def test_process_maybe_execute_sell_exception(mocker, default_conf,
limit_buy_order, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
trade = MagicMock()
trade.open_order_id = '123'
trade.open_fee = 0.001
2019-03-31 13:51:45 +00:00
# Test raise of DependencyException exception
mocker.patch(
'freqtrade.freqtradebot.FreqtradeBot.update_trade_state',
2019-03-31 13:51:45 +00:00
side_effect=DependencyException()
)
freqtrade.process_maybe_execute_sell(trade)
2019-08-11 18:17:39 +00:00
assert log_has('Unable to sell trade: ', caplog)
2019-03-31 13:51:45 +00:00
def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
return_value=limit_buy_order['amount'])
trade = Trade()
# Mock session away
Trade.session = MagicMock()
trade.open_order_id = '123'
trade.open_fee = 0.001
freqtrade.update_trade_state(trade)
# Test amount not modified by fee-logic
2019-08-11 18:17:39 +00:00
assert not log_has_re(r'Applying fee to .*', caplog)
assert trade.open_order_id is None
assert trade.amount == limit_buy_order['amount']
trade.open_order_id = '123'
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
assert trade.amount != 90.81
# test amount modified by fee-logic
freqtrade.update_trade_state(trade)
assert trade.amount == 90.81
assert trade.open_order_id is None
trade.is_open = True
trade.open_order_id = None
# Assert we call handle_trade() if trade is feasible for execution
freqtrade.update_trade_state(trade)
2019-08-11 18:17:39 +00:00
assert log_has_re('Found open order for.*', caplog)
def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, mocker):
2019-03-31 17:56:01 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
# get_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.get_order', MagicMock(side_effect=ValueError))
patch_exchange(mocker)
Trade.session = MagicMock()
amount = sum(x['amount'] for x in trades_for_order)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456",
is_open=True,
2019-03-31 17:56:01 +00:00
)
freqtrade.update_trade_state(trade, limit_buy_order)
2019-03-31 17:56:01 +00:00
assert trade.amount != amount
assert trade.amount == limit_buy_order['amount']
def test_update_trade_state_exception(mocker, default_conf,
limit_buy_order, caplog) -> None:
2019-03-31 13:51:45 +00:00
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
trade = MagicMock()
trade.open_order_id = '123'
trade.open_fee = 0.001
# Test raise of OperationalException exception
mocker.patch(
'freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
2019-03-31 13:51:45 +00:00
side_effect=OperationalException()
)
freqtrade.update_trade_state(trade)
2019-08-11 18:17:39 +00:00
assert log_has('Could not update trade amount: ', caplog)
def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.get_order',
MagicMock(side_effect=InvalidOrderException))
trade = MagicMock()
trade.open_order_id = '123'
trade.open_fee = 0.001
# Test raise of OperationalException exception
grm_mock = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", MagicMock())
freqtrade.update_trade_state(trade)
assert grm_mock.call_count == 0
2019-08-11 18:17:39 +00:00
assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog)
def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order, mocker):
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
# get_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.get_order', MagicMock(side_effect=ValueError))
wallet_mock = MagicMock()
mocker.patch('freqtrade.wallets.Wallets.update', wallet_mock)
patch_exchange(mocker)
Trade.session = MagicMock()
amount = limit_sell_order["amount"]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
wallet_mock.reset_mock()
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
fee_open=0.0025,
fee_close=0.0025,
open_order_id="123456",
is_open=True,
)
freqtrade.update_trade_state(trade, limit_sell_order)
assert trade.amount == limit_sell_order['amount']
# Wallet needs to be updated after closing a limit-sell order to reenable buying
assert wallet_mock.call_count == 1
assert not trade.is_open
2018-06-16 23:23:12 +00:00
def test_handle_trade(default_conf, limit_buy_order, limit_sell_order,
fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
'last': 0.00001172
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
2018-06-16 23:23:12 +00:00
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
assert trade
time.sleep(0.01) # Race condition fix
trade.update(limit_buy_order)
assert trade.is_open is True
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.open_order_id == limit_sell_order['id']
# Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order)
assert trade.close_rate == 0.00001173
assert trade.close_profit == 0.06201058
assert trade.calc_profit() == 0.00006217
assert trade.close_date is not None
2018-06-16 23:23:12 +00:00
def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
fee, markets, mocker) -> None:
default_conf.update({'experimental': {'use_sell_signal': True}})
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade, value=(True, True))
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade()
# Buy and Sell triggering, so doing nothing ...
trades = Trade.query.all()
nb_trades = len(trades)
assert nb_trades == 0
# Buy is triggering, so buying ...
patch_get_signal(freqtrade, value=(True, False))
freqtrade.create_trade()
trades = Trade.query.all()
nb_trades = len(trades)
assert nb_trades == 1
assert trades[0].is_open is True
# Buy and Sell are not triggering, so doing nothing ...
patch_get_signal(freqtrade, value=(False, False))
assert freqtrade.handle_trade(trades[0]) is False
trades = Trade.query.all()
nb_trades = len(trades)
assert nb_trades == 1
assert trades[0].is_open is True
# Buy and Sell are triggering, so doing nothing ...
patch_get_signal(freqtrade, value=(True, True))
assert freqtrade.handle_trade(trades[0]) is False
trades = Trade.query.all()
nb_trades = len(trades)
assert nb_trades == 1
assert trades[0].is_open is True
# Sell is triggering, guess what : we are Selling!
patch_get_signal(freqtrade, value=(False, True))
trades = Trade.query.all()
assert freqtrade.handle_trade(trades[0]) is True
2018-06-16 23:23:12 +00:00
def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
fee, mocker, markets, caplog) -> None:
caplog.set_level(logging.DEBUG)
default_conf.update({'experimental': {'use_sell_signal': True}})
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade, value=(True, False))
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
freqtrade.create_trade()
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
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade)
2019-08-11 18:17:39 +00:00
assert log_has('Required profit reached. Selling..', caplog)
2018-04-21 17:39:18 +00:00
def test_handle_trade_experimental(
2018-06-16 23:23:12 +00:00
default_conf, ticker, limit_buy_order, fee, mocker, markets, caplog) -> None:
caplog.set_level(logging.DEBUG)
default_conf.update({'experimental': {'use_sell_signal': True}})
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade()
trade = Trade.query.first()
trade.is_open = True
patch_get_signal(freqtrade, value=(False, False))
assert not freqtrade.handle_trade(trade)
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade)
2019-08-11 18:17:39 +00:00
assert log_has('Sell signal received. Selling..', caplog)
2018-06-16 23:23:12 +00:00
def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# Create trade and sell it
freqtrade.create_trade()
trade = Trade.query.first()
assert trade
trade.update(limit_buy_order)
trade.update(limit_sell_order)
assert trade.is_open is False
with pytest.raises(ValueError, match=r'.*closed trade.*'):
freqtrade.handle_trade(trade)
def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fee, mocker) -> None:
rpc_mock = patch_RPCManager(mocker)
cancel_order_mock = MagicMock()
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old),
cancel_order=cancel_order_mock,
get_fee=fee
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
trade_buy = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
2018-04-22 08:04:30 +00:00
fee_open=0.0,
fee_close=0.0,
stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True
)
Trade.session.add(trade_buy)
# check it does cancel buy orders over the time limit
freqtrade.check_handle_timedout()
assert cancel_order_mock.call_count == 1
assert rpc_mock.call_count == 1
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
nb_trades = len(trades)
assert nb_trades == 0
2019-02-03 12:39:19 +00:00
def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old,
fee, mocker, caplog) -> None:
""" Handle Buy order cancelled on exchange"""
rpc_mock = patch_RPCManager(mocker)
cancel_order_mock = MagicMock()
patch_exchange(mocker)
limit_buy_order_old.update({"status": "canceled"})
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old),
cancel_order=cancel_order_mock,
get_fee=fee
)
freqtrade = FreqtradeBot(default_conf)
trade_buy = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
fee_open=0.0,
fee_close=0.0,
stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True
)
Trade.session.add(trade_buy)
# check it does cancel buy orders over the time limit
freqtrade.check_handle_timedout()
assert cancel_order_mock.call_count == 0
assert rpc_mock.call_count == 1
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
nb_trades = len(trades)
assert nb_trades == 0
2019-08-11 18:17:39 +00:00
assert log_has_re("Buy order canceled on Exchange for Trade.*", caplog)
2019-02-03 12:39:19 +00:00
def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old,
fee, mocker) -> None:
rpc_mock = patch_RPCManager(mocker)
cancel_order_mock = MagicMock()
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
get_order=MagicMock(side_effect=DependencyException),
cancel_order=cancel_order_mock,
get_fee=fee
)
freqtrade = FreqtradeBot(default_conf)
trade_buy = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
fee_open=0.0,
fee_close=0.0,
stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True
)
Trade.session.add(trade_buy)
# check it does cancel buy orders over the time limit
freqtrade.check_handle_timedout()
assert cancel_order_mock.call_count == 0
assert rpc_mock.call_count == 0
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
nb_trades = len(trades)
assert nb_trades == 1
def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, mocker) -> None:
rpc_mock = patch_RPCManager(mocker)
cancel_order_mock = MagicMock()
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
get_order=MagicMock(return_value=limit_sell_order_old),
cancel_order=cancel_order_mock
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
trade_sell = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
2018-04-22 08:04:30 +00:00
fee_open=0.0,
fee_close=0.0,
stake_amount=1,
open_date=arrow.utcnow().shift(hours=-5).datetime,
close_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=False
)
Trade.session.add(trade_sell)
# check it does cancel sell orders over the time limit
freqtrade.check_handle_timedout()
assert cancel_order_mock.call_count == 1
assert rpc_mock.call_count == 1
assert trade_sell.is_open is True
2019-02-03 12:50:15 +00:00
def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old,
mocker, caplog) -> None:
2019-02-03 12:39:19 +00:00
""" Handle sell order cancelled on exchange"""
rpc_mock = patch_RPCManager(mocker)
cancel_order_mock = MagicMock()
limit_sell_order_old.update({"status": "canceled"})
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker,
get_order=MagicMock(return_value=limit_sell_order_old),
cancel_order=cancel_order_mock
)
freqtrade = FreqtradeBot(default_conf)
trade_sell = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
fee_open=0.0,
fee_close=0.0,
stake_amount=1,
open_date=arrow.utcnow().shift(hours=-5).datetime,
close_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=False
)
Trade.session.add(trade_sell)
# check it does cancel sell orders over the time limit
freqtrade.check_handle_timedout()
assert cancel_order_mock.call_count == 0
assert rpc_mock.call_count == 1
assert trade_sell.is_open is True
2019-08-11 18:17:39 +00:00
assert log_has_re("Sell order canceled on exchange for Trade.*", caplog)
2019-02-03 12:39:19 +00:00
def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial,
mocker) -> None:
rpc_mock = patch_RPCManager(mocker)
cancel_order_mock = MagicMock()
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old_partial),
cancel_order=cancel_order_mock
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
trade_buy = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
2018-04-22 08:04:30 +00:00
fee_open=0.0,
fee_close=0.0,
stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True
)
Trade.session.add(trade_buy)
# check it does cancel buy orders over the time limit
# note this is for a partially-complete buy order
freqtrade.check_handle_timedout()
assert cancel_order_mock.call_count == 1
assert rpc_mock.call_count == 1
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
assert len(trades) == 1
assert trades[0].amount == 23.0
assert trades[0].stake_amount == trade_buy.open_rate * trades[0].amount
2018-03-05 08:11:13 +00:00
def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-03-05 08:11:13 +00:00
cancel_order_mock = MagicMock()
mocker.patch.multiple(
'freqtrade.freqtradebot.FreqtradeBot',
handle_timedout_limit_buy=MagicMock(),
handle_timedout_limit_sell=MagicMock(),
)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
2018-03-05 08:11:13 +00:00
get_ticker=ticker,
get_order=MagicMock(side_effect=requests.exceptions.RequestException('Oh snap')),
cancel_order=cancel_order_mock
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
2018-03-05 08:11:13 +00:00
trade_buy = Trade(
pair='ETH/BTC',
2018-03-05 08:11:13 +00:00
open_rate=0.00001099,
exchange='bittrex',
2018-03-05 08:11:13 +00:00
open_order_id='123456789',
amount=90.99181073,
2018-04-22 08:04:30 +00:00
fee_open=0.0,
fee_close=0.0,
2018-03-05 08:11:13 +00:00
stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True
)
Trade.session.add(trade_buy)
freqtrade.check_handle_timedout()
assert log_has_re(r'Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, '
r'open_rate=0.00001099, open_since=10 hours ago\) due to Traceback \(most '
2019-08-11 18:17:39 +00:00
r'recent call last\):\n.*', caplog)
2018-03-05 08:11:13 +00:00
def test_handle_timedout_limit_buy(mocker, default_conf) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
cancel_order_mock = MagicMock()
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
cancel_order=cancel_order_mock
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
Trade.session = MagicMock()
trade = MagicMock()
order = {'remaining': 1,
'amount': 1}
assert freqtrade.handle_timedout_limit_buy(trade, order)
assert cancel_order_mock.call_count == 1
order['amount'] = 2
assert not freqtrade.handle_timedout_limit_buy(trade, order)
assert cancel_order_mock.call_count == 2
def test_handle_timedout_limit_sell(mocker, default_conf) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
cancel_order_mock = MagicMock()
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
cancel_order=cancel_order_mock
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
trade = MagicMock()
order = {'remaining': 1,
2019-02-03 12:39:19 +00:00
'amount': 1,
'status': "open"}
assert freqtrade.handle_timedout_limit_sell(trade, order)
assert cancel_order_mock.call_count == 1
order['amount'] = 2
assert not freqtrade.handle_timedout_limit_sell(trade, order)
# Assert cancel_order was not called (callcount remains unchanged)
assert cancel_order_mock.call_count == 1
2018-06-16 23:23:12 +00:00
def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, mocker) -> None:
rpc_mock = patch_RPCManager(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
_load_markets=MagicMock(return_value={}),
get_ticker=ticker,
2018-06-16 23:23:12 +00:00
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trade()
trade = Trade.query.first()
assert trade
# Increase the price and sell it
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker_sell_up
)
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
assert rpc_mock.call_count == 2
last_msg = rpc_mock.call_args_list[-1][0][0]
assert {
'type': RPCMessageType.SELL_NOTIFICATION,
'exchange': 'Bittrex',
'pair': 'ETH/BTC',
'gain': 'profit',
'limit': 1.172e-05,
'amount': 90.99181073703367,
2019-06-17 04:55:54 +00:00
'order_type': 'limit',
'open_rate': 1.099e-05,
'current_rate': 1.172e-05,
'profit_amount': 6.126e-05,
'profit_percent': 0.0611052,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.ROI.value
} == last_msg
2018-12-01 09:52:36 +00:00
def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets, mocker) -> None:
rpc_mock = patch_RPCManager(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
_load_markets=MagicMock(return_value={}),
get_ticker=ticker,
2018-06-16 23:23:12 +00:00
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trade()
trade = Trade.query.first()
assert trade
# Decrease the price and sell it
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=ticker_sell_down
)
2018-12-01 09:50:41 +00:00
2018-07-11 17:57:20 +00:00
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'],
sell_reason=SellType.STOP_LOSS)
assert rpc_mock.call_count == 2
last_msg = rpc_mock.call_args_list[-1][0][0]
assert {
'type': RPCMessageType.SELL_NOTIFICATION,
'exchange': 'Bittrex',
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.044e-05,
'amount': 90.99181073703367,
2019-06-17 04:55:54 +00:00
'order_type': 'limit',
'open_rate': 1.099e-05,
'current_rate': 1.044e-05,
'profit_amount': -5.492e-05,
'profit_percent': -0.05478342,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.STOP_LOSS.value
} == last_msg
2018-12-01 09:43:26 +00:00
def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee,
ticker_sell_down,
markets, mocker) -> None:
2018-11-25 19:16:53 +00:00
rpc_mock = patch_RPCManager(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
_load_markets=MagicMock(return_value={}),
get_ticker=ticker,
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-11-25 19:16:53 +00:00
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trade()
trade = Trade.query.first()
assert trade
# Decrease the price and sell it
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker_sell_down
)
default_conf['dry_run'] = True
2018-12-01 09:43:26 +00:00
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
2018-11-25 19:16:53 +00:00
# Setting trade stoploss to 0.01
2018-12-01 09:43:26 +00:00
trade.stop_loss = 0.00001099 * 0.99
2018-11-25 19:16:53 +00:00
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'],
sell_reason=SellType.STOP_LOSS)
assert rpc_mock.call_count == 2
last_msg = rpc_mock.call_args_list[-1][0][0]
2018-12-01 09:43:26 +00:00
2018-11-25 19:16:53 +00:00
assert {
'type': RPCMessageType.SELL_NOTIFICATION,
'exchange': 'Bittrex',
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.08801e-05,
'amount': 90.99181073703367,
2019-06-17 04:55:54 +00:00
'order_type': 'limit',
2018-11-25 19:16:53 +00:00
'open_rate': 1.099e-05,
'current_rate': 1.044e-05,
'profit_amount': -1.498e-05,
'profit_percent': -0.01493766,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.STOP_LOSS.value
2018-11-25 19:16:53 +00:00
} == last_msg
def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee,
markets, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException())
sellmock = MagicMock()
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
_load_markets=MagicMock(return_value={}),
get_ticker=ticker,
get_fee=fee,
markets=PropertyMock(return_value=markets),
sell=sellmock
)
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
Trade.session = MagicMock()
freqtrade.config['dry_run'] = False
trade.stoploss_order_id = "abcd"
freqtrade.execute_sell(trade=trade, limit=1234,
sell_reason=SellType.STOP_LOSS)
assert sellmock.call_count == 1
2019-08-11 18:17:39 +00:00
assert log_has('Could not cancel stoploss order abcd', caplog)
def test_execute_sell_with_stoploss_on_exchange(default_conf,
ticker, fee, ticker_sell_up,
markets, mocker) -> None:
default_conf['exchange']['name'] = 'binance'
2018-11-22 20:12:49 +00:00
rpc_mock = patch_RPCManager(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
_load_markets=MagicMock(return_value={}),
get_ticker=ticker,
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
stoploss_limit = MagicMock(return_value={
'id': 123,
'info': {
'foo': 'bar'
}
})
cancel_order = MagicMock(return_value=True)
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order)
freqtrade = FreqtradeBot(default_conf)
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trade()
trade = Trade.query.first()
assert trade
2018-11-23 19:28:01 +00:00
freqtrade.process_maybe_execute_sell(trade)
# Increase the price and sell it
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker_sell_up
)
2018-11-23 14:17:36 +00:00
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'],
sell_reason=SellType.SELL_SIGNAL)
trade = Trade.query.first()
assert trade
assert cancel_order.call_count == 1
2018-11-22 20:12:49 +00:00
assert rpc_mock.call_count == 2
2018-11-23 14:17:36 +00:00
def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf,
ticker, fee,
limit_buy_order,
markets, mocker) -> None:
default_conf['exchange']['name'] = 'binance'
rpc_mock = patch_RPCManager(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
_load_markets=MagicMock(return_value={}),
get_ticker=ticker,
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-11-23 14:17:36 +00:00
)
stoploss_limit = MagicMock(return_value={
'id': 123,
'info': {
'foo': 'bar'
}
})
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
freqtrade = FreqtradeBot(default_conf)
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
2018-11-23 14:17:36 +00:00
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trade()
trade = Trade.query.first()
2018-11-23 19:28:01 +00:00
freqtrade.process_maybe_execute_sell(trade)
2018-11-23 14:17:36 +00:00
assert trade
assert trade.stoploss_order_id == '123'
2018-11-23 19:28:01 +00:00
assert trade.open_order_id is None
2018-11-23 14:17:36 +00:00
# Assuming stoploss on exchnage is hit
# stoploss_order_id should become None
# and trade should be sold at the price of stoploss
stoploss_limit_executed = MagicMock(return_value={
"id": "123",
"timestamp": 1542707426845,
"datetime": "2018-11-20T09:50:26.845Z",
"lastTradeTimestamp": None,
"symbol": "BTC/USDT",
"type": "stop_loss_limit",
"side": "sell",
"price": 1.08801,
"amount": 90.99181074,
"cost": 99.0000000032274,
"average": 1.08801,
"filled": 90.99181074,
"remaining": 0.0,
"status": "closed",
"fee": None,
"trades": None
})
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_limit_executed)
freqtrade.process_maybe_execute_sell(trade)
assert trade.stoploss_order_id is None
assert trade.is_open is False
print(trade.sell_reason)
2018-11-26 17:28:13 +00:00
assert trade.sell_reason == SellType.STOPLOSS_ON_EXCHANGE.value
assert rpc_mock.call_count == 2
2018-11-23 14:17:36 +00:00
2018-06-16 23:23:12 +00:00
def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00002172,
'ask': 0.00002173,
'last': 0.00002172
}),
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['experimental'] = {
'use_sell_signal': True,
'sell_profit_only': True,
}
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.SELL_SIGNAL.value
2018-06-16 23:23:12 +00:00
def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00002172,
'ask': 0.00002173,
'last': 0.00002172
}),
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['experimental'] = {
'use_sell_signal': True,
'sell_profit_only': False,
}
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.SELL_SIGNAL.value
2018-06-16 23:23:12 +00:00
def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00000172,
'ask': 0.00000173,
'last': 0.00000172
}),
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['experimental'] = {
'use_sell_signal': True,
'sell_profit_only': True,
}
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
sell_flag=False, sell_type=SellType.NONE))
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is False
2018-06-16 23:23:12 +00:00
def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, markets, mocker) -> None:
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
mocker.patch.multiple(
2018-06-17 19:11:10 +00:00
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.0000172,
'ask': 0.0000173,
'last': 0.0000172
}),
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['experimental'] = {
'use_sell_signal': True,
'sell_profit_only': False,
}
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.SELL_SIGNAL.value
2018-04-15 17:38:58 +00:00
2019-08-12 18:48:21 +00:00
def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, markets, mocker, caplog) -> None:
patch_RPCManager(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
_load_markets=MagicMock(return_value={}),
get_ticker=ticker,
get_fee=fee,
markets=PropertyMock(return_value=markets)
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# Create some test data
freqtrade.create_trade()
trade = Trade.query.first()
assert trade
# Decrease the price and sell it
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker_sell_down
)
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'],
sell_reason=SellType.STOP_LOSS)
trade.close(ticker_sell_down()['bid'])
assert trade.pair in freqtrade.strategy._pair_locked_until
assert freqtrade.strategy.is_pair_locked(trade.pair)
# reinit - should buy other pair.
caplog.clear()
freqtrade.create_trade()
assert log_has(f"Pair {trade.pair} is currently locked.", caplog)
2018-06-30 15:49:46 +00:00
def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, mocker) -> None:
2018-06-22 18:10:05 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-22 18:10:05 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.0000172,
'ask': 0.0000173,
'last': 0.0000172
2018-06-22 18:10:05 +00:00
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-06-22 18:10:05 +00:00
)
default_conf['experimental'] = {
2018-06-22 18:10:05 +00:00
'ignore_roi_if_buy_signal': True
}
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
2018-06-22 18:10:05 +00:00
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
patch_get_signal(freqtrade, value=(True, True))
2018-06-22 18:10:05 +00:00
assert freqtrade.handle_trade(trade) is False
# Test if buy-signal is absent (should sell due to roi = true)
patch_get_signal(freqtrade, value=(False, True))
2018-06-22 18:10:05 +00:00
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.ROI.value
2018-06-22 18:10:05 +00:00
2018-07-22 12:24:41 +00:00
def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog, mocker) -> None:
2018-06-26 21:40:36 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-26 21:40:36 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
2019-06-02 11:27:31 +00:00
'bid': 0.00001099,
'ask': 0.00001099,
'last': 0.00001099
2018-06-26 21:40:36 +00:00
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2018-06-26 21:40:36 +00:00
)
default_conf['trailing_stop'] = True
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
2018-06-26 21:40:36 +00:00
freqtrade.create_trade()
trade = Trade.query.first()
2019-06-02 11:27:31 +00:00
assert freqtrade.handle_trade(trade) is False
# Raise ticker above buy price
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={
'bid': 0.00001099 * 1.5,
'ask': 0.00001099 * 1.5,
'last': 0.00001099 * 1.5
}))
# Stoploss should be adjusted
assert freqtrade.handle_trade(trade) is False
# Price fell
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={
'bid': 0.00001099 * 1.1,
'ask': 0.00001099 * 1.1,
'last': 0.00001099 * 1.1
}))
2018-06-26 21:40:36 +00:00
caplog.set_level(logging.DEBUG)
2018-06-26 22:16:19 +00:00
# Sell as trailing-stop is reached
2018-06-26 21:40:36 +00:00
assert freqtrade.handle_trade(trade) is True
assert log_has(
2019-06-02 11:27:31 +00:00
f'HIT STOP: current price at 0.000012, stop loss is 0.000015, '
2019-08-11 18:17:39 +00:00
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
2018-06-26 21:40:36 +00:00
2018-07-22 12:24:41 +00:00
def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets,
caplog, mocker) -> None:
2018-06-27 04:51:48 +00:00
buy_price = limit_buy_order['price']
2018-06-26 22:16:19 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-26 22:16:19 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
2018-06-27 04:51:48 +00:00
'bid': buy_price - 0.000001,
'ask': buy_price - 0.000001,
'last': buy_price - 0.000001
2018-06-26 22:16:19 +00:00
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2018-06-26 22:16:19 +00:00
)
default_conf['trailing_stop'] = True
default_conf['trailing_stop_positive'] = 0.01
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
2018-06-26 22:16:19 +00:00
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
caplog.set_level(logging.DEBUG)
# stop-loss not reached
assert freqtrade.handle_trade(trade) is False
2018-06-27 04:51:48 +00:00
# Raise ticker above buy price
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={
'bid': buy_price + 0.000003,
'ask': buy_price + 0.000003,
'last': buy_price + 0.000003
}))
# stop-loss not reached, adjusted stoploss
assert freqtrade.handle_trade(trade) is False
2019-08-11 18:17:39 +00:00
assert log_has(f'using positive stop loss: 0.01 offset: 0 profit: 0.2666%', caplog)
assert log_has(f'adjusted stop loss', caplog)
2018-07-16 19:23:35 +00:00
assert trade.stop_loss == 0.0000138501
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={
'bid': buy_price + 0.000002,
'ask': buy_price + 0.000002,
'last': buy_price + 0.000002
}))
# Lower price again (but still positive)
assert freqtrade.handle_trade(trade) is True
assert log_has(
f'HIT STOP: current price at {buy_price + 0.000002:.6f}, '
f'stop loss is {trade.stop_loss:.6f}, '
2019-08-11 18:17:39 +00:00
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
2018-07-16 19:23:35 +00:00
def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
caplog, mocker, markets) -> None:
2018-07-16 19:23:35 +00:00
buy_price = limit_buy_order['price']
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-07-16 19:23:35 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': buy_price - 0.000001,
'ask': buy_price - 0.000001,
'last': buy_price - 0.000001
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2018-07-16 19:23:35 +00:00
)
default_conf['trailing_stop'] = True
default_conf['trailing_stop_positive'] = 0.01
default_conf['trailing_stop_positive_offset'] = 0.011
freqtrade = FreqtradeBot(default_conf)
2018-07-19 18:36:49 +00:00
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
2018-07-16 19:23:35 +00:00
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
caplog.set_level(logging.DEBUG)
# stop-loss not reached
assert freqtrade.handle_trade(trade) is False
# Raise ticker above buy price
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={
'bid': buy_price + 0.000003,
'ask': buy_price + 0.000003,
'last': buy_price + 0.000003
}))
# stop-loss not reached, adjusted stoploss
assert freqtrade.handle_trade(trade) is False
2019-08-11 18:17:39 +00:00
assert log_has(f'using positive stop loss: 0.01 offset: 0.011 profit: 0.2666%', caplog)
assert log_has(f'adjusted stop loss', caplog)
2018-06-27 04:51:48 +00:00
assert trade.stop_loss == 0.0000138501
2018-06-26 22:16:19 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={
2018-06-27 04:51:48 +00:00
'bid': buy_price + 0.000002,
'ask': buy_price + 0.000002,
'last': buy_price + 0.000002
}))
# Lower price again (but still positive)
2018-06-26 22:16:19 +00:00
assert freqtrade.handle_trade(trade) is True
assert log_has(
2018-06-27 04:51:48 +00:00
f'HIT STOP: current price at {buy_price + 0.000002:.6f}, '
f'stop loss is {trade.stop_loss:.6f}, '
2019-08-11 18:17:39 +00:00
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
2018-06-26 21:40:36 +00:00
2018-06-27 04:51:48 +00:00
2019-03-10 16:11:28 +00:00
def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
caplog, mocker, markets) -> None:
buy_price = limit_buy_order['price']
# buy_price: 0.00001099
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': buy_price,
'ask': buy_price,
'last': buy_price
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-14 08:00:28 +00:00
markets=PropertyMock(return_value=markets),
2019-03-10 16:11:28 +00:00
)
default_conf['trailing_stop'] = True
default_conf['trailing_stop_positive'] = 0.05
default_conf['trailing_stop_positive_offset'] = 0.055
default_conf['trailing_only_offset_is_reached'] = True
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
caplog.set_level(logging.DEBUG)
# stop-loss not reached
assert freqtrade.handle_trade(trade) is False
assert trade.stop_loss == 0.0000098910
# Raise ticker above buy price
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={
'bid': buy_price + 0.0000004,
'ask': buy_price + 0.0000004,
'last': buy_price + 0.0000004
}))
# stop-loss should not be adjusted as offset is not reached yet
assert freqtrade.handle_trade(trade) is False
2019-08-11 18:17:39 +00:00
assert not log_has(f'adjusted stop loss', caplog)
2019-03-10 16:11:28 +00:00
assert trade.stop_loss == 0.0000098910
# price rises above the offset (rises 12% when the offset is 5.5%)
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(return_value={
'bid': buy_price + 0.0000014,
'ask': buy_price + 0.0000014,
'last': buy_price + 0.0000014
}))
assert freqtrade.handle_trade(trade) is False
2019-08-11 18:17:39 +00:00
assert log_has(f'using positive stop loss: 0.05 offset: 0.055 profit: 0.1218%', caplog)
assert log_has(f'adjusted stop loss', caplog)
2019-03-10 16:11:28 +00:00
assert trade.stop_loss == 0.0000117705
2018-06-23 13:50:27 +00:00
def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
fee, markets, mocker) -> None:
2018-06-22 18:10:05 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-22 18:10:05 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00000172,
'ask': 0.00000173,
'last': 0.00000172
}),
2018-04-21 17:39:18 +00:00
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
)
default_conf['experimental'] = {
2018-06-22 18:10:05 +00:00
'ignore_roi_if_buy_signal': False
}
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-11-25 13:31:46 +00:00
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
2018-06-22 18:10:05 +00:00
# Sell due to min_roi_reached
patch_get_signal(freqtrade, value=(True, True))
2018-06-22 18:10:05 +00:00
assert freqtrade.handle_trade(trade) is True
# Test if buy-signal is absent
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.STOP_LOSS.value
2018-04-15 17:38:58 +00:00
2018-04-25 06:52:08 +00:00
def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
2018-04-15 17:38:58 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-04-15 17:56:33 +00:00
amount = sum(x['amount'] for x in trades_for_order)
2018-04-15 17:38:58 +00:00
trade = Trade(
pair='LTC/ETH',
2018-04-15 17:56:33 +00:00
amount=amount,
2018-04-15 17:38:58 +00:00
exchange='binance',
2018-04-25 06:52:08 +00:00
open_rate=0.245441,
2018-04-15 17:38:58 +00:00
open_order_id="123456"
2018-10-04 16:07:47 +00:00
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-04-15 17:56:33 +00:00
# Amount is reduced by "fee"
2018-04-25 06:52:08 +00:00
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades',
2019-08-11 18:17:39 +00:00
caplog)
2018-04-15 17:56:33 +00:00
2018-04-25 07:08:02 +00:00
def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker):
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
2018-04-25 07:08:02 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-04-25 07:08:02 +00:00
amount = buy_order_fee['amount']
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-04-25 07:08:02 +00:00
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found',
2019-08-11 18:17:39 +00:00
caplog)
2018-04-25 07:08:02 +00:00
2018-04-25 07:13:56 +00:00
def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, mocker):
2018-04-15 17:56:33 +00:00
trades_for_order[0]['fee']['currency'] = 'ETH'
2018-04-15 17:38:58 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
2018-04-15 17:56:33 +00:00
amount = sum(x['amount'] for x in trades_for_order)
2018-04-15 17:38:58 +00:00
trade = Trade(
2018-04-15 17:56:33 +00:00
pair='LTC/ETH',
amount=amount,
2018-04-15 17:38:58 +00:00
exchange='binance',
2018-04-25 06:52:08 +00:00
open_rate=0.245441,
2018-04-15 17:38:58 +00:00
open_order_id="123456"
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-04-15 17:56:33 +00:00
# Amount does not change
2018-04-25 06:52:08 +00:00
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
2018-04-15 17:56:33 +00:00
def test_get_real_amount_no_currency_in_fee(default_conf, trades_for_order, buy_order_fee, mocker):
limit_buy_order = deepcopy(buy_order_fee)
limit_buy_order['fee'] = {'cost': 0.004, 'currency': None}
trades_for_order[0]['fee']['currency'] = None
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
amount = sum(x['amount'] for x in trades_for_order)
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
# Amount does not change
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount
2018-04-25 06:52:08 +00:00
def test_get_real_amount_BNB(default_conf, trades_for_order, buy_order_fee, mocker):
2018-04-15 17:56:33 +00:00
trades_for_order[0]['fee']['currency'] = 'BNB'
trades_for_order[0]['fee']['cost'] = 0.00094518
2018-04-15 17:38:58 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
2018-04-15 17:56:33 +00:00
amount = sum(x['amount'] for x in trades_for_order)
2018-04-15 17:38:58 +00:00
trade = Trade(
2018-04-15 17:56:33 +00:00
pair='LTC/ETH',
amount=amount,
2018-04-15 17:38:58 +00:00
exchange='binance',
2018-04-25 06:52:08 +00:00
open_rate=0.245441,
2018-04-15 17:38:58 +00:00
open_order_id="123456"
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-04-15 17:56:33 +00:00
# Amount does not change
2018-04-25 06:52:08 +00:00
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
2018-04-15 17:56:33 +00:00
2018-04-25 06:52:08 +00:00
def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, caplog, mocker):
2018-04-15 17:38:58 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order2)
2018-04-15 17:56:33 +00:00
amount = float(sum(x['amount'] for x in trades_for_order2))
2018-04-15 17:38:58 +00:00
trade = Trade(
pair='LTC/ETH',
2018-04-15 17:56:33 +00:00
amount=amount,
2018-04-15 17:38:58 +00:00
exchange='binance',
2018-04-25 06:52:08 +00:00
open_rate=0.245441,
2018-04-15 17:38:58 +00:00
open_order_id="123456"
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-04-15 17:56:33 +00:00
# Amount is reduced by "fee"
2018-04-25 06:52:08 +00:00
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades',
2019-08-11 18:17:39 +00:00
caplog)
2018-04-25 06:52:08 +00:00
def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
limit_buy_order = deepcopy(buy_order_fee)
limit_buy_order['fee'] = {'cost': 0.004, 'currency': 'LTC'}
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order',
return_value=[trades_for_order])
2018-04-25 06:52:08 +00:00
amount = float(sum(x['amount'] for x in trades_for_order))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-04-25 06:52:08 +00:00
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996) from Order',
2019-08-11 18:17:39 +00:00
caplog)
2018-05-15 17:49:47 +00:00
2018-05-15 18:13:43 +00:00
def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, mocker):
limit_buy_order = deepcopy(buy_order_fee)
limit_buy_order['fee'] = {'cost': 0.004}
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
2018-05-15 18:13:43 +00:00
amount = float(sum(x['amount'] for x in trades_for_order))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-05-15 18:13:43 +00:00
# Amount does not change
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount
def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee, mocker):
2018-05-15 17:49:47 +00:00
# Remove "Currency" from fee dict
trades_for_order[0]['fee'] = {'cost': 0.008}
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-06-17 19:11:10 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
2018-05-15 17:49:47 +00:00
amount = sum(x['amount'] for x in trades_for_order)
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
2018-06-07 03:27:55 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
2018-05-15 17:49:47 +00:00
# Amount does not change
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
def test_get_real_amount_open_trade(default_conf, mocker):
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
amount = 12345
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
order = {
'id': 'mocked_order',
'amount': amount,
'status': 'open',
}
2018-06-08 06:12:03 +00:00
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
assert freqtrade.get_real_amount(trade, order) == amount
2018-08-15 04:05:56 +00:00
2018-08-14 10:12:44 +00:00
def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order, fee, markets, mocker,
order_book_l2):
default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True
default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 0.1
2018-08-05 04:41:06 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-08-14 10:12:44 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
2018-08-05 04:41:06 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-08-05 04:41:06 +00:00
)
# Save state of current whitelist
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
assert trade is not None
assert trade.stake_amount == 0.001
assert trade.is_open
assert trade.open_date is not None
assert trade.exchange == 'bittrex'
# Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order)
assert trade.open_rate == 0.00001099
assert whitelist == default_conf['exchange']['pair_whitelist']
2018-08-05 14:41:58 +00:00
2018-08-14 10:12:44 +00:00
def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_order,
fee, markets, mocker, order_book_l2):
default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True
2018-08-07 10:29:37 +00:00
# delta is 100 which is impossible to reach. hence check_depth_of_market will return false
default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 100
2018-08-05 14:41:58 +00:00
patch_RPCManager(mocker)
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-08-14 10:12:44 +00:00
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
2018-08-05 14:41:58 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-08-05 14:41:58 +00:00
)
# Save state of current whitelist
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
assert trade is None
2018-08-25 11:21:10 +00:00
def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2, markets) -> None:
2018-08-07 10:29:37 +00:00
"""
test if function get_target_bid will return the order book price
instead of the ask rate
"""
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2019-02-14 06:27:13 +00:00
ticker_mock = MagicMock(return_value={'ask': 0.045, 'last': 0.046})
2018-08-25 11:21:10 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2019-02-14 06:27:13 +00:00
get_order_book=order_book_l2,
get_ticker=ticker_mock,
2018-08-25 11:21:10 +00:00
)
2018-08-05 14:41:58 +00:00
default_conf['exchange']['name'] = 'binance'
default_conf['bid_strategy']['use_order_book'] = True
default_conf['bid_strategy']['order_book_top'] = 2
2018-08-05 14:56:14 +00:00
default_conf['bid_strategy']['ask_last_balance'] = 0
2018-08-05 14:41:58 +00:00
default_conf['telegram']['enabled'] = False
freqtrade = FreqtradeBot(default_conf)
2019-02-14 06:27:13 +00:00
assert freqtrade.get_target_bid('ETH/BTC') == 0.043935
assert ticker_mock.call_count == 0
2018-08-05 14:41:58 +00:00
2018-08-25 11:21:10 +00:00
def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets) -> None:
2018-08-14 10:12:44 +00:00
"""
test if function get_target_bid will return the ask rate (since its value is lower)
instead of the order book rate (even if enabled)
"""
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2019-02-14 06:27:13 +00:00
ticker_mock = MagicMock(return_value={'ask': 0.042, 'last': 0.046})
2018-08-25 11:21:10 +00:00
mocker.patch.multiple(
2018-10-04 16:07:47 +00:00
'freqtrade.exchange.Exchange',
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2019-02-14 06:27:13 +00:00
get_order_book=order_book_l2,
get_ticker=ticker_mock,
2018-08-14 10:12:44 +00:00
2018-08-25 11:21:10 +00:00
)
2018-08-07 10:29:37 +00:00
default_conf['exchange']['name'] = 'binance'
default_conf['bid_strategy']['use_order_book'] = True
2019-02-14 06:27:13 +00:00
default_conf['bid_strategy']['order_book_top'] = 2
2018-08-07 10:29:37 +00:00
default_conf['bid_strategy']['ask_last_balance'] = 0
2018-08-05 14:41:58 +00:00
default_conf['telegram']['enabled'] = False
2018-08-07 10:29:37 +00:00
2018-08-05 14:41:58 +00:00
freqtrade = FreqtradeBot(default_conf)
2019-02-14 18:15:16 +00:00
# ordrebook shall be used even if tickers would be lower.
assert freqtrade.get_target_bid('ETH/BTC', ) != 0.042
assert ticker_mock.call_count == 0
2018-08-05 14:41:58 +00:00
2018-08-25 11:21:10 +00:00
def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2, markets) -> None:
2018-08-14 10:12:44 +00:00
"""
test check depth of market
"""
2018-09-10 18:19:28 +00:00
patch_exchange(mocker)
2018-08-25 11:21:10 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets),
2018-08-25 11:21:10 +00:00
get_order_book=order_book_l2
)
2018-08-05 14:41:58 +00:00
default_conf['telegram']['enabled'] = False
default_conf['exchange']['name'] = 'binance'
default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True
2018-08-07 10:29:37 +00:00
# delta is 100 which is impossible to reach. hence function will return false
default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 100
2018-08-05 14:41:58 +00:00
freqtrade = FreqtradeBot(default_conf)
conf = default_conf['bid_strategy']['check_depth_of_market']
2018-08-07 10:29:37 +00:00
assert freqtrade._check_depth_of_market_buy('ETH/BTC', conf) is False
2018-08-05 14:41:58 +00:00
def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order,
2018-08-14 10:12:44 +00:00
fee, markets, mocker, order_book_l2) -> None:
"""
test order book ask strategy
"""
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
2018-08-05 14:41:58 +00:00
default_conf['exchange']['name'] = 'binance'
default_conf['ask_strategy']['use_order_book'] = True
default_conf['ask_strategy']['order_book_min'] = 1
default_conf['ask_strategy']['order_book_max'] = 2
2018-08-05 14:41:58 +00:00
default_conf['telegram']['enabled'] = False
patch_RPCManager(mocker)
patch_exchange(mocker)
2018-08-05 14:41:58 +00:00
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_ticker=MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
'last': 0.00001172
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-08-05 14:41:58 +00:00
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.create_trade()
trade = Trade.query.first()
assert trade
2018-08-05 14:56:14 +00:00
time.sleep(0.01) # Race condition fix
trade.update(limit_buy_order)
assert trade.is_open is True
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
2018-08-29 17:32:44 +00:00
2019-03-16 12:32:26 +00:00
def test_get_sell_rate(default_conf, mocker, ticker, order_book_l2) -> None:
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_order_book=order_book_l2,
get_ticker=ticker,
)
pair = "ETH/BTC"
# Test regular mode
ft = get_patched_freqtradebot(mocker, default_conf)
rate = ft.get_sell_rate(pair, True)
assert isinstance(rate, float)
assert rate == 0.00001098
# Test orderbook mode
default_conf['ask_strategy']['use_order_book'] = True
default_conf['ask_strategy']['order_book_min'] = 1
default_conf['ask_strategy']['order_book_max'] = 2
ft = get_patched_freqtradebot(mocker, default_conf)
rate = ft.get_sell_rate(pair, True)
assert isinstance(rate, float)
assert rate == 0.043936
2019-03-16 12:32:26 +00:00
def test_startup_state(default_conf, mocker):
2018-12-04 19:23:03 +00:00
default_conf['pairlist'] = {'method': 'VolumePairList',
'config': {'number_assets': 20}
}
2018-12-02 21:12:12 +00:00
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
2019-03-26 08:07:24 +00:00
worker = get_patched_worker(mocker, default_conf)
assert worker.state is State.RUNNING
def test_startup_trade_reinit(default_conf, edge_conf, mocker):
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
reinit_mock = MagicMock()
mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', reinit_mock)
ftbot = get_patched_freqtradebot(mocker, default_conf)
ftbot.startup()
assert reinit_mock.call_count == 1
reinit_mock.reset_mock()
ftbot = get_patched_freqtradebot(mocker, edge_conf)
ftbot.startup()
assert reinit_mock.call_count == 0