Merge branch 'develop' into fix/backtest_toomanyopen

This commit is contained in:
Matthias
2018-11-24 10:38:30 +01:00
47 changed files with 2615 additions and 301 deletions

View File

@@ -4,6 +4,7 @@ import logging
from datetime import datetime
from functools import reduce
from typing import Dict, Optional
from collections import namedtuple
from unittest.mock import MagicMock, PropertyMock
import arrow
@@ -12,6 +13,7 @@ from telegram import Chat, Message, Update
from freqtrade.exchange.exchange_helpers import parse_ticker_dataframe
from freqtrade.exchange import Exchange
from freqtrade.edge import Edge
from freqtrade.freqtradebot import FreqtradeBot
logging.getLogger('').setLevel(logging.INFO)
@@ -28,6 +30,7 @@ def log_has(line, logs):
def patch_exchange(mocker, api_mock=None) -> None:
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock())
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value="Bittrex"))
mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value="bittrex"))
if api_mock:
@@ -42,7 +45,32 @@ def get_patched_exchange(mocker, config, api_mock=None) -> Exchange:
return exchange
def patch_edge(mocker) -> None:
# "ETH/BTC",
# "LTC/BTC",
# "XRP/BTC",
# "NEO/BTC"
pair_info = namedtuple(
'pair_info',
'stoploss, winrate, risk_reward_ratio, required_risk_reward, expectancy')
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'NEO/BTC': pair_info(-0.20, 0.66, 3.71, 0.50, 1.71),
'LTC/BTC': pair_info(-0.21, 0.66, 3.71, 0.50, 1.71),
}
))
mocker.patch('freqtrade.edge.Edge.stoploss', MagicMock(return_value=-0.20))
mocker.patch('freqtrade.edge.Edge.calculate', MagicMock(return_value=True))
def get_patched_edge(mocker, config) -> Edge:
patch_edge(mocker)
edge = Edge(config)
return edge
# Functions for recurrent object patching
def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
"""
This function patch _init_modules() to not call dependencies
@@ -752,3 +780,23 @@ def buy_order_fee():
'status': 'closed',
'fee': None
}
@pytest.fixture(scope="function")
def edge_conf(default_conf):
default_conf['edge'] = {
"enabled": True,
"process_throttle_secs": 1800,
"calculate_since_number_of_days": 14,
"allowed_risk": 0.01,
"stoploss_range_min": -0.01,
"stoploss_range_max": -0.1,
"stoploss_range_step": -0.01,
"maximum_winrate": 0.80,
"minimum_expectancy": 0.20,
"min_trade_number": 15,
"max_trade_duration_minute": 1440,
"remove_pumps": False
}
return default_conf

View File

View File

@@ -0,0 +1,310 @@
# pragma pylint: disable=missing-docstring, C0103, C0330
# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
import pytest
import logging
from freqtrade.tests.conftest import get_patched_freqtradebot
from freqtrade.edge import Edge, PairInfo
from pandas import DataFrame, to_datetime
from freqtrade.strategy.interface import SellType
from freqtrade.tests.optimize import (BTrade, BTContainer, _build_backtest_dataframe,
_get_frame_time_from_offset)
import arrow
import numpy as np
import math
from unittest.mock import MagicMock
# Cases to be tested:
# 1) Open trade should be removed from the end
# 2) Two complete trades within dataframe (with sell hit for all)
# 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss
# 4) Entered, sl 3%, candle drops 4%, recovers to 1% => Trade closed, 3% loss
# 5) Stoploss and sell are hit. should sell on stoploss
####################################################################
ticker_start_time = arrow.get(2018, 10, 3)
ticker_interval_in_minute = 60
_ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7}
# Open trade should be removed from the end
tc0 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 1]], # enter trade (signal on last candle)
stop_loss=-0.99, roi=float('inf'), profit_perc=0.00,
trades=[]
)
# Two complete trades within dataframe(with sell hit for all)
tc1 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 1], # enter trade (signal on last candle)
[2, 5000, 5025, 4975, 4987, 6172, 0, 0], # exit at open
[3, 5000, 5025, 4975, 4987, 6172, 1, 0], # no action
[4, 5000, 5025, 4975, 4987, 6172, 0, 0], # should enter the trade
[5, 5000, 5025, 4975, 4987, 6172, 0, 1], # no action
[6, 5000, 5025, 4975, 4987, 6172, 0, 0], # should sell
],
stop_loss=-0.99, roi=float('inf'), profit_perc=0.00,
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=2),
BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=4, close_tick=6)]
)
# 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss
tc2 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4600, 4987, 6172, 0, 0], # enter trade, stoploss hit
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.01, roi=float('inf'), profit_perc=-0.01,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
)
# 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss
tc3 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4800, 4987, 6172, 0, 0], # enter trade, stoploss hit
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.03, roi=float('inf'), profit_perc=-0.03,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
)
# 5) Stoploss and sell are hit. should sell on stoploss
tc4 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4800, 4987, 6172, 0, 1], # enter trade, stoploss hit, sell signal
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.03, roi=float('inf'), profit_perc=-0.03,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
)
TESTS = [
tc0,
tc1,
tc2,
tc3,
tc4
]
@pytest.mark.parametrize("data", TESTS)
def test_edge_results(edge_conf, mocker, caplog, data) -> None:
"""
run functional tests
"""
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
frame = _build_backtest_dataframe(data.data)
caplog.set_level(logging.DEBUG)
edge.fee = 0
trades = edge._find_trades_for_stoploss_range(frame, 'TEST/BTC', [data.stop_loss])
results = edge._fill_calculable_fields(DataFrame(trades)) if trades else DataFrame()
print(results)
assert len(trades) == len(data.trades)
if not results.empty:
assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3)
for c, trade in enumerate(data.trades):
res = results.iloc[c]
assert res.exit_type == trade.sell_reason
assert res.open_time == _get_frame_time_from_offset(trade.open_tick)
assert res.close_time == _get_frame_time_from_offset(trade.close_tick)
def test_adjust(mocker, default_conf):
freqtrade = get_patched_freqtradebot(mocker, default_conf)
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'E/F': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
'C/D': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
'N/O': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60)
}
))
pairs = ['A/B', 'C/D', 'E/F', 'G/H']
assert(edge.adjust(pairs) == ['E/F', 'C/D'])
def test_stoploss(mocker, default_conf):
freqtrade = get_patched_freqtradebot(mocker, default_conf)
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'E/F': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
'C/D': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
'N/O': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60)
}
))
assert edge.stoploss('E/F') == -0.01
def _validate_ohlc(buy_ohlc_sell_matrice):
for index, ohlc in enumerate(buy_ohlc_sell_matrice):
# if not high < open < low or not high < close < low
if not ohlc[3] >= ohlc[2] >= ohlc[4] or not ohlc[3] >= ohlc[5] >= ohlc[4]:
raise Exception('Line ' + str(index + 1) + ' of ohlc has invalid values!')
return True
def _build_dataframe(buy_ohlc_sell_matrice):
_validate_ohlc(buy_ohlc_sell_matrice)
tickers = []
for ohlc in buy_ohlc_sell_matrice:
ticker = {
'date': ticker_start_time.shift(
minutes=(
ohlc[0] *
ticker_interval_in_minute)).timestamp *
1000,
'buy': ohlc[1],
'open': ohlc[2],
'high': ohlc[3],
'low': ohlc[4],
'close': ohlc[5],
'sell': ohlc[6]}
tickers.append(ticker)
frame = DataFrame(tickers)
frame['date'] = to_datetime(frame['date'],
unit='ms',
utc=True,
infer_datetime_format=True)
return frame
def _time_on_candle(number):
return np.datetime64(ticker_start_time.shift(
minutes=(number * ticker_interval_in_minute)).timestamp * 1000, 'ms')
def test_edge_heartbeat_calculate(mocker, edge_conf):
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
heartbeat = edge_conf['edge']['process_throttle_secs']
# should not recalculate if heartbeat not reached
edge._last_updated = arrow.utcnow().timestamp - heartbeat + 1
assert edge.calculate() is False
def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=False,
timerange=None, exchange=None):
hz = 0.1
base = 0.001
ETHBTC = [
[
ticker_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000,
math.sin(x * hz) / 1000 + base,
math.sin(x * hz) / 1000 + base + 0.0001,
math.sin(x * hz) / 1000 + base - 0.0001,
math.sin(x * hz) / 1000 + base,
123.45
] for x in range(0, 500)]
hz = 0.2
base = 0.002
LTCBTC = [
[
ticker_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000,
math.sin(x * hz) / 1000 + base,
math.sin(x * hz) / 1000 + base + 0.0001,
math.sin(x * hz) / 1000 + base - 0.0001,
math.sin(x * hz) / 1000 + base,
123.45
] for x in range(0, 500)]
pairdata = {'NEO/BTC': ETHBTC, 'LTC/BTC': LTCBTC}
return pairdata
def test_edge_process_downloaded_data(mocker, default_conf):
default_conf['datadir'] = None
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.001))
mocker.patch('freqtrade.optimize.load_data', mocked_load_data)
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
assert edge.calculate()
assert len(edge._cached_pairs) == 2
assert edge._last_updated <= arrow.utcnow().timestamp + 2
def test_process_expectancy(mocker, edge_conf):
edge_conf['edge']['min_trade_number'] = 2
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
def get_fee():
return 0.001
freqtrade.exchange.get_fee = get_fee
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
trades = [
{'pair': 'TEST/BTC',
'stoploss': -0.9,
'profit_percent': '',
'profit_abs': '',
'open_time': np.datetime64('2018-10-03T00:05:00.000000000'),
'close_time': np.datetime64('2018-10-03T00:10:00.000000000'),
'open_index': 1,
'close_index': 1,
'trade_duration': '',
'open_rate': 17,
'close_rate': 17,
'exit_type': 'sell_signal'},
{'pair': 'TEST/BTC',
'stoploss': -0.9,
'profit_percent': '',
'profit_abs': '',
'open_time': np.datetime64('2018-10-03T00:20:00.000000000'),
'close_time': np.datetime64('2018-10-03T00:25:00.000000000'),
'open_index': 4,
'close_index': 4,
'trade_duration': '',
'open_rate': 20,
'close_rate': 20,
'exit_type': 'sell_signal'},
{'pair': 'TEST/BTC',
'stoploss': -0.9,
'profit_percent': '',
'profit_abs': '',
'open_time': np.datetime64('2018-10-03T00:30:00.000000000'),
'close_time': np.datetime64('2018-10-03T00:40:00.000000000'),
'open_index': 6,
'close_index': 7,
'trade_duration': '',
'open_rate': 26,
'close_rate': 34,
'exit_type': 'sell_signal'}
]
trades_df = DataFrame(trades)
trades_df = edge._fill_calculable_fields(trades_df)
final = edge._process_expectancy(trades_df)
assert len(final) == 1
assert 'TEST/BTC' in final
assert final['TEST/BTC'].stoploss == -0.9
assert round(final['TEST/BTC'].winrate, 10) == 0.3333333333
assert round(final['TEST/BTC'].risk_reward_ratio, 10) == 306.5384615384
assert round(final['TEST/BTC'].required_risk_reward, 10) == 2.0
assert round(final['TEST/BTC'].expectancy, 10) == 101.5128205128

View File

@@ -355,6 +355,36 @@ def test_validate_timeframes_not_in_config(default_conf, mocker):
Exchange(default_conf)
def test_validate_order_types(default_conf, mocker):
api_mock = MagicMock()
type(api_mock).has = PropertyMock(return_value={'createMarketOrder': True})
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
default_conf['order_types'] = {'buy': 'limit', 'sell': 'limit', 'stoploss': 'market'}
Exchange(default_conf)
type(api_mock).has = PropertyMock(return_value={'createMarketOrder': False})
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
default_conf['order_types'] = {'buy': 'limit', 'sell': 'limit', 'stoploss': 'market'}
with pytest.raises(OperationalException,
match=r'Exchange .* does not support market orders.'):
Exchange(default_conf)
def test_validate_order_types_not_in_config(default_conf, mocker):
api_mock = MagicMock()
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
conf = copy.deepcopy(default_conf)
Exchange(conf)
def test_exchange_has(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf)
assert not exchange.exchange_has('ASDFASDF')
@@ -373,7 +403,7 @@ def test_buy_dry_run(default_conf, mocker):
default_conf['dry_run'] = True
exchange = get_patched_exchange(mocker, default_conf)
order = exchange.buy(pair='ETH/BTC', rate=200, amount=1)
order = exchange.buy(pair='ETH/BTC', ordertype='limit', amount=1, rate=200)
assert 'id' in order
assert 'dry_run_buy_' in order['id']
@@ -381,47 +411,64 @@ def test_buy_dry_run(default_conf, mocker):
def test_buy_prod(default_conf, mocker):
api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
api_mock.create_limit_buy_order = MagicMock(return_value={
order_type = 'market'
api_mock.create_order = MagicMock(return_value={
'id': order_id,
'info': {
'foo': 'bar'
}
})
default_conf['dry_run'] = False
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)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
order = exchange.buy(pair='ETH/BTC', rate=200, amount=1)
order = exchange.buy(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'buy'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] is None
api_mock.create_order.reset_mock()
order_type = 'limit'
order = exchange.buy(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'buy'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] == 200
# test exception handling
with pytest.raises(DependencyException):
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InsufficientFunds)
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
exchange.buy(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
with pytest.raises(DependencyException):
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InvalidOrder)
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
exchange.buy(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
with pytest.raises(TemporaryError):
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.NetworkError)
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
exchange.buy(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
with pytest.raises(OperationalException):
api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.BaseError)
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.buy(pair='ETH/BTC', rate=200, amount=1)
exchange.buy(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
def test_sell_dry_run(default_conf, mocker):
default_conf['dry_run'] = True
exchange = get_patched_exchange(mocker, default_conf)
order = exchange.sell(pair='ETH/BTC', rate=200, amount=1)
order = exchange.sell(pair='ETH/BTC', ordertype='limit', amount=1, rate=200)
assert 'id' in order
assert 'dry_run_sell_' in order['id']
@@ -429,7 +476,8 @@ def test_sell_dry_run(default_conf, mocker):
def test_sell_prod(default_conf, mocker):
api_mock = MagicMock()
order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6))
api_mock.create_limit_sell_order = MagicMock(return_value={
order_type = 'market'
api_mock.create_order = MagicMock(return_value={
'id': order_id,
'info': {
'foo': 'bar'
@@ -438,32 +486,48 @@ def test_sell_prod(default_conf, mocker):
default_conf['dry_run'] = False
exchange = get_patched_exchange(mocker, default_conf, api_mock)
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)
order = exchange.sell(pair='ETH/BTC', rate=200, amount=1)
order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'sell'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] is None
api_mock.create_order.reset_mock()
order_type = 'limit'
order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'sell'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] == 200
# test exception handling
with pytest.raises(DependencyException):
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InsufficientFunds)
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
with pytest.raises(DependencyException):
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InvalidOrder)
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
with pytest.raises(TemporaryError):
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.NetworkError)
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
with pytest.raises(OperationalException):
api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.BaseError)
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.sell(pair='ETH/BTC', rate=200, amount=1)
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
def test_get_balance_dry_run(default_conf, mocker):

View File

@@ -31,8 +31,8 @@ class BTContainer(NamedTuple):
def _get_frame_time_from_offset(offset):
return ticker_start_time.shift(
minutes=(offset * TICKER_INTERVAL_MINUTES[tests_ticker_interval])).datetime
return ticker_start_time.shift(minutes=(offset * TICKER_INTERVAL_MINUTES[tests_ticker_interval])
).datetime.replace(tzinfo=None)
def _build_backtest_dataframe(ticker_with_signals):

View File

@@ -1,4 +1,4 @@
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, C0330, unused-argument
import logging
from unittest.mock import MagicMock
@@ -35,15 +35,15 @@ tc0 = BTContainer(data=[
# TC2: Stop-Loss Triggered 3% Loss
tc1 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
[2, 4987, 5012, 4962, 4975, 6172, 0, 0],
[3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
[2, 4987, 5012, 4962, 4975, 6172, 0, 0],
[3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.03, roi=1, profit_perc=-0.03,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)]
)
)
# Test 3 Candle drops 4%, Recovers 1%.
@@ -128,7 +128,7 @@ tc6 = BTContainer(data=[
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi=0.03, profit_perc=0.03,
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
)
)
TESTS = [
tc0,

View File

@@ -638,6 +638,7 @@ def test_backtest_only_sell(mocker, default_conf):
def test_backtest_alternate_buy_sell(default_conf, fee, mocker):
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch('freqtrade.optimize.backtesting.file_dump_json', MagicMock())
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC')
# We need to enable sell-signal - otherwise it sells on ROI!!
default_conf['experimental'] = {"use_sell_signal": True}

View File

@@ -0,0 +1,131 @@
# pragma pylint: disable=missing-docstring, C0103, C0330
# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
from unittest.mock import MagicMock
import json
from typing import List
from freqtrade.edge import PairInfo
from freqtrade.arguments import Arguments
from freqtrade.optimize.edge_cli import (EdgeCli, setup_configuration, start)
from freqtrade.tests.conftest import log_has, patch_exchange
def get_args(args) -> List[str]:
return Arguments(args, '').get_parsed_arg()
def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'edge'
]
config = setup_configuration(get_args(args))
assert 'max_open_trades' in config
assert 'stake_currency' in config
assert 'stake_amount' in config
assert 'exchange' in config
assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config
assert log_has(
'Using data folder: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config
assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples)
assert 'refresh_pairs' not in config
assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)
assert 'timerange' not in config
assert 'stoploss_range' not in config
def test_setup_configuration_with_arguments(mocker, edge_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(edge_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'--datadir', '/foo/bar',
'edge',
'--ticker-interval', '1m',
'--refresh-pairs-cached',
'--timerange', ':100',
'--stoplosses=-0.01,-0.10,-0.001'
]
config = setup_configuration(get_args(args))
assert 'max_open_trades' in config
assert 'stake_currency' in config
assert 'stake_amount' in config
assert 'exchange' in config
assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config
assert log_has(
'Using data folder: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples)
assert log_has(
'Using ticker_interval: 1m ...',
caplog.record_tuples
)
assert 'refresh_pairs' in config
assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)
assert 'timerange' in config
assert log_has(
'Parameter --timerange detected: {} ...'.format(config['timerange']),
caplog.record_tuples
)
def test_start(mocker, fee, edge_conf, caplog) -> None:
start_mock = MagicMock()
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.edge_cli.EdgeCli.start', start_mock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(edge_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'edge'
]
args = get_args(args)
start(args)
assert log_has(
'Starting freqtrade in Edge mode',
caplog.record_tuples
)
assert start_mock.call_count == 1
def test_edge_init(mocker, edge_conf) -> None:
patch_exchange(mocker)
edge_cli = EdgeCli(edge_conf)
assert edge_cli.config == edge_conf
assert callable(edge_cli.edge.calculate)
def test_generate_edge_table(edge_conf, mocker):
patch_exchange(mocker)
edge_cli = EdgeCli(edge_conf)
results = {}
results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
assert edge_cli._generate_edge_table(results).count(':|') == 7
assert edge_cli._generate_edge_table(results).count('| ETH/BTC |') == 1
assert edge_cli._generate_edge_table(results).count(
'| risk reward ratio | required risk reward | expectancy |') == 1

View File

@@ -176,7 +176,7 @@ def test_roi_table_generation(hyperopt) -> None:
'roi_p3': 3,
}
assert hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0}
assert hyperopt.custom_hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0}
def test_start_calls_optimizer(mocker, default_conf, caplog) -> None:
@@ -244,7 +244,8 @@ def test_populate_indicators(hyperopt) -> None:
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
tickerlist = {'UNITTEST/BTC': tick}
dataframes = hyperopt.strategy.tickerdata_to_dataframe(tickerlist)
dataframe = hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], {'pair': 'UNITTEST/BTC'})
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'})
# Check if some indicators are generated. We will not test all of them
assert 'adx' in dataframe
@@ -256,9 +257,10 @@ def test_buy_strategy_generator(hyperopt) -> None:
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
tickerlist = {'UNITTEST/BTC': tick}
dataframes = hyperopt.strategy.tickerdata_to_dataframe(tickerlist)
dataframe = hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], {'pair': 'UNITTEST/BTC'})
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'})
populate_buy_trend = hyperopt.buy_strategy_generator(
populate_buy_trend = hyperopt.custom_hyperopt.buy_strategy_generator(
{
'adx-value': 20,
'fastd-value': 20,

View File

@@ -645,3 +645,28 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None:
pair = 'ETH/BTC'
with pytest.raises(RPCException, match=r'Forcebuy not enabled.'):
rpc._rpc_forcebuy(pair, None)
def test_rpc_whitelist(mocker, default_conf) -> None:
patch_coinmarketcap(mocker)
patch_exchange(mocker)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
freqtradebot = FreqtradeBot(default_conf)
rpc = RPC(freqtradebot)
ret = rpc._rpc_whitelist()
assert ret['method'] == 'static'
assert ret['whitelist'] == default_conf['exchange']['pair_whitelist']
def test_rpc_whitelist_dynamic(mocker, default_conf) -> None:
patch_coinmarketcap(mocker)
patch_exchange(mocker)
default_conf['dynamic_whitelist'] = 4
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
freqtradebot = FreqtradeBot(default_conf)
rpc = RPC(freqtradebot)
ret = rpc._rpc_whitelist()
assert ret['method'] == 4
assert ret['whitelist'] == default_conf['exchange']['pair_whitelist']

View File

@@ -72,7 +72,8 @@ def test_init(default_conf, mocker, caplog) -> None:
message_str = "rpc.telegram is listening for following commands: [['status'], ['profit'], " \
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], " \
"['performance'], ['daily'], ['count'], ['reload_conf'], ['help'], ['version']]"
"['performance'], ['daily'], ['count'], ['reload_conf'], " \
"['whitelist'], ['help'], ['version']]"
assert log_has(message_str, caplog.record_tuples)
@@ -1006,6 +1007,43 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non
assert msg in msg_mock.call_args_list[0][0][0]
def test_whitelist_static(default_conf, update, mocker) -> None:
patch_coinmarketcap(mocker)
msg_mock = MagicMock()
mocker.patch.multiple(
'freqtrade.rpc.telegram.Telegram',
_init=MagicMock(),
_send_msg=msg_mock
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
telegram = Telegram(freqtradebot)
telegram._whitelist(bot=MagicMock(), update=update)
assert msg_mock.call_count == 1
assert ('Using static whitelist with `4` pairs \n`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`'
in msg_mock.call_args_list[0][0][0])
def test_whitelist_dynamic(default_conf, update, mocker) -> None:
patch_coinmarketcap(mocker)
msg_mock = MagicMock()
mocker.patch.multiple(
'freqtrade.rpc.telegram.Telegram',
_init=MagicMock(),
_send_msg=msg_mock
)
default_conf['dynamic_whitelist'] = 4
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
telegram = Telegram(freqtradebot)
telegram._whitelist(bot=MagicMock(), update=update)
assert msg_mock.call_count == 1
assert ('Dynamic whitelist with `4` pairs\n`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`'
in msg_mock.call_args_list[0][0][0])
def test_help_handle(default_conf, update, mocker) -> None:
patch_coinmarketcap(mocker)
msg_mock = MagicMock()

View File

@@ -88,8 +88,8 @@ def test_load_strategy_invalid_directory(result, caplog):
def test_load_not_found_strategy():
strategy = StrategyResolver()
with pytest.raises(ImportError,
match=r'Impossible to load Strategy \'NotFoundStrategy\'.'
r' This class does not exist or contains Python code errors'):
match=r"Impossible to load Strategy 'NotFoundStrategy'."
r" This class does not exist or contains Python code errors"):
strategy._load_strategy(strategy_name='NotFoundStrategy', config={})
@@ -182,6 +182,42 @@ def test_strategy_override_process_only_new_candles(caplog):
) in caplog.record_tuples
def test_strategy_override_order_types(caplog):
caplog.set_level(logging.INFO)
order_types = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit'
}
config = {
'strategy': 'DefaultStrategy',
'order_types': order_types
}
resolver = StrategyResolver(config)
assert resolver.strategy.order_types
for method in ['buy', 'sell', 'stoploss']:
assert resolver.strategy.order_types[method] == order_types[method]
assert ('freqtrade.strategy.resolver',
logging.INFO,
"Override strategy 'order_types' with value in config file:"
" {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'}."
) in caplog.record_tuples
config = {
'strategy': 'DefaultStrategy',
'order_types': {'buy': 'market'}
}
# Raise error for invalid configuration
with pytest.raises(ImportError,
match=r"Impossible to load Strategy 'DefaultStrategy'. "
r"Order-types mapping is incomplete."):
StrategyResolver(config)
def test_deprecate_populate_indicators(result):
default_location = path.join(path.dirname(path.realpath(__file__)))
resolver = StrategyResolver({'strategy': 'TestStrategyLegacy',

View File

@@ -64,6 +64,22 @@ def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None:
assert log_has('Validating configuration ...', caplog.record_tuples)
def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None:
default_conf['max_open_trades'] = -1
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = Arguments([], '').get_parsed_arg()
configuration = Configuration(args)
validated_conf = configuration.load_config()
print(validated_conf)
assert validated_conf['max_open_trades'] > 999999999
assert validated_conf['max_open_trades'] == float('inf')
assert log_has('Validating configuration ...', caplog.record_tuples)
def test_load_config_file_exception(mocker) -> None:
mocker.patch(
'freqtrade.configuration.open',

View File

@@ -18,7 +18,7 @@ from freqtrade.persistence import Trade
from freqtrade.rpc import RPCMessageType
from freqtrade.state import State
from freqtrade.strategy.interface import SellType, SellCheckTuple
from freqtrade.tests.conftest import log_has, patch_exchange
from freqtrade.tests.conftest import log_has, patch_exchange, patch_edge
# Functions for recurrent object patching
@@ -177,7 +177,7 @@ def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mock
freqtrade = FreqtradeBot(default_conf)
result = freqtrade._get_trade_stake_amount()
result = freqtrade._get_trade_stake_amount('ETH/BTC')
assert result == default_conf['stake_amount']
@@ -195,7 +195,7 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf,
freqtrade = FreqtradeBot(default_conf)
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
freqtrade._get_trade_stake_amount()
freqtrade._get_trade_stake_amount('ETH/BTC')
def test_get_trade_stake_amount_unlimited_amount(default_conf,
@@ -224,28 +224,131 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf,
patch_get_signal(freqtrade)
# no open trades, order amount should be 'balance / max_open_trades'
result = freqtrade._get_trade_stake_amount()
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()
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
freqtrade.create_trade()
result = freqtrade._get_trade_stake_amount()
result = freqtrade._get_trade_stake_amount('XRP/BTC')
assert result is None
# set max_open_trades = None, so do not trade
conf['max_open_trades'] = 0
freqtrade = FreqtradeBot(conf)
result = freqtrade._get_trade_stake_amount()
result = freqtrade._get_trade_stake_amount('NEO/BTC')
assert result is None
def test_edge_called_in_process(mocker, edge_conf) -> None:
patch_RPCManager(mocker)
patch_edge(mocker)
def _refresh_whitelist(list):
return ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC']
patch_exchange(mocker)
freqtrade = FreqtradeBot(edge_conf)
freqtrade._refresh_whitelist = _refresh_whitelist
patch_get_signal(freqtrade)
freqtrade._process()
assert freqtrade.active_pair_whitelist == ['NEO/BTC', 'LTC/BTC']
def test_edge_overrides_stake_amount(mocker, edge_conf) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
patch_edge(mocker)
freqtrade = FreqtradeBot(edge_conf)
assert freqtrade._get_trade_stake_amount('NEO/BTC') == (0.001 * 0.01) / 0.20
assert freqtrade._get_trade_stake_amount('LTC/BTC') == (0.001 * 0.01) / 0.20
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,
get_markets=markets,
)
#############################################
# Create a trade with "limit_buy_order" price
freqtrade = FreqtradeBot(edge_conf)
freqtrade.active_pair_whitelist = ['NEO/BTC']
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
#############################################
# stoploss shoud be hit
assert freqtrade.handle_trade(trade) is True
assert log_has('executed sell, reason: SellType.STOP_LOSS', caplog.record_tuples)
assert trade.sell_reason == SellType.STOP_LOSS.value
def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee, markets,
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,
get_markets=markets,
)
#############################################
# Create a trade with "limit_buy_order" price
freqtrade = FreqtradeBot(edge_conf)
freqtrade.active_pair_whitelist = ['NEO/BTC']
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
freqtrade.create_trade()
trade = Trade.query.first()
trade.update(limit_buy_order)
#############################################
# stoploss shoud not be hit
assert freqtrade.handle_trade(trade) is False
def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
@@ -450,7 +553,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
patch_get_signal(freqtrade)
freqtrade.create_trade()
rate, amount = buy_mock.call_args[0][1], buy_mock.call_args[0][2]
rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount']
assert rate * amount >= default_conf['stake_amount']
@@ -494,7 +597,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order,
patch_get_signal(freqtrade)
assert freqtrade.create_trade() is False
assert freqtrade._get_trade_stake_amount() is None
assert freqtrade._get_trade_stake_amount('ETH/BTC') is None
def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, markets, mocker) -> None:
@@ -593,7 +696,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
assert trade.amount == 90.99181073703367
assert log_has(
'Checking buy signals to create a new trade with stake_amount: 0.001000 ...',
'Buy signal found: about create a new trade with stake_amount: 0.001000 ...',
caplog.record_tuples
)
@@ -760,10 +863,10 @@ def test_execute_buy(mocker, default_conf, fee, markets, limit_buy_order) -> Non
assert freqtrade.execute_buy(pair, stake_amount)
assert get_bid.call_count == 1
assert buy_mm.call_count == 1
call_args = buy_mm.call_args_list[0][0]
assert call_args[0] == pair
assert call_args[1] == bid
assert call_args[2] == stake_amount / bid
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
# Test calling with price
fix_price = 0.06
@@ -772,10 +875,10 @@ def test_execute_buy(mocker, default_conf, fee, markets, limit_buy_order) -> Non
assert get_bid.call_count == 1
assert buy_mm.call_count == 2
call_args = buy_mm.call_args_list[1][0]
assert call_args[0] == pair
assert call_args[1] == fix_price
assert call_args[2] == stake_amount / fix_price
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
def test_process_maybe_execute_buy(mocker, default_conf) -> None:
@@ -1547,7 +1650,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, market
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.strategy.stop_loss_reached = \
lambda current_rate, trade, current_time, current_profit: SellCheckTuple(
lambda current_rate, trade, current_time, force_stoploss, current_profit: SellCheckTuple(
sell_flag=False, sell_type=SellType.NONE)
freqtrade.create_trade()
@@ -1821,7 +1924,7 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, ca
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
@@ -2097,9 +2200,9 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets)
"""
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_markets=markets,
get_order_book=order_book_l2
'freqtrade.exchange.Exchange',
get_markets=markets,
get_order_book=order_book_l2
)
default_conf['exchange']['name'] = 'binance'
default_conf['bid_strategy']['use_order_book'] = True

View File

@@ -0,0 +1,84 @@
# pragma pylint: disable=missing-docstring
from freqtrade.tests.conftest import get_patched_freqtradebot
from unittest.mock import MagicMock
def test_sync_wallet_at_boot(mocker, default_conf):
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_balances=MagicMock(return_value={
"BNT": {
"free": 1.0,
"used": 2.0,
"total": 3.0
},
"GAS": {
"free": 0.260739,
"used": 0.0,
"total": 0.260739
},
})
)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
assert len(freqtrade.wallets.wallets) == 2
assert freqtrade.wallets.wallets['BNT'].free == 1.0
assert freqtrade.wallets.wallets['BNT'].used == 2.0
assert freqtrade.wallets.wallets['BNT'].total == 3.0
assert freqtrade.wallets.wallets['GAS'].free == 0.260739
assert freqtrade.wallets.wallets['GAS'].used == 0.0
assert freqtrade.wallets.wallets['GAS'].total == 0.260739
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_balances=MagicMock(return_value={
"BNT": {
"free": 1.2,
"used": 1.9,
"total": 3.5
},
"GAS": {
"free": 0.270739,
"used": 0.1,
"total": 0.260439
},
})
)
freqtrade.wallets.update()
assert len(freqtrade.wallets.wallets) == 2
assert freqtrade.wallets.wallets['BNT'].free == 1.2
assert freqtrade.wallets.wallets['BNT'].used == 1.9
assert freqtrade.wallets.wallets['BNT'].total == 3.5
assert freqtrade.wallets.wallets['GAS'].free == 0.270739
assert freqtrade.wallets.wallets['GAS'].used == 0.1
assert freqtrade.wallets.wallets['GAS'].total == 0.260439
def test_sync_wallet_missing_data(mocker, default_conf):
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_balances=MagicMock(return_value={
"BNT": {
"free": 1.0,
"used": 2.0,
"total": 3.0
},
"GAS": {
"free": 0.260739,
"total": 0.260739
},
})
)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
assert len(freqtrade.wallets.wallets) == 2
assert freqtrade.wallets.wallets['BNT'].free == 1.0
assert freqtrade.wallets.wallets['BNT'].used == 2.0
assert freqtrade.wallets.wallets['BNT'].total == 3.0
assert freqtrade.wallets.wallets['GAS'].free == 0.260739
assert freqtrade.wallets.wallets['GAS'].used is None
assert freqtrade.wallets.wallets['GAS'].total == 0.260739