Merge branch 'develop' into pr/hroff-1902/3619
This commit is contained in:
@@ -736,7 +736,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--no-details"
|
||||
"--no-details",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -749,7 +749,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--best",
|
||||
"--no-details"
|
||||
"--no-details",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -763,7 +763,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--profitable",
|
||||
"--no-details"
|
||||
"--no-details",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -776,7 +776,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
" 11/12", " 12/12"])
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--profitable"
|
||||
"--profitable",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -792,7 +792,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
"hyperopt-list",
|
||||
"--no-details",
|
||||
"--no-color",
|
||||
"--min-trades", "20"
|
||||
"--min-trades", "20",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -806,7 +806,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
"hyperopt-list",
|
||||
"--profitable",
|
||||
"--no-details",
|
||||
"--max-trades", "20"
|
||||
"--max-trades", "20",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -821,7 +821,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
"hyperopt-list",
|
||||
"--profitable",
|
||||
"--no-details",
|
||||
"--min-avg-profit", "0.11"
|
||||
"--min-avg-profit", "0.11",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -835,7 +835,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--no-details",
|
||||
"--max-avg-profit", "0.10"
|
||||
"--max-avg-profit", "0.10",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -849,7 +849,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--no-details",
|
||||
"--min-total-profit", "0.4"
|
||||
"--min-total-profit", "0.4",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -863,7 +863,35 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--no-details",
|
||||
"--max-total-profit", "0.4"
|
||||
"--max-total-profit", "0.4",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
start_hyperopt_list(pargs)
|
||||
captured = capsys.readouterr()
|
||||
assert all(x in captured.out
|
||||
for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12",
|
||||
" 9/12", " 11/12"])
|
||||
assert all(x not in captured.out
|
||||
for x in [" 4/12", " 10/12", " 12/12"])
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--no-details",
|
||||
"--min-objective", "0.1",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
start_hyperopt_list(pargs)
|
||||
captured = capsys.readouterr()
|
||||
assert all(x in captured.out
|
||||
for x in [" 10/12"])
|
||||
assert all(x not in captured.out
|
||||
for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12",
|
||||
" 9/12", " 11/12", " 12/12"])
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--no-details",
|
||||
"--max-objective", "0.1",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -878,7 +906,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
"hyperopt-list",
|
||||
"--profitable",
|
||||
"--no-details",
|
||||
"--min-avg-time", "2000"
|
||||
"--min-avg-time", "2000",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -892,7 +920,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--no-details",
|
||||
"--max-avg-time", "1500"
|
||||
"--max-avg-time", "1500",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -906,7 +934,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||
args = [
|
||||
"hyperopt-list",
|
||||
"--no-details",
|
||||
"--export-csv", "test_file.csv"
|
||||
"--export-csv", "test_file.csv",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
@@ -1089,7 +1117,7 @@ def test_show_trades(mocker, fee, capsys, caplog):
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
start_show_trades(pargs)
|
||||
assert log_has("Printing 3 Trades: ", caplog)
|
||||
assert log_has("Printing 4 Trades: ", caplog)
|
||||
captured = capsys.readouterr()
|
||||
assert "Trade(id=1" in captured.out
|
||||
assert "Trade(id=2" in captured.out
|
||||
|
@@ -176,6 +176,7 @@ def create_mock_trades(fee):
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
amount_requested=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_rate=0.123,
|
||||
@@ -188,6 +189,7 @@ def create_mock_trades(fee):
|
||||
pair='ETC/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
amount_requested=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_rate=0.123,
|
||||
@@ -199,11 +201,26 @@ def create_mock_trades(fee):
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
|
||||
trade = Trade(
|
||||
pair='XRP/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_rate=0.05,
|
||||
close_rate=0.06,
|
||||
close_profit=0.01,
|
||||
exchange='bittrex',
|
||||
is_open=False,
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
|
||||
# Simulate prod entry
|
||||
trade = Trade(
|
||||
pair='ETC/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
amount_requested=124.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_rate=0.123,
|
||||
|
@@ -43,7 +43,7 @@ def test_load_trades_from_db(default_conf, fee, mocker):
|
||||
|
||||
trades = load_trades_from_db(db_url=default_conf['db_url'])
|
||||
assert init_mock.call_count == 1
|
||||
assert len(trades) == 3
|
||||
assert len(trades) == 4
|
||||
assert isinstance(trades, DataFrame)
|
||||
assert "pair" in trades.columns
|
||||
assert "open_time" in trades.columns
|
||||
|
@@ -409,3 +409,98 @@ def test_process_expectancy(mocker, edge_conf, fee, risk_reward_ratio, expectanc
|
||||
final = edge._process_expectancy(trades_df)
|
||||
assert len(final) == 0
|
||||
assert isinstance(final, dict)
|
||||
|
||||
|
||||
def test_process_expectancy_remove_pumps(mocker, edge_conf, fee,):
|
||||
edge_conf['edge']['min_trade_number'] = 2
|
||||
edge_conf['edge']['remove_pumps'] = True
|
||||
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
|
||||
|
||||
freqtrade.exchange.get_fee = 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': 15,
|
||||
'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': 10,
|
||||
'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': 10,
|
||||
'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': 10,
|
||||
'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': 10,
|
||||
'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': 134,
|
||||
'exit_type': 'sell_signal'}
|
||||
]
|
||||
|
||||
trades_df = DataFrame(trades)
|
||||
trades_df = edge._fill_calculable_fields(trades_df)
|
||||
final = edge._process_expectancy(trades_df)
|
||||
|
||||
assert 'TEST/BTC' in final
|
||||
assert final['TEST/BTC'].stoploss == -0.9
|
||||
assert final['TEST/BTC'].nb_trades == len(trades_df) - 1
|
||||
assert round(final['TEST/BTC'].winrate, 10) == 0.0
|
||||
|
@@ -11,11 +11,12 @@ import ccxt
|
||||
import pytest
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.exceptions import (DependencyException, InvalidOrderException, DDosProtection,
|
||||
OperationalException, TemporaryError)
|
||||
from freqtrade.exceptions import (DDosProtection, DependencyException,
|
||||
InvalidOrderException, OperationalException,
|
||||
TemporaryError)
|
||||
from freqtrade.exchange import Binance, Exchange, Kraken
|
||||
from freqtrade.exchange.common import API_RETRY_COUNT, calculate_backoff
|
||||
from freqtrade.exchange.exchange import (market_is_active, symbol_is_pair,
|
||||
from freqtrade.exchange.exchange import (market_is_active,
|
||||
timeframe_to_minutes,
|
||||
timeframe_to_msecs,
|
||||
timeframe_to_next_date,
|
||||
@@ -1818,7 +1819,7 @@ def test_cancel_order_with_result_error(default_conf, mocker, exchange_name, cap
|
||||
|
||||
res = exchange.cancel_order_with_result('1234', 'ETH/BTC', 1541)
|
||||
assert isinstance(res, dict)
|
||||
assert log_has("Could not cancel order 1234.", caplog)
|
||||
assert log_has("Could not cancel order 1234 for ETH/BTC.", caplog)
|
||||
assert log_has("Could not fetch cancelled order 1234.", caplog)
|
||||
assert res['amount'] == 1541
|
||||
|
||||
@@ -1896,10 +1897,10 @@ def test_fetch_order(default_conf, mocker, exchange_name):
|
||||
assert tm.call_args_list[1][0][0] == 2
|
||||
assert tm.call_args_list[2][0][0] == 5
|
||||
assert tm.call_args_list[3][0][0] == 10
|
||||
assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
|
||||
assert api_mock.fetch_order.call_count == 6
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
'fetch_order', 'fetch_order',
|
||||
'fetch_order', 'fetch_order', retries=6,
|
||||
order_id='_', pair='TKN/BTC')
|
||||
|
||||
|
||||
@@ -1932,6 +1933,7 @@ def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
'fetch_stoploss_order', 'fetch_order',
|
||||
retries=6,
|
||||
order_id='_', pair='TKN/BTC')
|
||||
|
||||
|
||||
@@ -2217,25 +2219,42 @@ def test_timeframe_to_next_date():
|
||||
assert timeframe_to_next_date("5m") > date
|
||||
|
||||
|
||||
@pytest.mark.parametrize("market_symbol,base_currency,quote_currency,expected_result", [
|
||||
("BTC/USDT", None, None, True),
|
||||
("USDT/BTC", None, None, True),
|
||||
("BTCUSDT", None, None, False),
|
||||
("BTC/USDT", None, "USDT", True),
|
||||
("USDT/BTC", None, "USDT", False),
|
||||
("BTCUSDT", None, "USDT", False),
|
||||
("BTC/USDT", "BTC", None, True),
|
||||
("USDT/BTC", "BTC", None, False),
|
||||
("BTCUSDT", "BTC", None, False),
|
||||
("BTC/USDT", "BTC", "USDT", True),
|
||||
("BTC/USDT", "USDT", "BTC", False),
|
||||
("BTC/USDT", "BTC", "USD", False),
|
||||
("BTCUSDT", "BTC", "USDT", False),
|
||||
("BTC/", None, None, False),
|
||||
("/USDT", None, None, False),
|
||||
@pytest.mark.parametrize("market_symbol,base,quote,exchange,add_dict,expected_result", [
|
||||
("BTC/USDT", 'BTC', 'USDT', "binance", {}, True),
|
||||
("USDT/BTC", 'USDT', 'BTC', "binance", {}, True),
|
||||
("USDT/BTC", 'BTC', 'USDT', "binance", {}, False), # Reversed currencies
|
||||
("BTCUSDT", 'BTC', 'USDT', "binance", {}, False), # No seperating /
|
||||
("BTCUSDT", None, "USDT", "binance", {}, False), #
|
||||
("USDT/BTC", "BTC", None, "binance", {}, False),
|
||||
("BTCUSDT", "BTC", None, "binance", {}, False),
|
||||
("BTC/USDT", "BTC", "USDT", "binance", {}, True),
|
||||
("BTC/USDT", "USDT", "BTC", "binance", {}, False), # reversed currencies
|
||||
("BTC/USDT", "BTC", "USD", "binance", {}, False), # Wrong quote currency
|
||||
("BTC/", "BTC", 'UNK', "binance", {}, False),
|
||||
("/USDT", 'UNK', 'USDT', "binance", {}, False),
|
||||
("BTC/EUR", 'BTC', 'EUR', "kraken", {"darkpool": False}, True),
|
||||
("EUR/BTC", 'EUR', 'BTC', "kraken", {"darkpool": False}, True),
|
||||
("EUR/BTC", 'BTC', 'EUR', "kraken", {"darkpool": False}, False), # Reversed currencies
|
||||
("BTC/EUR", 'BTC', 'USD', "kraken", {"darkpool": False}, False), # wrong quote currency
|
||||
("BTC/EUR", 'BTC', 'EUR', "kraken", {"darkpool": True}, False), # no darkpools
|
||||
("BTC/EUR.d", 'BTC', 'EUR', "kraken", {"darkpool": True}, False), # no darkpools
|
||||
("BTC/USD", 'BTC', 'USD', "ftx", {'spot': True}, True),
|
||||
("USD/BTC", 'USD', 'BTC', "ftx", {'spot': True}, True),
|
||||
("BTC/USD", 'BTC', 'USDT', "ftx", {'spot': True}, False), # Wrong quote currency
|
||||
("BTC/USD", 'USD', 'BTC', "ftx", {'spot': True}, False), # Reversed currencies
|
||||
("BTC/USD", 'BTC', 'USD', "ftx", {'spot': False}, False), # Can only trade spot markets
|
||||
("BTC-PERP", 'BTC', 'USD', "ftx", {'spot': False}, False), # Can only trade spot markets
|
||||
])
|
||||
def test_symbol_is_pair(market_symbol, base_currency, quote_currency, expected_result) -> None:
|
||||
assert symbol_is_pair(market_symbol, base_currency, quote_currency) == expected_result
|
||||
def test_market_is_tradable(mocker, default_conf, market_symbol, base,
|
||||
quote, add_dict, exchange, expected_result) -> None:
|
||||
ex = get_patched_exchange(mocker, default_conf, id=exchange)
|
||||
market = {
|
||||
'symbol': market_symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
**(add_dict),
|
||||
}
|
||||
assert ex.market_is_tradable(market) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("market,expected_result", [
|
||||
@@ -2315,6 +2334,18 @@ def test_calculate_fee_rate(mocker, default_conf, order, expected) -> None:
|
||||
(3, 3, 1),
|
||||
(0, 1, 2),
|
||||
(1, 1, 1),
|
||||
(0, 4, 17),
|
||||
(1, 4, 10),
|
||||
(2, 4, 5),
|
||||
(3, 4, 2),
|
||||
(4, 4, 1),
|
||||
(0, 5, 26),
|
||||
(1, 5, 17),
|
||||
(2, 5, 10),
|
||||
(3, 5, 5),
|
||||
(4, 5, 2),
|
||||
(5, 5, 1),
|
||||
|
||||
])
|
||||
def test_calculate_backoff(retrycount, max_retries, expected):
|
||||
assert calculate_backoff(retrycount, max_retries) == expected
|
||||
|
@@ -154,4 +154,5 @@ def test_fetch_stoploss_order(default_conf, mocker):
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'ftx',
|
||||
'fetch_stoploss_order', 'fetch_orders',
|
||||
retries=6,
|
||||
order_id='_', pair='TKN/BTC')
|
||||
|
@@ -744,8 +744,10 @@ def test_generate_optimizer(mocker, default_conf) -> None:
|
||||
}
|
||||
response_expected = {
|
||||
'loss': 1.9840569076926293,
|
||||
'results_explanation': (' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC '
|
||||
'( 2.31\N{GREEK CAPITAL LETTER SIGMA}%). Avg duration 100.0 min.'
|
||||
'results_explanation': (' 1 trades. 1/0/0 Wins/Draws/Losses. '
|
||||
'Avg profit 2.31%. Median profit 2.31%. Total profit '
|
||||
'0.00023300 BTC ( 2.31\N{GREEK CAPITAL LETTER SIGMA}%). '
|
||||
'Avg duration 100.0 min.'
|
||||
).encode(locale.getpreferredencoding(), 'replace').decode('utf-8'),
|
||||
'params_details': {'buy': {'adx-enabled': False,
|
||||
'adx-value': 0,
|
||||
@@ -776,10 +778,15 @@ def test_generate_optimizer(mocker, default_conf) -> None:
|
||||
'trailing_stop_positive_offset': 0.07}},
|
||||
'params_dict': optimizer_param,
|
||||
'results_metrics': {'avg_profit': 2.3117,
|
||||
'draws': 0,
|
||||
'duration': 100.0,
|
||||
'losses': 0,
|
||||
'winsdrawslosses': '1/0/0',
|
||||
'median_profit': 2.3117,
|
||||
'profit': 2.3117,
|
||||
'total_profit': 0.000233,
|
||||
'trade_count': 1},
|
||||
'trade_count': 1,
|
||||
'wins': 1},
|
||||
'total_profit': 0.00023300
|
||||
}
|
||||
|
||||
|
@@ -468,7 +468,9 @@ def test_pairlist_class(mocker, whitelist_conf, markets, pairlist):
|
||||
# BCH/BTC not available
|
||||
(['ETH/BTC', 'TKN/BTC', 'BCH/BTC'], "is not compatible with exchange"),
|
||||
# BTT/BTC is inactive
|
||||
(['ETH/BTC', 'TKN/BTC', 'BTT/BTC'], "Market is not active")
|
||||
(['ETH/BTC', 'TKN/BTC', 'BTT/BTC'], "Market is not active"),
|
||||
# XLTCUSDT is not a valid pair
|
||||
(['ETH/BTC', 'TKN/BTC', 'XLTCUSDT'], "is not tradable with Freqtrade"),
|
||||
])
|
||||
def test__whitelist_for_active_markets(mocker, whitelist_conf, markets, pairlist, whitelist, caplog,
|
||||
log_message, tickers):
|
||||
|
@@ -8,7 +8,7 @@ import pytest
|
||||
from numpy import isnan
|
||||
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.exceptions import ExchangeError, TemporaryError
|
||||
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.rpc import RPC, RPCException
|
||||
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
||||
@@ -79,7 +79,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'open_rate': 1.098e-05,
|
||||
'close_rate': None,
|
||||
'current_rate': 1.099e-05,
|
||||
'amount': 91.07468124,
|
||||
'amount': 91.07468123,
|
||||
'amount_requested': 91.07468123,
|
||||
'stake_amount': 0.001,
|
||||
'close_profit': None,
|
||||
'close_profit_pct': None,
|
||||
@@ -142,7 +143,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'open_rate': 1.098e-05,
|
||||
'close_rate': None,
|
||||
'current_rate': ANY,
|
||||
'amount': 91.07468124,
|
||||
'amount': 91.07468123,
|
||||
'amount_requested': 91.07468123,
|
||||
'stake_amount': 0.001,
|
||||
'close_profit': None,
|
||||
'close_profit_pct': None,
|
||||
@@ -284,12 +286,66 @@ def test_rpc_trade_history(mocker, default_conf, markets, fee):
|
||||
assert isinstance(trades['trades'][1], dict)
|
||||
|
||||
trades = rpc._rpc_trade_history(0)
|
||||
assert len(trades['trades']) == 3
|
||||
assert trades['trades_count'] == 3
|
||||
# The first trade is for ETH ... sorting is descending
|
||||
assert trades['trades'][-1]['pair'] == 'ETH/BTC'
|
||||
assert trades['trades'][0]['pair'] == 'ETC/BTC'
|
||||
assert trades['trades'][1]['pair'] == 'ETC/BTC'
|
||||
assert len(trades['trades']) == 2
|
||||
assert trades['trades_count'] == 2
|
||||
# The first closed trade is for ETC ... sorting is descending
|
||||
assert trades['trades'][-1]['pair'] == 'ETC/BTC'
|
||||
assert trades['trades'][0]['pair'] == 'XRP/BTC'
|
||||
|
||||
|
||||
def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog):
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
stoploss_mock = MagicMock()
|
||||
cancel_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
markets=PropertyMock(return_value=markets),
|
||||
cancel_order=cancel_mock,
|
||||
cancel_stoploss_order=stoploss_mock,
|
||||
)
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
freqtradebot.strategy.order_types['stoploss_on_exchange'] = True
|
||||
create_mock_trades(fee)
|
||||
rpc = RPC(freqtradebot)
|
||||
with pytest.raises(RPCException, match='invalid argument'):
|
||||
rpc._rpc_delete('200')
|
||||
|
||||
create_mock_trades(fee)
|
||||
trades = Trade.query.all()
|
||||
trades[1].stoploss_order_id = '1234'
|
||||
trades[2].stoploss_order_id = '1234'
|
||||
assert len(trades) > 2
|
||||
|
||||
res = rpc._rpc_delete('1')
|
||||
assert isinstance(res, dict)
|
||||
assert res['result'] == 'success'
|
||||
assert res['trade_id'] == '1'
|
||||
assert res['cancel_order_count'] == 1
|
||||
assert cancel_mock.call_count == 1
|
||||
assert stoploss_mock.call_count == 0
|
||||
cancel_mock.reset_mock()
|
||||
stoploss_mock.reset_mock()
|
||||
|
||||
res = rpc._rpc_delete('2')
|
||||
assert isinstance(res, dict)
|
||||
assert cancel_mock.call_count == 1
|
||||
assert stoploss_mock.call_count == 1
|
||||
assert res['cancel_order_count'] == 2
|
||||
|
||||
stoploss_mock = mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
|
||||
side_effect=InvalidOrderException)
|
||||
|
||||
res = rpc._rpc_delete('3')
|
||||
assert stoploss_mock.call_count == 1
|
||||
stoploss_mock.reset_mock()
|
||||
|
||||
cancel_mock = mocker.patch('freqtrade.exchange.Exchange.cancel_order',
|
||||
side_effect=InvalidOrderException)
|
||||
|
||||
res = rpc._rpc_delete('4')
|
||||
assert cancel_mock.call_count == 1
|
||||
assert stoploss_mock.call_count == 0
|
||||
|
||||
|
||||
def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
||||
|
@@ -50,6 +50,12 @@ def client_get(client, url):
|
||||
'Origin': 'http://example.com'})
|
||||
|
||||
|
||||
def client_delete(client, url):
|
||||
# Add fake Origin to ensure CORS kicks in
|
||||
return client.delete(url, headers={'Authorization': _basic_auth_str(_TEST_USER, _TEST_PASS),
|
||||
'Origin': 'http://example.com'})
|
||||
|
||||
|
||||
def assert_response(response, expected_code=200, needs_cors=True):
|
||||
assert response.status_code == expected_code
|
||||
assert response.content_type == "application/json"
|
||||
@@ -352,7 +358,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets):
|
||||
assert rc.json['data'][0]['date'] == str(datetime.utcnow().date())
|
||||
|
||||
|
||||
def test_api_trades(botclient, mocker, ticker, fee, markets):
|
||||
def test_api_trades(botclient, mocker, fee, markets):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot, (True, False))
|
||||
mocker.patch.multiple(
|
||||
@@ -368,12 +374,53 @@ def test_api_trades(botclient, mocker, ticker, fee, markets):
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/trades")
|
||||
assert_response(rc)
|
||||
assert len(rc.json['trades']) == 3
|
||||
assert rc.json['trades_count'] == 3
|
||||
rc = client_get(client, f"{BASE_URI}/trades?limit=2")
|
||||
assert_response(rc)
|
||||
assert len(rc.json['trades']) == 2
|
||||
assert rc.json['trades_count'] == 2
|
||||
rc = client_get(client, f"{BASE_URI}/trades?limit=1")
|
||||
assert_response(rc)
|
||||
assert len(rc.json['trades']) == 1
|
||||
assert rc.json['trades_count'] == 1
|
||||
|
||||
|
||||
def test_api_delete_trade(botclient, mocker, fee, markets):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot, (True, False))
|
||||
stoploss_mock = MagicMock()
|
||||
cancel_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
markets=PropertyMock(return_value=markets),
|
||||
cancel_order=cancel_mock,
|
||||
cancel_stoploss_order=stoploss_mock,
|
||||
)
|
||||
rc = client_delete(client, f"{BASE_URI}/trades/1")
|
||||
# Error - trade won't exist yet.
|
||||
assert_response(rc, 502)
|
||||
|
||||
create_mock_trades(fee)
|
||||
ftbot.strategy.order_types['stoploss_on_exchange'] = True
|
||||
trades = Trade.query.all()
|
||||
trades[1].stoploss_order_id = '1234'
|
||||
assert len(trades) > 2
|
||||
|
||||
rc = client_delete(client, f"{BASE_URI}/trades/1")
|
||||
assert_response(rc)
|
||||
assert rc.json['result_msg'] == 'Deleted trade 1. Closed 1 open orders.'
|
||||
assert len(trades) - 1 == len(Trade.query.all())
|
||||
assert cancel_mock.call_count == 1
|
||||
|
||||
cancel_mock.reset_mock()
|
||||
rc = client_delete(client, f"{BASE_URI}/trades/1")
|
||||
# Trade is gone now.
|
||||
assert_response(rc, 502)
|
||||
assert cancel_mock.call_count == 0
|
||||
|
||||
assert len(trades) - 1 == len(Trade.query.all())
|
||||
rc = client_delete(client, f"{BASE_URI}/trades/2")
|
||||
assert_response(rc)
|
||||
assert rc.json['result_msg'] == 'Deleted trade 2. Closed 2 open orders.'
|
||||
assert len(trades) - 2 == len(Trade.query.all())
|
||||
assert stoploss_mock.call_count == 1
|
||||
|
||||
|
||||
def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
||||
@@ -519,7 +566,8 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||
rc = client_get(client, f"{BASE_URI}/status")
|
||||
assert_response(rc)
|
||||
assert len(rc.json) == 1
|
||||
assert rc.json == [{'amount': 91.07468124,
|
||||
assert rc.json == [{'amount': 91.07468123,
|
||||
'amount_requested': 91.07468123,
|
||||
'base_currency': 'BTC',
|
||||
'close_date': None,
|
||||
'close_date_hum': None,
|
||||
@@ -641,6 +689,7 @@ def test_api_forcebuy(botclient, mocker, fee):
|
||||
fbuy_mock = MagicMock(return_value=Trade(
|
||||
pair='ETH/ETH',
|
||||
amount=1,
|
||||
amount_requested=1,
|
||||
exchange='bittrex',
|
||||
stake_amount=1,
|
||||
open_rate=0.245441,
|
||||
@@ -657,6 +706,7 @@ def test_api_forcebuy(botclient, mocker, fee):
|
||||
data='{"pair": "ETH/BTC"}')
|
||||
assert_response(rc)
|
||||
assert rc.json == {'amount': 1,
|
||||
'amount_requested': 1,
|
||||
'trade_id': None,
|
||||
'close_date': None,
|
||||
'close_date_hum': None,
|
||||
@@ -693,7 +743,7 @@ def test_api_forcebuy(botclient, mocker, fee):
|
||||
'min_rate': None,
|
||||
'open_order_id': '123456',
|
||||
'open_rate_requested': None,
|
||||
'open_trade_price': 0.2460546025,
|
||||
'open_trade_price': 0.24605460,
|
||||
'sell_reason': None,
|
||||
'sell_order_status': None,
|
||||
'strategy': None,
|
||||
|
@@ -21,8 +21,9 @@ from freqtrade.rpc import RPCMessageType
|
||||
from freqtrade.rpc.telegram import Telegram, authorized_only
|
||||
from freqtrade.state import State
|
||||
from freqtrade.strategy.interface import SellType
|
||||
from tests.conftest import (get_patched_freqtradebot, log_has, patch_exchange,
|
||||
patch_get_signal, patch_whitelist)
|
||||
from tests.conftest import (create_mock_trades, get_patched_freqtradebot,
|
||||
log_has, patch_exchange, patch_get_signal,
|
||||
patch_whitelist)
|
||||
|
||||
|
||||
class DummyCls(Telegram):
|
||||
@@ -60,7 +61,7 @@ def test__init__(default_conf, mocker) -> None:
|
||||
assert telegram._config == default_conf
|
||||
|
||||
|
||||
def test_init(default_conf, mocker, caplog) -> None:
|
||||
def test_telegram_init(default_conf, mocker, caplog) -> None:
|
||||
start_polling = MagicMock()
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock(return_value=start_polling))
|
||||
|
||||
@@ -72,10 +73,10 @@ def test_init(default_conf, mocker, caplog) -> None:
|
||||
assert start_polling.start_polling.call_count == 1
|
||||
|
||||
message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], "
|
||||
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], "
|
||||
"['performance'], ['daily'], ['count'], ['reload_config', 'reload_conf'], "
|
||||
"['show_config', 'show_conf'], ['stopbuy'], ['whitelist'], ['blacklist'], "
|
||||
"['edge'], ['help'], ['version']]")
|
||||
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], ['trades'], "
|
||||
"['delete'], ['performance'], ['daily'], ['count'], ['reload_config', "
|
||||
"'reload_conf'], ['show_config', 'show_conf'], ['stopbuy'], "
|
||||
"['whitelist'], ['blacklist'], ['edge'], ['help'], ['version']]")
|
||||
|
||||
assert log_has(message_str, caplog)
|
||||
|
||||
@@ -690,8 +691,8 @@ def test_reload_config_handle(default_conf, update, mocker) -> None:
|
||||
assert 'reloading config' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_forcesell_handle(default_conf, update, ticker, fee,
|
||||
ticker_sell_up, mocker) -> None:
|
||||
def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
|
||||
ticker_sell_up, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
@@ -730,7 +731,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'limit': 1.173e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.173e-05,
|
||||
@@ -744,8 +745,8 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
||||
} == last_msg
|
||||
|
||||
|
||||
def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
ticker_sell_down, mocker) -> None:
|
||||
def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
ticker_sell_down, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
|
||||
return_value=15000.0)
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
@@ -790,7 +791,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'limit': 1.043e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.043e-05,
|
||||
@@ -839,7 +840,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'limit': 1.099e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.099e-05,
|
||||
@@ -1146,6 +1147,63 @@ def test_edge_enabled(edge_conf, update, mocker) -> None:
|
||||
assert 'Pair Winrate Expectancy Stoploss' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_telegram_trades(mocker, update, default_conf, fee):
|
||||
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)
|
||||
context = MagicMock()
|
||||
context.args = []
|
||||
|
||||
telegram._trades(update=update, context=context)
|
||||
assert "<b>0 recent trades</b>:" in msg_mock.call_args_list[0][0][0]
|
||||
assert "<pre>" not in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
msg_mock.reset_mock()
|
||||
create_mock_trades(fee)
|
||||
|
||||
context = MagicMock()
|
||||
context.args = [5]
|
||||
telegram._trades(update=update, context=context)
|
||||
msg_mock.call_count == 1
|
||||
assert "2 recent trades</b>:" in msg_mock.call_args_list[0][0][0]
|
||||
assert "Profit (" in msg_mock.call_args_list[0][0][0]
|
||||
assert "Open Date" in msg_mock.call_args_list[0][0][0]
|
||||
assert "<pre>" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_telegram_delete_trade(mocker, update, default_conf, fee):
|
||||
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)
|
||||
context = MagicMock()
|
||||
context.args = []
|
||||
|
||||
telegram._delete_trade(update=update, context=context)
|
||||
assert "invalid argument" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
msg_mock.reset_mock()
|
||||
create_mock_trades(fee)
|
||||
|
||||
context = MagicMock()
|
||||
context.args = [1]
|
||||
telegram._delete_trade(update=update, context=context)
|
||||
msg_mock.call_count == 1
|
||||
assert "Deleted trade 1." in msg_mock.call_args_list[0][0][0]
|
||||
assert "Please make sure to take care of this asset" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_help_handle(default_conf, update, mocker) -> None:
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
|
@@ -2,7 +2,8 @@
|
||||
# Test Documentation boxes -
|
||||
# !!! <TYPE>: is not allowed!
|
||||
# !!! <TYPE> "title" - Title needs to be quoted!
|
||||
grep -Er '^!{3}\s\S+:|^!{3}\s\S+\s[^"]' docs/*
|
||||
# !!! <TYPE> Spaces at the beginning are not allowed
|
||||
grep -Er '^!{3}\s\S+:|^!{3}\s\S+\s[^"]|^\s+!{3}\s\S+' docs/*
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Docs test success."
|
||||
|
@@ -595,7 +595,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
|
||||
|
||||
freqtrade.create_trade('ETH/BTC')
|
||||
rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount']
|
||||
assert rate * amount >= default_conf['stake_amount']
|
||||
assert rate * amount <= default_conf['stake_amount']
|
||||
|
||||
|
||||
def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order,
|
||||
@@ -782,7 +782,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
|
||||
assert trade.open_date is not None
|
||||
assert trade.exchange == 'bittrex'
|
||||
assert trade.open_rate == 0.00001098
|
||||
assert trade.amount == 91.07468123861567
|
||||
assert trade.amount == 91.07468123
|
||||
|
||||
assert log_has(
|
||||
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
|
||||
@@ -1009,7 +1009,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
|
||||
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
|
||||
assert call_args['amount'] == round(stake_amount / bid, 8)
|
||||
buy_rate_mock.reset_mock()
|
||||
|
||||
# Should create an open trade with an open order id
|
||||
@@ -1029,7 +1029,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
|
||||
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
|
||||
assert call_args['amount'] == round(stake_amount / fix_price, 8)
|
||||
|
||||
# In case of closed order
|
||||
limit_buy_order['status'] = 'closed'
|
||||
@@ -1301,7 +1301,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee,
|
||||
freqtrade.enter_positions()
|
||||
trade = Trade.query.first()
|
||||
caplog.clear()
|
||||
freqtrade.create_stoploss_order(trade, 200, 199)
|
||||
freqtrade.create_stoploss_order(trade, 200)
|
||||
assert trade.stoploss_order_id is None
|
||||
assert trade.sell_reason == SellType.EMERGENCY_SELL.value
|
||||
assert log_has("Unable to place a stoploss order on exchange. ", caplog)
|
||||
@@ -1407,7 +1407,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
||||
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.32423208191126,
|
||||
stoploss_order_mock.assert_called_once_with(amount=85.32423208,
|
||||
pair='ETH/BTC',
|
||||
order_types=freqtrade.strategy.order_types,
|
||||
stop_price=0.00002346 * 0.95)
|
||||
@@ -1595,7 +1595,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
|
||||
# stoploss should be set to 1% as trailing is on
|
||||
assert trade.stop_loss == 0.00002346 * 0.99
|
||||
cancel_order_mock.assert_called_once_with(100, 'NEO/BTC')
|
||||
stoploss_order_mock.assert_called_once_with(amount=2132892.491467577,
|
||||
stoploss_order_mock.assert_called_once_with(amount=2132892.49146757,
|
||||
pair='NEO/BTC',
|
||||
order_types=freqtrade.strategy.order_types,
|
||||
stop_price=0.00002346 * 0.99)
|
||||
@@ -1660,6 +1660,7 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog)
|
||||
trade = MagicMock()
|
||||
trade.open_order_id = None
|
||||
trade.open_fee = 0.001
|
||||
trade.pair = 'ETH/BTC'
|
||||
trades = [trade]
|
||||
|
||||
# Test raise of DependencyException exception
|
||||
@@ -1669,7 +1670,7 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog)
|
||||
)
|
||||
n = freqtrade.exit_positions(trades)
|
||||
assert n == 0
|
||||
assert log_has('Unable to sell trade: ', caplog)
|
||||
assert log_has('Unable to sell trade ETH/BTC: ', caplog)
|
||||
|
||||
|
||||
def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None:
|
||||
@@ -1726,6 +1727,7 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_
|
||||
amount=amount,
|
||||
exchange='binance',
|
||||
open_rate=0.245441,
|
||||
open_date=arrow.utcnow().datetime,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_order_id="123456",
|
||||
@@ -1816,6 +1818,7 @@ def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_orde
|
||||
open_rate=0.245441,
|
||||
fee_open=0.0025,
|
||||
fee_close=0.0025,
|
||||
open_date=arrow.utcnow().datetime,
|
||||
open_order_id="123456",
|
||||
is_open=True,
|
||||
)
|
||||
@@ -2023,11 +2026,16 @@ def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_or
|
||||
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
cancel_order_mock = MagicMock(return_value=limit_buy_order_old)
|
||||
cancel_buy_order = deepcopy(limit_buy_order_old)
|
||||
cancel_buy_order['status'] = 'canceled'
|
||||
cancel_order_wr_mock = MagicMock(return_value=cancel_buy_order)
|
||||
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
fetch_order=MagicMock(return_value=limit_buy_order_old),
|
||||
cancel_order_with_result=cancel_order_wr_mock,
|
||||
cancel_order=cancel_order_mock,
|
||||
get_fee=fee
|
||||
)
|
||||
@@ -2060,7 +2068,7 @@ def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_or
|
||||
freqtrade.strategy.check_buy_timeout = MagicMock(return_value=True)
|
||||
# Trade should be closed since the function returns true
|
||||
freqtrade.check_handle_timedout()
|
||||
assert cancel_order_mock.call_count == 1
|
||||
assert cancel_order_wr_mock.call_count == 1
|
||||
assert rpc_mock.call_count == 1
|
||||
trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
|
||||
nb_trades = len(trades)
|
||||
@@ -2071,7 +2079,9 @@ def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_or
|
||||
def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, open_trade,
|
||||
fee, mocker) -> None:
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
cancel_order_mock = MagicMock(return_value=limit_buy_order_old)
|
||||
limit_buy_cancel = deepcopy(limit_buy_order_old)
|
||||
limit_buy_cancel['status'] = 'canceled'
|
||||
cancel_order_mock = MagicMock(return_value=limit_buy_cancel)
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -2259,7 +2269,10 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old,
|
||||
def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial,
|
||||
open_trade, mocker) -> None:
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
cancel_order_mock = MagicMock(return_value=limit_buy_order_old_partial)
|
||||
limit_buy_canceled = deepcopy(limit_buy_order_old_partial)
|
||||
limit_buy_canceled['status'] = 'canceled'
|
||||
|
||||
cancel_order_mock = MagicMock(return_value=limit_buy_canceled)
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -2392,7 +2405,11 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke
|
||||
def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
cancel_order_mock = MagicMock(return_value=limit_buy_order)
|
||||
cancel_buy_order = deepcopy(limit_buy_order)
|
||||
cancel_buy_order['status'] = 'canceled'
|
||||
del cancel_buy_order['filled']
|
||||
|
||||
cancel_order_mock = MagicMock(return_value=cancel_buy_order)
|
||||
mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock)
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
@@ -2412,9 +2429,12 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non
|
||||
assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason)
|
||||
assert cancel_order_mock.call_count == 1
|
||||
|
||||
limit_buy_order['filled'] = 2
|
||||
mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException)
|
||||
# Order remained open for some reason (cancel failed)
|
||||
cancel_buy_order['status'] = 'open'
|
||||
cancel_order_mock = MagicMock(return_value=cancel_buy_order)
|
||||
mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock)
|
||||
assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason)
|
||||
assert log_has_re(r"Order .* for .* not cancelled.", caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
|
||||
@@ -2578,7 +2598,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'limit': 1.172e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.173e-05,
|
||||
@@ -2628,7 +2648,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'limit': 1.044e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.043e-05,
|
||||
@@ -2685,7 +2705,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'limit': 1.08801e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.043e-05,
|
||||
@@ -2891,7 +2911,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'limit': 1.172e-05,
|
||||
'amount': 91.07468123861567,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'market',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.173e-05,
|
||||
@@ -4087,14 +4107,14 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order,
|
||||
def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order, limit_sell_order):
|
||||
default_conf['cancel_open_orders_on_exit'] = True
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_order',
|
||||
side_effect=[DependencyException(), limit_sell_order, limit_buy_order])
|
||||
side_effect=[ExchangeError(), limit_sell_order, limit_buy_order])
|
||||
buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_buy')
|
||||
sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_sell')
|
||||
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
create_mock_trades(fee)
|
||||
trades = Trade.query.all()
|
||||
assert len(trades) == 3
|
||||
assert len(trades) == 4
|
||||
freqtrade.cancel_all_open_orders()
|
||||
assert buy_mock.call_count == 1
|
||||
assert sell_mock.call_count == 1
|
||||
|
@@ -11,7 +11,7 @@ from freqtrade.misc import (datesarray_to_datetimearray, file_dump_json,
|
||||
file_load_json, format_ms_time, pair_to_filename,
|
||||
plural, render_template,
|
||||
render_template_with_fallback, safe_value_fallback,
|
||||
shorten_date)
|
||||
safe_value_fallback2, shorten_date)
|
||||
|
||||
|
||||
def test_shorten_date() -> None:
|
||||
@@ -96,24 +96,40 @@ def test_format_ms_time() -> None:
|
||||
|
||||
|
||||
def test_safe_value_fallback():
|
||||
dict1 = {'keya': None, 'keyb': 2, 'keyc': 5, 'keyd': None}
|
||||
assert safe_value_fallback(dict1, 'keya', 'keyb') == 2
|
||||
assert safe_value_fallback(dict1, 'keyb', 'keya') == 2
|
||||
|
||||
assert safe_value_fallback(dict1, 'keyb', 'keyc') == 2
|
||||
assert safe_value_fallback(dict1, 'keya', 'keyc') == 5
|
||||
|
||||
assert safe_value_fallback(dict1, 'keyc', 'keyb') == 5
|
||||
|
||||
assert safe_value_fallback(dict1, 'keya', 'keyd') is None
|
||||
|
||||
assert safe_value_fallback(dict1, 'keyNo', 'keyNo') is None
|
||||
assert safe_value_fallback(dict1, 'keyNo', 'keyNo', 55) == 55
|
||||
|
||||
|
||||
def test_safe_value_fallback2():
|
||||
dict1 = {'keya': None, 'keyb': 2, 'keyc': 5, 'keyd': None}
|
||||
dict2 = {'keya': 20, 'keyb': None, 'keyc': 6, 'keyd': None}
|
||||
assert safe_value_fallback(dict1, dict2, 'keya', 'keya') == 20
|
||||
assert safe_value_fallback(dict2, dict1, 'keya', 'keya') == 20
|
||||
assert safe_value_fallback2(dict1, dict2, 'keya', 'keya') == 20
|
||||
assert safe_value_fallback2(dict2, dict1, 'keya', 'keya') == 20
|
||||
|
||||
assert safe_value_fallback(dict1, dict2, 'keyb', 'keyb') == 2
|
||||
assert safe_value_fallback(dict2, dict1, 'keyb', 'keyb') == 2
|
||||
assert safe_value_fallback2(dict1, dict2, 'keyb', 'keyb') == 2
|
||||
assert safe_value_fallback2(dict2, dict1, 'keyb', 'keyb') == 2
|
||||
|
||||
assert safe_value_fallback(dict1, dict2, 'keyc', 'keyc') == 5
|
||||
assert safe_value_fallback(dict2, dict1, 'keyc', 'keyc') == 6
|
||||
assert safe_value_fallback2(dict1, dict2, 'keyc', 'keyc') == 5
|
||||
assert safe_value_fallback2(dict2, dict1, 'keyc', 'keyc') == 6
|
||||
|
||||
assert safe_value_fallback(dict1, dict2, 'keyd', 'keyd') is None
|
||||
assert safe_value_fallback(dict2, dict1, 'keyd', 'keyd') is None
|
||||
assert safe_value_fallback(dict2, dict1, 'keyd', 'keyd', 1234) == 1234
|
||||
assert safe_value_fallback2(dict1, dict2, 'keyd', 'keyd') is None
|
||||
assert safe_value_fallback2(dict2, dict1, 'keyd', 'keyd') is None
|
||||
assert safe_value_fallback2(dict2, dict1, 'keyd', 'keyd', 1234) == 1234
|
||||
|
||||
assert safe_value_fallback(dict1, dict2, 'keyNo', 'keyNo') is None
|
||||
assert safe_value_fallback(dict2, dict1, 'keyNo', 'keyNo') is None
|
||||
assert safe_value_fallback(dict2, dict1, 'keyNo', 'keyNo', 1234) == 1234
|
||||
assert safe_value_fallback2(dict1, dict2, 'keyNo', 'keyNo') is None
|
||||
assert safe_value_fallback2(dict2, dict1, 'keyNo', 'keyNo') is None
|
||||
assert safe_value_fallback2(dict2, dict1, 'keyNo', 'keyNo', 1234) == 1234
|
||||
|
||||
|
||||
def test_plural() -> None:
|
||||
|
@@ -457,6 +457,7 @@ def test_migrate_old(mocker, default_conf, fee):
|
||||
assert trade.close_rate_requested is None
|
||||
assert trade.is_open == 1
|
||||
assert trade.amount == amount
|
||||
assert trade.amount_requested == amount
|
||||
assert trade.stake_amount == default_conf.get("stake_amount")
|
||||
assert trade.pair == "ETC/BTC"
|
||||
assert trade.exchange == "bittrex"
|
||||
@@ -546,6 +547,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
|
||||
assert trade.close_rate_requested is None
|
||||
assert trade.is_open == 1
|
||||
assert trade.amount == amount
|
||||
assert trade.amount_requested == amount
|
||||
assert trade.stake_amount == default_conf.get("stake_amount")
|
||||
assert trade.pair == "ETC/BTC"
|
||||
assert trade.exchange == "binance"
|
||||
@@ -725,6 +727,7 @@ def test_to_json(default_conf, fee):
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
amount_requested=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_date=arrow.utcnow().shift(hours=-2).datetime,
|
||||
@@ -757,6 +760,7 @@ def test_to_json(default_conf, fee):
|
||||
'close_rate': None,
|
||||
'close_rate_requested': None,
|
||||
'amount': 123.0,
|
||||
'amount_requested': 123.0,
|
||||
'stake_amount': 0.001,
|
||||
'close_profit': None,
|
||||
'close_profit_abs': None,
|
||||
@@ -786,6 +790,7 @@ def test_to_json(default_conf, fee):
|
||||
pair='XRP/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=100.0,
|
||||
amount_requested=101.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_date=arrow.utcnow().shift(hours=-2).datetime,
|
||||
@@ -808,6 +813,7 @@ def test_to_json(default_conf, fee):
|
||||
'open_rate': 0.123,
|
||||
'close_rate': 0.125,
|
||||
'amount': 100.0,
|
||||
'amount_requested': 101.0,
|
||||
'stake_amount': 0.001,
|
||||
'stop_loss': None,
|
||||
'stop_loss_abs': None,
|
||||
@@ -989,7 +995,7 @@ def test_get_overall_performance(fee):
|
||||
create_mock_trades(fee)
|
||||
res = Trade.get_overall_performance()
|
||||
|
||||
assert len(res) == 1
|
||||
assert len(res) == 2
|
||||
assert 'pair' in res[0]
|
||||
assert 'profit' in res[0]
|
||||
assert 'count' in res[0]
|
||||
@@ -1004,5 +1010,5 @@ def test_get_best_pair(fee):
|
||||
create_mock_trades(fee)
|
||||
res = Trade.get_best_pair()
|
||||
assert len(res) == 2
|
||||
assert res[0] == 'ETC/BTC'
|
||||
assert res[1] == 0.005
|
||||
assert res[0] == 'XRP/BTC'
|
||||
assert res[1] == 0.01
|
||||
|
Reference in New Issue
Block a user