Merge branch 'develop' into feat/short
This commit is contained in:
@@ -17,7 +17,7 @@ from telegram import Chat, Message, Update
|
||||
from freqtrade import constants
|
||||
from freqtrade.commands import Arguments
|
||||
from freqtrade.data.converter import ohlcv_to_dataframe
|
||||
from freqtrade.edge import Edge, PairInfo
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.enums import Collateral, RunMode, TradingMode
|
||||
from freqtrade.enums.signaltype import SignalDirection
|
||||
from freqtrade.exchange import Exchange
|
||||
@@ -163,11 +163,6 @@ def patch_edge(mocker) -> None:
|
||||
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
|
||||
|
||||
|
||||
@@ -2370,6 +2365,46 @@ def market_buy_order_usdt():
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def market_buy_order_usdt_doublefee(market_buy_order_usdt):
|
||||
order = deepcopy(market_buy_order_usdt)
|
||||
order['fee'] = None
|
||||
# Market orders filled with 2 trades can have fees in different currencies
|
||||
# assuming the account runs out of BNB.
|
||||
order['fees'] = [
|
||||
{'cost': 0.00025125, 'currency': 'BNB'},
|
||||
{'cost': 0.05030681, 'currency': 'USDT'},
|
||||
]
|
||||
order['trades'] = [{
|
||||
'timestamp': None,
|
||||
'datetime': None,
|
||||
'symbol': 'ETH/USDT',
|
||||
'id': None,
|
||||
'order': '123',
|
||||
'type': 'market',
|
||||
'side': 'sell',
|
||||
'takerOrMaker': None,
|
||||
'price': 2.01,
|
||||
'amount': 25.0,
|
||||
'cost': 50.25,
|
||||
'fee': {'cost': 0.00025125, 'currency': 'BNB'}
|
||||
}, {
|
||||
'timestamp': None,
|
||||
'datetime': None,
|
||||
'symbol': 'ETH/USDT',
|
||||
'id': None,
|
||||
'order': '123',
|
||||
'type': 'market',
|
||||
'side': 'sell',
|
||||
'takerOrMaker': None,
|
||||
'price': 2.0,
|
||||
'amount': 5,
|
||||
'cost': 10,
|
||||
'fee': {'cost': 0.0100306, 'currency': 'USDT'}
|
||||
}]
|
||||
return order
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def market_sell_order_usdt():
|
||||
return {
|
||||
|
@@ -360,13 +360,16 @@ async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog):
|
||||
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
|
||||
|
||||
pair = 'ETH/BTC'
|
||||
res = await exchange._async_get_historic_ohlcv(pair, "5m",
|
||||
1500000000000, is_new_pair=False)
|
||||
respair, restf, res = await exchange._async_get_historic_ohlcv(
|
||||
pair, "5m", 1500000000000, is_new_pair=False)
|
||||
assert respair == pair
|
||||
assert restf == '5m'
|
||||
# Call with very old timestamp - causes tons of requests
|
||||
assert exchange._api_async.fetch_ohlcv.call_count > 400
|
||||
# assert res == ohlcv
|
||||
exchange._api_async.fetch_ohlcv.reset_mock()
|
||||
res = await exchange._async_get_historic_ohlcv(pair, "5m", 1500000000000, is_new_pair=True)
|
||||
_, _, res = await exchange._async_get_historic_ohlcv(
|
||||
pair, "5m", 1500000000000, is_new_pair=True)
|
||||
|
||||
# Called twice - one "init" call - and one to get the actual data.
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
||||
@@ -375,7 +378,7 @@ async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("trading_mode,collateral,config", [
|
||||
("", "", {}),
|
||||
("spot", "", {}),
|
||||
("margin", "cross", {"options": {"defaultType": "margin"}}),
|
||||
("futures", "isolated", {"options": {"defaultType": "future"}}),
|
||||
])
|
||||
|
@@ -240,9 +240,9 @@ def test_validate_order_time_in_force(default_conf, mocker, caplog):
|
||||
(2.9999, 4, 0.005, 2.995),
|
||||
])
|
||||
def test_amount_to_precision(default_conf, mocker, amount, precision_mode, precision, expected):
|
||||
'''
|
||||
"""
|
||||
Test rounds down
|
||||
'''
|
||||
"""
|
||||
|
||||
markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'amount': precision}}})
|
||||
|
||||
@@ -281,9 +281,7 @@ def test_amount_to_precision(default_conf, mocker, amount, precision_mode, preci
|
||||
|
||||
])
|
||||
def test_price_to_precision(default_conf, mocker, price, precision_mode, precision, expected):
|
||||
'''
|
||||
Test price to precision
|
||||
'''
|
||||
"""Test price to precision"""
|
||||
markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'price': precision}}})
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||
@@ -967,9 +965,22 @@ def test_validate_required_startup_candles(default_conf, mocker, caplog):
|
||||
default_conf['startup_candle_count'] = 20
|
||||
ex = Exchange(default_conf)
|
||||
assert ex
|
||||
default_conf['startup_candle_count'] = 600
|
||||
# assumption is that the exchange provides 500 candles per call.s
|
||||
assert ex.validate_required_startup_candles(200, '5m') == 1
|
||||
assert ex.validate_required_startup_candles(499, '5m') == 1
|
||||
assert ex.validate_required_startup_candles(600, '5m') == 2
|
||||
assert ex.validate_required_startup_candles(501, '5m') == 2
|
||||
assert ex.validate_required_startup_candles(499, '5m') == 1
|
||||
assert ex.validate_required_startup_candles(1000, '5m') == 3
|
||||
assert ex.validate_required_startup_candles(2499, '5m') == 5
|
||||
assert log_has_re(r'Using 5 calls to get OHLCV. This.*', caplog)
|
||||
|
||||
with pytest.raises(OperationalException, match=r'This strategy requires 600.*'):
|
||||
with pytest.raises(OperationalException, match=r'This strategy requires 2500.*'):
|
||||
ex.validate_required_startup_candles(2500, '5m')
|
||||
|
||||
# Ensure the same also happens on init
|
||||
default_conf['startup_candle_count'] = 6000
|
||||
with pytest.raises(OperationalException, match=r'This strategy requires 6000.*'):
|
||||
Exchange(default_conf)
|
||||
|
||||
|
||||
@@ -1570,6 +1581,7 @@ def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name):
|
||||
assert exchange._async_get_candle_history.call_count == 2
|
||||
# Returns twice the above OHLCV data
|
||||
assert len(ret) == 2
|
||||
assert log_has_re(r'Downloaded data for .* with length .*\.', caplog)
|
||||
|
||||
caplog.clear()
|
||||
|
||||
@@ -1651,12 +1663,13 @@ async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_
|
||||
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
|
||||
|
||||
pair = 'ETH/USDT'
|
||||
res = await exchange._async_get_historic_ohlcv(pair, "5m",
|
||||
1500000000000, is_new_pair=False)
|
||||
respair, restf, res = await exchange._async_get_historic_ohlcv(
|
||||
pair, "5m", 1500000000000, is_new_pair=False)
|
||||
assert respair == pair
|
||||
assert restf == '5m'
|
||||
# Call with very old timestamp - causes tons of requests
|
||||
assert exchange._api_async.fetch_ohlcv.call_count > 200
|
||||
assert res[0] == ohlcv[0]
|
||||
assert log_has_re(r'Downloaded data for .* with length .*\.', caplog)
|
||||
|
||||
|
||||
def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
|
||||
@@ -1694,12 +1707,14 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
||||
exchange._api_async.fetch_ohlcv.reset_mock()
|
||||
|
||||
exchange.required_candle_call_count = 2
|
||||
res = exchange.refresh_latest_ohlcv(pairs)
|
||||
assert len(res) == len(pairs)
|
||||
|
||||
assert log_has(f'Refreshing candle (OHLCV) data for {len(pairs)} pairs', caplog)
|
||||
assert exchange._klines
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 4
|
||||
exchange._api_async.fetch_ohlcv.reset_mock()
|
||||
for pair in pairs:
|
||||
assert isinstance(exchange.klines(pair), DataFrame)
|
||||
assert len(exchange.klines(pair)) > 0
|
||||
@@ -1715,7 +1730,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
|
||||
res = exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')])
|
||||
assert len(res) == len(pairs)
|
||||
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 0
|
||||
assert log_has(f"Using cached candle (OHLCV) data for pair {pairs[0][0]}, "
|
||||
f"timeframe {pairs[0][1]} ...",
|
||||
caplog)
|
||||
@@ -2066,15 +2081,6 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog):
|
||||
assert exchange.get_rate(pair, refresh=True, side="sell") == 0.13
|
||||
|
||||
|
||||
def make_fetch_ohlcv_mock(data):
|
||||
def fetch_ohlcv_mock(pair, timeframe, since):
|
||||
if since:
|
||||
assert since > data[-1][0]
|
||||
return []
|
||||
return data
|
||||
return fetch_ohlcv_mock
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
@pytest.mark.asyncio
|
||||
async def test___async_get_candle_history_sort(default_conf, mocker, exchange_name):
|
||||
|
@@ -427,6 +427,7 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
|
||||
return_value=(Arrow(2017, 12, 10), Arrow(2017, 12, 13)))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.object(Path, 'open')
|
||||
mocker.patch('freqtrade.configuration.config_validation.validate_config_schema')
|
||||
mocker.patch('freqtrade.optimize.hyperopt.load', return_value={'XRP/BTC': None})
|
||||
|
||||
optimizer_param = {
|
||||
|
@@ -1018,7 +1018,7 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
assert len(res) == 1
|
||||
assert res[0]['mix_tag'] == 'Other Other'
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
||||
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||
|
||||
trade.buy_tag = "TESTBUY"
|
||||
trade.sell_reason = "TESTSELL"
|
||||
@@ -1027,7 +1027,7 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
assert len(res) == 1
|
||||
assert res[0]['mix_tag'] == 'TESTBUY TESTSELL'
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
||||
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||
|
||||
|
||||
def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
||||
@@ -1046,10 +1046,10 @@ def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
||||
assert len(res) == 2
|
||||
assert res[0]['mix_tag'] == 'TEST1 sell_signal'
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit'], 0.5)
|
||||
assert prec_satoshi(res[0]['profit_pct'], 0.5)
|
||||
assert res[1]['mix_tag'] == 'Other roi'
|
||||
assert res[1]['count'] == 1
|
||||
assert prec_satoshi(res[1]['profit'], 1.0)
|
||||
assert prec_satoshi(res[1]['profit_pct'], 1.0)
|
||||
|
||||
# Test for a specific pair
|
||||
res = rpc._rpc_mix_tag_performance('ETC/BTC')
|
||||
@@ -1057,7 +1057,7 @@ def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
||||
assert len(res) == 1
|
||||
assert res[0]['count'] == 1
|
||||
assert res[0]['mix_tag'] == 'TEST1 sell_signal'
|
||||
assert prec_satoshi(res[0]['profit'], 0.5)
|
||||
assert prec_satoshi(res[0]['profit_pct'], 0.5)
|
||||
|
||||
|
||||
def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
|
||||
|
@@ -521,7 +521,7 @@ def test_api_locks(botclient):
|
||||
assert rc.json()['lock_count'] == 0
|
||||
|
||||
|
||||
def test_api_show_config(botclient, mocker):
|
||||
def test_api_show_config(botclient):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot)
|
||||
|
||||
@@ -537,6 +537,8 @@ def test_api_show_config(botclient, mocker):
|
||||
assert not rc.json()['trailing_stop']
|
||||
assert 'bid_strategy' in rc.json()
|
||||
assert 'ask_strategy' in rc.json()
|
||||
assert 'unfilledtimeout' in rc.json()
|
||||
assert 'version' in rc.json()
|
||||
|
||||
|
||||
def test_api_daily(botclient, mocker, ticker, fee, markets):
|
||||
@@ -704,7 +706,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
||||
'is_short,expected',
|
||||
[(
|
||||
True,
|
||||
{'best_pair': 'ETC/BTC', 'best_rate': -0.5, 'profit_all_coin': 43.61269123,
|
||||
{'best_pair': 'ETC/BTC', 'best_rate': -0.5, 'best_pair_profit_ratio': -0.005,
|
||||
'profit_all_coin': 43.61269123,
|
||||
'profit_all_fiat': 538398.67323435, 'profit_all_percent_mean': 66.41,
|
||||
'profit_all_ratio_mean': 0.664109545, 'profit_all_percent_sum': 398.47,
|
||||
'profit_all_ratio_sum': 3.98465727, 'profit_all_percent': 4.36,
|
||||
@@ -716,7 +719,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
||||
),
|
||||
(
|
||||
False,
|
||||
{'best_pair': 'XRP/BTC', 'best_rate': 1.0, 'profit_all_coin': -44.0631579,
|
||||
{'best_pair': 'XRP/BTC', 'best_rate': 1.0, 'best_pair_profit_ratio': 0.01,
|
||||
'profit_all_coin': -44.0631579,
|
||||
'profit_all_fiat': -543959.6842755, 'profit_all_percent_mean': -66.41,
|
||||
'profit_all_ratio_mean': -0.6641100666666667, 'profit_all_percent_sum': -398.47,
|
||||
'profit_all_ratio_sum': -3.9846604, 'profit_all_percent': -4.41,
|
||||
@@ -728,7 +732,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
||||
),
|
||||
(
|
||||
None,
|
||||
{'best_pair': 'XRP/BTC', 'best_rate': 1.0, 'profit_all_coin': -14.43790415,
|
||||
{'best_pair': 'XRP/BTC', 'best_rate': 1.0, 'best_pair_profit_ratio': 0.01,
|
||||
'profit_all_coin': -14.43790415,
|
||||
'profit_all_fiat': -178235.92673175, 'profit_all_percent_mean': 0.08,
|
||||
'profit_all_ratio_mean': 0.000835751666666662, 'profit_all_percent_sum': 0.5,
|
||||
'profit_all_ratio_sum': 0.005014509999999972, 'profit_all_percent': -1.44,
|
||||
@@ -763,6 +768,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected)
|
||||
assert rc.json() == {
|
||||
'avg_duration': ANY,
|
||||
'best_pair': expected['best_pair'],
|
||||
'best_pair_profit_ratio': expected['best_pair_profit_ratio'],
|
||||
'best_rate': expected['best_rate'],
|
||||
'first_trade_date': ANY,
|
||||
'first_trade_timestamp': ANY,
|
||||
@@ -1185,7 +1191,7 @@ def test_api_pair_candles(botclient, ohlcv_history):
|
||||
assert isinstance(rc.json()['columns'], list)
|
||||
assert rc.json()['columns'] == ['date', 'open', 'high',
|
||||
'low', 'close', 'volume', 'sma', 'buy', 'sell',
|
||||
'__date_ts', '_buy_signal_open', '_sell_signal_open']
|
||||
'__date_ts', '_buy_signal_close', '_sell_signal_close']
|
||||
assert 'pair' in rc.json()
|
||||
assert rc.json()['pair'] == 'XRP/BTC'
|
||||
|
||||
@@ -1196,7 +1202,8 @@ def test_api_pair_candles(botclient, ohlcv_history):
|
||||
[['2017-11-26 08:50:00', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05, 0.0877869,
|
||||
None, 0, 0, 1511686200000, None, None],
|
||||
['2017-11-26 08:55:00', 8.88e-05, 8.942e-05, 8.88e-05,
|
||||
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0, 1511686500000, 8.88e-05, None],
|
||||
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0, 1511686500000, 8.893e-05,
|
||||
None],
|
||||
['2017-11-26 09:00:00', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
|
||||
0.7039405, 8.885e-05, 0, 0, 1511686800000, None, None]
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
import logging
|
||||
import re
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from functools import reduce
|
||||
from random import choice, randint
|
||||
from string import ascii_uppercase
|
||||
@@ -94,10 +94,11 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
|
||||
message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], "
|
||||
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], ['trades'], "
|
||||
"['delete'], ['performance'], ['buys'], ['sells'], ['mix_tags'], "
|
||||
"['stats'], ['daily'], ['count'], ['locks'], "
|
||||
"['unlock', 'delete_locks'], ['reload_config', 'reload_conf'], "
|
||||
"['show_config', 'show_conf'], ['stopbuy'], "
|
||||
"['whitelist'], ['blacklist'], ['logs'], ['edge'], ['help'], ['version']"
|
||||
"['stats'], ['daily'], ['weekly'], ['monthly'], "
|
||||
"['count'], ['locks'], ['unlock', 'delete_locks'], "
|
||||
"['reload_config', 'reload_conf'], ['show_config', 'show_conf'], "
|
||||
"['stopbuy'], ['whitelist'], ['blacklist'], "
|
||||
"['logs'], ['edge'], ['help'], ['version']"
|
||||
"]")
|
||||
|
||||
assert log_has(message_str, caplog)
|
||||
@@ -188,16 +189,16 @@ def test_telegram_status(default_conf, update, mocker) -> None:
|
||||
'amount': 90.99181074,
|
||||
'stake_amount': 90.99181074,
|
||||
'buy_tag': None,
|
||||
'close_profit_pct': None,
|
||||
'close_profit_ratio': None,
|
||||
'profit': -0.0059,
|
||||
'profit_pct': -0.59,
|
||||
'profit_ratio': -0.0059,
|
||||
'initial_stop_loss_abs': 1.098e-05,
|
||||
'stop_loss_abs': 1.099e-05,
|
||||
'sell_order_status': None,
|
||||
'initial_stop_loss_pct': -0.05,
|
||||
'initial_stop_loss_ratio': -0.0005,
|
||||
'stoploss_current_dist': 1e-08,
|
||||
'stoploss_current_dist_pct': -0.02,
|
||||
'stop_loss_pct': -0.01,
|
||||
'stoploss_current_dist_ratio': -0.0002,
|
||||
'stop_loss_ratio': -0.0001,
|
||||
'open_order': '(limit buy rem=0.00000000)',
|
||||
'is_open': True
|
||||
}]),
|
||||
@@ -354,7 +355,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
||||
context.args = ["2"]
|
||||
telegram._daily(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Daily' in msg_mock.call_args_list[0][0][0]
|
||||
assert "Daily Profit over the last 2 days</b>:" in msg_mock.call_args_list[0][0][0]
|
||||
assert 'Day ' in msg_mock.call_args_list[0][0][0]
|
||||
assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
|
||||
@@ -366,7 +368,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
||||
context.args = []
|
||||
telegram._daily(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Daily' in msg_mock.call_args_list[0][0][0]
|
||||
assert "Daily Profit over the last 7 days</b>:" in msg_mock.call_args_list[0][0][0]
|
||||
assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
|
||||
@@ -422,7 +424,242 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
||||
context = MagicMock()
|
||||
context.args = ["today"]
|
||||
telegram._daily(update=update, context=context)
|
||||
assert str('Daily Profit over the last 7 days') in msg_mock.call_args_list[0][0][0]
|
||||
assert str('Daily Profit over the last 7 days</b>:') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee,
|
||||
limit_sell_order, mocker) -> None:
|
||||
default_conf['max_open_trades'] = 1
|
||||
mocker.patch(
|
||||
'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price',
|
||||
return_value=15000.0
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
get_fee=fee,
|
||||
)
|
||||
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
patch_get_signal(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
freqtradebot.enter_positions()
|
||||
trade = Trade.query.first()
|
||||
assert trade
|
||||
|
||||
# Simulate fulfilled LIMIT_BUY order for trade
|
||||
trade.update(limit_buy_order)
|
||||
|
||||
# Simulate fulfilled LIMIT_SELL order for trade
|
||||
trade.update(limit_sell_order)
|
||||
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
|
||||
# Try valid data
|
||||
# /weekly 2
|
||||
context = MagicMock()
|
||||
context.args = ["2"]
|
||||
telegram._weekly(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert "Weekly Profit over the last 2 weeks (starting from Monday)</b>:" \
|
||||
in msg_mock.call_args_list[0][0][0]
|
||||
assert 'Monday ' in msg_mock.call_args_list[0][0][0]
|
||||
today = datetime.utcnow().date()
|
||||
first_iso_day_of_current_week = today - timedelta(days=today.weekday())
|
||||
assert str(first_iso_day_of_current_week) in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 1 trade') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0 trade') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
# Reset msg_mock
|
||||
msg_mock.reset_mock()
|
||||
context.args = []
|
||||
telegram._weekly(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert "Weekly Profit over the last 8 weeks (starting from Monday)</b>:" \
|
||||
in msg_mock.call_args_list[0][0][0]
|
||||
assert 'Weekly' in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 1 trade') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0 trade') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
# Reset msg_mock
|
||||
msg_mock.reset_mock()
|
||||
freqtradebot.config['max_open_trades'] = 2
|
||||
# Add two other trades
|
||||
n = freqtradebot.enter_positions()
|
||||
assert n == 2
|
||||
|
||||
trades = Trade.query.all()
|
||||
for trade in trades:
|
||||
trade.update(limit_buy_order)
|
||||
trade.update(limit_sell_order)
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
|
||||
# /weekly 1
|
||||
# By default, the 8 previous weeks are shown
|
||||
# So the previous modified trade should be excluded from the stats
|
||||
context = MagicMock()
|
||||
context.args = ["1"]
|
||||
telegram._weekly(update=update, context=context)
|
||||
assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 3 trades') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_weekly_wrong_input(default_conf, update, ticker, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker
|
||||
)
|
||||
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
|
||||
# Try invalid data
|
||||
msg_mock.reset_mock()
|
||||
freqtradebot.state = State.RUNNING
|
||||
# /weekly -3
|
||||
context = MagicMock()
|
||||
context.args = ["-3"]
|
||||
telegram._weekly(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'must be an integer greater than 0' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
# Try invalid data
|
||||
msg_mock.reset_mock()
|
||||
freqtradebot.state = State.RUNNING
|
||||
# /weekly this week
|
||||
context = MagicMock()
|
||||
context.args = ["this week"]
|
||||
telegram._weekly(update=update, context=context)
|
||||
assert str('Weekly Profit over the last 8 weeks (starting from Monday)</b>:') \
|
||||
in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
|
||||
limit_sell_order, mocker) -> None:
|
||||
default_conf['max_open_trades'] = 1
|
||||
mocker.patch(
|
||||
'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price',
|
||||
return_value=15000.0
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
get_fee=fee,
|
||||
)
|
||||
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
patch_get_signal(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
freqtradebot.enter_positions()
|
||||
trade = Trade.query.first()
|
||||
assert trade
|
||||
|
||||
# Simulate fulfilled LIMIT_BUY order for trade
|
||||
trade.update(limit_buy_order)
|
||||
|
||||
# Simulate fulfilled LIMIT_SELL order for trade
|
||||
trade.update(limit_sell_order)
|
||||
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
|
||||
# Try valid data
|
||||
# /monthly 2
|
||||
context = MagicMock()
|
||||
context.args = ["2"]
|
||||
telegram._monthly(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Monthly Profit over the last 2 months</b>:' in msg_mock.call_args_list[0][0][0]
|
||||
assert 'Month ' in msg_mock.call_args_list[0][0][0]
|
||||
today = datetime.utcnow().date()
|
||||
current_month = f"{today.year}-{today.month} "
|
||||
assert current_month in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 1 trade') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0 trade') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
# Reset msg_mock
|
||||
msg_mock.reset_mock()
|
||||
context.args = []
|
||||
telegram._monthly(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
# Default to 6 months
|
||||
assert 'Monthly Profit over the last 6 months</b>:' in msg_mock.call_args_list[0][0][0]
|
||||
assert 'Month ' in msg_mock.call_args_list[0][0][0]
|
||||
assert current_month in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 1 trade') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0 trade') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
# Reset msg_mock
|
||||
msg_mock.reset_mock()
|
||||
freqtradebot.config['max_open_trades'] = 2
|
||||
# Add two other trades
|
||||
n = freqtradebot.enter_positions()
|
||||
assert n == 2
|
||||
|
||||
trades = Trade.query.all()
|
||||
for trade in trades:
|
||||
trade.update(limit_buy_order)
|
||||
trade.update(limit_sell_order)
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
|
||||
# /monthly 12
|
||||
context = MagicMock()
|
||||
context.args = ["12"]
|
||||
telegram._monthly(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Monthly Profit over the last 12 months</b>:' in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0]
|
||||
assert str(' 3 trades') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
# The one-digit months should contain a zero, Eg: September 2021 = "2021-09"
|
||||
# Since we loaded the last 12 months, any month should appear
|
||||
assert str('-09') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_monthly_wrong_input(default_conf, update, ticker, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker
|
||||
)
|
||||
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
|
||||
# Try invalid data
|
||||
msg_mock.reset_mock()
|
||||
freqtradebot.state = State.RUNNING
|
||||
# /monthly -3
|
||||
context = MagicMock()
|
||||
context.args = ["-3"]
|
||||
telegram._monthly(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'must be an integer greater than 0' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
# Try invalid data
|
||||
msg_mock.reset_mock()
|
||||
freqtradebot.state = State.RUNNING
|
||||
# /monthly february
|
||||
context = MagicMock()
|
||||
context.args = ["february"]
|
||||
telegram._monthly(update=update, context=context)
|
||||
assert str('Monthly Profit over the last 6 months</b>:') in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
@@ -495,7 +732,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
|
||||
telegram._stats(update=update, context=MagicMock())
|
||||
assert msg_mock.call_count == 1
|
||||
# assert 'No trades yet.' in msg_mock.call_args_list[0][0][0]
|
||||
assert 'No trades yet.' in msg_mock.call_args_list[0][0][0]
|
||||
msg_mock.reset_mock()
|
||||
|
||||
# Create some test data
|
||||
@@ -1453,17 +1690,25 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None:
|
||||
|
||||
telegram.send_msg({
|
||||
'type': RPCMessageType.BUY_FILL,
|
||||
'buy_tag': 'buy_signal_01',
|
||||
'trade_id': 1,
|
||||
'buy_tag': 'buy_signal_01',
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/USDT',
|
||||
'open_rate': 200,
|
||||
'stake_amount': 100,
|
||||
'amount': 0.5,
|
||||
'open_date': arrow.utcnow().datetime
|
||||
'pair': 'ETH/BTC',
|
||||
'stake_amount': 0.001,
|
||||
# 'stake_amount_fiat': 0.0,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'USD',
|
||||
'open_rate': 1.099e-05,
|
||||
'amount': 1333.3333333333335,
|
||||
'open_date': arrow.utcnow().shift(hours=-1)
|
||||
})
|
||||
assert (msg_mock.call_args[0][0] == '\N{LARGE CIRCLE} *Binance:* '
|
||||
'Buy order for ETH/USDT (#1) filled for 200.')
|
||||
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== '\N{CHECK MARK} *Binance:* Bought ETH/BTC (#1)\n' \
|
||||
'*Buy Tag:* `buy_signal_01`\n' \
|
||||
'*Amount:* `1333.33333333`\n' \
|
||||
'*Open Rate:* `0.00001099`\n' \
|
||||
'*Total:* `(0.00100000 BTC, 12.345 USD)`'
|
||||
|
||||
|
||||
def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
@@ -1494,7 +1739,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
|
||||
'*Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n'
|
||||
'*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n'
|
||||
'*Buy Tag:* `buy_signal1`\n'
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1:00:00 (60.0 min)`\n'
|
||||
@@ -1526,7 +1771,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
|
||||
'*Profit:* `-57.41%`\n'
|
||||
'*Unrealized Profit:* `-57.41%`\n'
|
||||
'*Buy Tag:* `buy_signal1`\n'
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
|
||||
@@ -1580,25 +1825,30 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None:
|
||||
'type': RPCMessageType.SELL_FILL,
|
||||
'trade_id': 1,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/USDT',
|
||||
'pair': 'KEY/ETH',
|
||||
'gain': 'loss',
|
||||
'limit': 3.201e-05,
|
||||
'amount': 0.1,
|
||||
'amount': 1333.3333333333335,
|
||||
'order_type': 'market',
|
||||
'open_rate': 500,
|
||||
'close_rate': 550,
|
||||
'current_rate': 3.201e-05,
|
||||
'open_rate': 7.5e-05,
|
||||
'close_rate': 3.201e-05,
|
||||
'profit_amount': -0.05746268,
|
||||
'profit_ratio': -0.57405275,
|
||||
'stake_currency': 'ETH',
|
||||
'fiat_currency': 'USD',
|
||||
'buy_tag': 'buy_signal1',
|
||||
'sell_reason': SellType.STOP_LOSS.value,
|
||||
'open_date': arrow.utcnow().shift(hours=-1),
|
||||
'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
|
||||
'close_date': arrow.utcnow(),
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('\N{LARGE CIRCLE} *Binance:* Sell order for ETH/USDT (#1) filled for 550.')
|
||||
== ('\N{WARNING SIGN} *Binance:* Sold KEY/ETH (#1)\n'
|
||||
'*Profit:* `-57.41%`\n'
|
||||
'*Buy Tag:* `buy_signal1`\n'
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
|
||||
|
||||
def test_send_msg_status_notification(default_conf, mocker) -> None:
|
||||
@@ -1690,7 +1940,7 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
|
||||
'close_date': arrow.utcnow(),
|
||||
})
|
||||
assert msg_mock.call_args[0][0] == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
|
||||
'*Profit:* `-57.41%`\n'
|
||||
'*Unrealized Profit:* `-57.41%`\n'
|
||||
'*Buy Tag:* `buy_signal1`\n'
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `2:35:03 (155.1 min)`\n'
|
||||
|
@@ -171,12 +171,8 @@ def test_edge_called_in_process(mocker, edge_conf) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_edge(mocker)
|
||||
|
||||
def _refresh_whitelist(list):
|
||||
return ['ETH/USDT', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC']
|
||||
|
||||
patch_exchange(mocker)
|
||||
freqtrade = FreqtradeBot(edge_conf)
|
||||
freqtrade.pairlists._validate_whitelist = _refresh_whitelist
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.process()
|
||||
assert freqtrade.active_pair_whitelist == ['NEO/BTC', 'LTC/BTC']
|
||||
@@ -328,7 +324,7 @@ def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, fee, mocke
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize('stake_amount,create,amount_enough,max_open_trades', [
|
||||
(5.0, True, True, 99),
|
||||
(0.00005, True, False, 99),
|
||||
(0.049, True, False, 99), # Amount will be adjusted to min - which is 0.051
|
||||
(0, False, True, 99),
|
||||
(UNLIMITED_STAKE_AMOUNT, False, True, 0),
|
||||
])
|
||||
@@ -678,9 +674,6 @@ def test_process_informative_pairs_added(default_conf_usdt, ticker_usdt, mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
|
||||
def _refresh_whitelist(list):
|
||||
return ['ETH/USDT', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC']
|
||||
|
||||
refresh_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -697,7 +690,6 @@ def test_process_informative_pairs_added(default_conf_usdt, ticker_usdt, mocker)
|
||||
mocker.patch('time.sleep', return_value=None)
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
freqtrade.pairlists._validate_whitelist = _refresh_whitelist
|
||||
freqtrade.strategy.informative_pairs = inf_pairs
|
||||
# patch_get_signal(freqtrade)
|
||||
|
||||
@@ -1733,7 +1725,6 @@ def test_exit_positions_exception(
|
||||
trade = MagicMock()
|
||||
trade.is_short = is_short
|
||||
trade.open_order_id = None
|
||||
trade.open_fee = 0.001
|
||||
trade.pair = 'ETH/USDT'
|
||||
trades = [trade]
|
||||
|
||||
@@ -1853,8 +1844,6 @@ def test_update_trade_state_exception(mocker, default_conf_usdt, is_short, limit
|
||||
|
||||
trade = MagicMock()
|
||||
trade.open_order_id = '123'
|
||||
trade.open_fee = 0.001
|
||||
trade.is_short = is_short
|
||||
|
||||
# Test raise of OperationalException exception
|
||||
mocker.patch(
|
||||
@@ -1872,7 +1861,6 @@ def test_update_trade_state_orderexception(mocker, default_conf_usdt, caplog) ->
|
||||
|
||||
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())
|
||||
@@ -2364,12 +2352,13 @@ def test_check_handle_timedout_buy_exception(
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_sell_usercustom(
|
||||
default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker,
|
||||
is_short, open_trade_usdt
|
||||
is_short, open_trade_usdt, caplog
|
||||
) -> None:
|
||||
default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440}
|
||||
default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440, "exit_timeout_count": 1}
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
cancel_order_mock = MagicMock()
|
||||
patch_exchange(mocker)
|
||||
et_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.execute_trade_exit')
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker_usdt,
|
||||
@@ -2412,6 +2401,14 @@ def test_check_handle_timedout_sell_usercustom(
|
||||
assert open_trade_usdt.is_open is True
|
||||
assert freqtrade.strategy.check_sell_timeout.call_count == 1
|
||||
|
||||
# 2nd canceled trade ...
|
||||
caplog.clear()
|
||||
open_trade_usdt.open_order_id = 'order_id_2'
|
||||
mocker.patch('freqtrade.persistence.Trade.get_exit_order_count', return_value=1)
|
||||
freqtrade.check_handle_timedout()
|
||||
assert log_has_re('Emergencyselling trade.*', caplog)
|
||||
assert et_mock.call_count == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_sell(
|
||||
@@ -2837,6 +2834,8 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
|
||||
)
|
||||
assert rpc_mock.call_count == 0
|
||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
||||
assert id(freqtrade.strategy.confirm_trade_exit.call_args_list[0][1]['trade']) != id(trade)
|
||||
assert freqtrade.strategy.confirm_trade_exit.call_args_list[0][1]['trade'].id == trade.id
|
||||
|
||||
# Repatch with true
|
||||
freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
|
||||
@@ -3700,7 +3699,7 @@ def test_trailing_stop_loss_positive(
|
||||
# stop-loss not reached, adjusted stoploss
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
caplog_text = (f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: "
|
||||
f"{'0.0249' if not is_short else '0.0224'}%")
|
||||
f"{'2.49' if not is_short else '2.24'}%")
|
||||
if trail_if_reached:
|
||||
assert not log_has(caplog_text, caplog)
|
||||
assert not log_has("ETH/USDT - Adjusting stoploss...", caplog)
|
||||
@@ -3721,7 +3720,7 @@ def test_trailing_stop_loss_positive(
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
assert log_has(
|
||||
f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: "
|
||||
f"{'0.0572' if not is_short else '0.0567'}%",
|
||||
f"{'5.72' if not is_short else '5.67'}%",
|
||||
caplog
|
||||
)
|
||||
assert log_has("ETH/USDT - Adjusting stoploss...", caplog)
|
||||
@@ -3997,6 +3996,31 @@ def test_get_real_amount_invalid_order(default_conf_usdt, trades_for_order, buy_
|
||||
assert freqtrade.get_real_amount(trade, limit_buy_order_usdt) == amount
|
||||
|
||||
|
||||
def test_get_real_amount_fees_order(default_conf_usdt, market_buy_order_usdt_doublefee,
|
||||
fee, mocker):
|
||||
|
||||
tfo_mock = mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', return_value='BNB/USDT')
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'last': 200})
|
||||
trade = Trade(
|
||||
pair='LTC/USDT',
|
||||
amount=30.0,
|
||||
exchange='binance',
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_rate=0.245441,
|
||||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
# Amount does not change
|
||||
assert trade.fee_open == 0.0025
|
||||
assert freqtrade.get_real_amount(trade, market_buy_order_usdt_doublefee) == 30.0
|
||||
assert tfo_mock.call_count == 0
|
||||
# Fetch fees from trades dict if available to get "proper" values
|
||||
assert round(trade.fee_open, 4) == 0.001
|
||||
|
||||
|
||||
def test_get_real_amount_wrong_amount(default_conf_usdt, trades_for_order, buy_order_fee, fee,
|
||||
mocker):
|
||||
limit_buy_order_usdt = deepcopy(buy_order_fee)
|
||||
|
@@ -14,8 +14,8 @@ from freqtrade import constants
|
||||
from freqtrade.enums import TradingMode
|
||||
from freqtrade.exceptions import DependencyException, OperationalException
|
||||
from freqtrade.persistence import LocalTrade, Order, Trade, clean_dry_run_db, init_db
|
||||
from tests.conftest import (create_mock_trades, create_mock_trades_with_leverage, get_sides,
|
||||
log_has, log_has_re)
|
||||
from tests.conftest import (create_mock_trades, create_mock_trades_usdt,
|
||||
create_mock_trades_with_leverage, get_sides, log_has, log_has_re)
|
||||
|
||||
|
||||
spot, margin, futures = TradingMode.SPOT, TradingMode.MARGIN, TradingMode.FUTURES
|
||||
@@ -1980,6 +1980,13 @@ def test_get_best_pair_lev(fee):
|
||||
assert res[1] == 0.1713156134055116
|
||||
|
||||
|
||||
def test_get_exit_order_count(fee):
|
||||
|
||||
create_mock_trades_usdt(fee)
|
||||
trade = Trade.get_trades([Trade.pair == 'ETC/USDT']).first()
|
||||
assert trade.get_exit_order_count() == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_update_order_from_ccxt(caplog):
|
||||
# Most basic order return (only has orderid)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
@@ -172,7 +171,7 @@ def test_plot_trades(testdatadir, caplog):
|
||||
assert len(trades) == len(trade_buy.x)
|
||||
assert trade_buy.marker.color == 'cyan'
|
||||
assert trade_buy.marker.symbol == 'circle-open'
|
||||
assert trade_buy.text[0] == '4.0%, roi, 15 min'
|
||||
assert trade_buy.text[0] == '3.99%, roi, 15 min'
|
||||
|
||||
trade_sell = find_trace_in_fig_data(figure.data, 'Sell - Profit')
|
||||
assert isinstance(trade_sell, go.Scatter)
|
||||
@@ -180,7 +179,7 @@ def test_plot_trades(testdatadir, caplog):
|
||||
assert len(trades.loc[trades['profit_ratio'] > 0]) == len(trade_sell.x)
|
||||
assert trade_sell.marker.color == 'green'
|
||||
assert trade_sell.marker.symbol == 'square-open'
|
||||
assert trade_sell.text[0] == '4.0%, roi, 15 min'
|
||||
assert trade_sell.text[0] == '3.99%, roi, 15 min'
|
||||
|
||||
trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss')
|
||||
assert isinstance(trade_sell_loss, go.Scatter)
|
||||
@@ -188,7 +187,7 @@ def test_plot_trades(testdatadir, caplog):
|
||||
assert len(trades.loc[trades['profit_ratio'] <= 0]) == len(trade_sell_loss.x)
|
||||
assert trade_sell_loss.marker.color == 'red'
|
||||
assert trade_sell_loss.marker.symbol == 'square-open'
|
||||
assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min'
|
||||
assert trade_sell_loss.text[5] == '-10.45%, stop_loss, 720 min'
|
||||
|
||||
|
||||
def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog):
|
||||
|
@@ -185,17 +185,18 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r
|
||||
(100, 11, 500, 100),
|
||||
(1000, 11, 500, 500), # Above max-stake
|
||||
(20, 15, 10, 0), # Minimum stake > max-stake
|
||||
(1, 11, 100, 11), # Below min stake
|
||||
(9, 11, 100, 11), # Below min stake
|
||||
(1, 15, 10, 0), # Below min stake and min_stake > max_stake
|
||||
(20, 50, 100, 0), # Below min stake and stake * 1.3 > min_stake
|
||||
|
||||
])
|
||||
def test__validate_stake_amount(mocker, default_conf,
|
||||
stake_amount, min_stake_amount, max_stake_amount, expected):
|
||||
def test_validate_stake_amount(mocker, default_conf,
|
||||
stake_amount, min_stake_amount, max_stake_amount, expected):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount",
|
||||
return_value=max_stake_amount)
|
||||
res = freqtrade.wallets._validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount)
|
||||
res = freqtrade.wallets.validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount)
|
||||
assert res == expected
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user