diff --git a/tests/rpc/test_fiat_convert.py b/tests/rpc/test_fiat_convert.py
index 717866cfd..049716414 100644
--- a/tests/rpc/test_fiat_convert.py
+++ b/tests/rpc/test_fiat_convert.py
@@ -13,10 +13,10 @@ from tests.conftest import log_has, log_has_re
def test_fiat_convert_is_supported(mocker):
fiat_convert = CryptoToFiatConverter()
- assert fiat_convert._is_supported_fiat(fiat='USD') is True
- assert fiat_convert._is_supported_fiat(fiat='usd') is True
- assert fiat_convert._is_supported_fiat(fiat='abc') is False
- assert fiat_convert._is_supported_fiat(fiat='ABC') is False
+ assert fiat_convert._is_supported_fiat(fiat="USD") is True
+ assert fiat_convert._is_supported_fiat(fiat="usd") is True
+ assert fiat_convert._is_supported_fiat(fiat="abc") is False
+ assert fiat_convert._is_supported_fiat(fiat="ABC") is False
def test_fiat_convert_find_price(mocker):
@@ -24,69 +24,72 @@ def test_fiat_convert_find_price(mocker):
fiat_convert._coinlistings = {}
fiat_convert._backoff = 0
- mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._load_cryptomap',
- return_value=None)
- assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='EUR') == 0.0
+ mocker.patch(
+ "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._load_cryptomap", return_value=None
+ )
+ assert fiat_convert.get_price(crypto_symbol="BTC", fiat_symbol="EUR") == 0.0
- with pytest.raises(ValueError, match=r'The fiat ABC is not supported.'):
- fiat_convert._find_price(crypto_symbol='BTC', fiat_symbol='ABC')
+ with pytest.raises(ValueError, match=r"The fiat ABC is not supported."):
+ fiat_convert._find_price(crypto_symbol="BTC", fiat_symbol="ABC")
- assert fiat_convert.get_price(crypto_symbol='XRP', fiat_symbol='USD') == 0.0
+ assert fiat_convert.get_price(crypto_symbol="XRP", fiat_symbol="USD") == 0.0
- mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
- return_value=12345.0)
- assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 12345.0
- assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 12345.0
+ mocker.patch(
+ "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price", return_value=12345.0
+ )
+ assert fiat_convert.get_price(crypto_symbol="BTC", fiat_symbol="USD") == 12345.0
+ assert fiat_convert.get_price(crypto_symbol="btc", fiat_symbol="usd") == 12345.0
- mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
- return_value=13000.2)
- assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='EUR') == 13000.2
+ mocker.patch(
+ "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price", return_value=13000.2
+ )
+ assert fiat_convert.get_price(crypto_symbol="BTC", fiat_symbol="EUR") == 13000.2
def test_fiat_convert_unsupported_crypto(mocker, caplog):
- mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._coinlistings', return_value=[])
+ mocker.patch("freqtrade.rpc.fiat_convert.CryptoToFiatConverter._coinlistings", return_value=[])
fiat_convert = CryptoToFiatConverter()
- assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0
- assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog)
+ assert fiat_convert._find_price(crypto_symbol="CRYPTO_123", fiat_symbol="EUR") == 0.0
+ assert log_has("unsupported crypto-symbol CRYPTO_123 - returning 0.0", caplog)
def test_fiat_convert_get_price(mocker):
- find_price = mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
- return_value=28000.0)
+ find_price = mocker.patch(
+ "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price", return_value=28000.0
+ )
fiat_convert = CryptoToFiatConverter()
- with pytest.raises(ValueError, match=r'The fiat us dollar is not supported.'):
- fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='US Dollar')
+ with pytest.raises(ValueError, match=r"The fiat us dollar is not supported."):
+ fiat_convert.get_price(crypto_symbol="btc", fiat_symbol="US Dollar")
# Check the value return by the method
pair_len = len(fiat_convert._pair_price)
assert pair_len == 0
- assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 28000.0
- assert fiat_convert._pair_price['btc/usd'] == 28000.0
+ assert fiat_convert.get_price(crypto_symbol="btc", fiat_symbol="usd") == 28000.0
+ assert fiat_convert._pair_price["btc/usd"] == 28000.0
assert len(fiat_convert._pair_price) == 1
assert find_price.call_count == 1
# Verify the cached is used
- fiat_convert._pair_price['btc/usd'] = 9867.543
- assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 9867.543
+ fiat_convert._pair_price["btc/usd"] = 9867.543
+ assert fiat_convert.get_price(crypto_symbol="btc", fiat_symbol="usd") == 9867.543
assert find_price.call_count == 1
def test_fiat_convert_same_currencies(mocker):
fiat_convert = CryptoToFiatConverter()
- assert fiat_convert.get_price(crypto_symbol='USD', fiat_symbol='USD') == 1.0
+ assert fiat_convert.get_price(crypto_symbol="USD", fiat_symbol="USD") == 1.0
def test_fiat_convert_two_FIAT(mocker):
fiat_convert = CryptoToFiatConverter()
- assert fiat_convert.get_price(crypto_symbol='USD', fiat_symbol='EUR') == 0.0
+ assert fiat_convert.get_price(crypto_symbol="USD", fiat_symbol="EUR") == 0.0
def test_loadcryptomap(mocker):
-
fiat_convert = CryptoToFiatConverter()
assert len(fiat_convert._coinlistings) == 2
@@ -97,7 +100,7 @@ def test_fiat_init_network_exception(mocker):
# Because CryptoToFiatConverter is a Singleton we reset the listings
listmock = MagicMock(side_effect=RequestException)
mocker.patch.multiple(
- 'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
+ "freqtrade.rpc.fiat_convert.CoinGeckoAPI",
get_coins_list=listmock,
)
# with pytest.raises(RequestEsxception):
@@ -117,7 +120,7 @@ def test_fiat_convert_without_network(mocker):
CryptoToFiatConverter._coingecko = None
assert fiat_convert._coingecko is None
- assert fiat_convert._find_price(crypto_symbol='btc', fiat_symbol='usd') == 0.0
+ assert fiat_convert._find_price(crypto_symbol="btc", fiat_symbol="usd") == 0.0
CryptoToFiatConverter._coingecko = cmc_temp
@@ -126,7 +129,7 @@ def test_fiat_too_many_requests_response(mocker, caplog):
req_exception = "429 Too Many Requests"
listmock = MagicMock(return_value="{}", side_effect=RequestException(req_exception))
mocker.patch.multiple(
- 'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
+ "freqtrade.rpc.fiat_convert.CoinGeckoAPI",
get_coins_list=listmock,
)
# with pytest.raises(RequestEsxception):
@@ -137,33 +140,32 @@ def test_fiat_too_many_requests_response(mocker, caplog):
assert len(fiat_convert._coinlistings) == 0
assert fiat_convert._backoff > datetime.datetime.now().timestamp()
assert log_has(
- 'Too many requests for CoinGecko API, backing off and trying again later.',
- caplog
+ "Too many requests for CoinGecko API, backing off and trying again later.", caplog
)
def test_fiat_multiple_coins(mocker, caplog):
fiat_convert = CryptoToFiatConverter()
fiat_convert._coinlistings = [
- {'id': 'helium', 'symbol': 'hnt', 'name': 'Helium'},
- {'id': 'hymnode', 'symbol': 'hnt', 'name': 'Hymnode'},
- {'id': 'bitcoin', 'symbol': 'btc', 'name': 'Bitcoin'},
- {'id': 'ethereum', 'symbol': 'eth', 'name': 'Ethereum'},
- {'id': 'ethereum-wormhole', 'symbol': 'eth', 'name': 'Ethereum Wormhole'},
+ {"id": "helium", "symbol": "hnt", "name": "Helium"},
+ {"id": "hymnode", "symbol": "hnt", "name": "Hymnode"},
+ {"id": "bitcoin", "symbol": "btc", "name": "Bitcoin"},
+ {"id": "ethereum", "symbol": "eth", "name": "Ethereum"},
+ {"id": "ethereum-wormhole", "symbol": "eth", "name": "Ethereum Wormhole"},
]
- assert fiat_convert._get_gecko_id('btc') == 'bitcoin'
- assert fiat_convert._get_gecko_id('hnt') is None
- assert fiat_convert._get_gecko_id('eth') == 'ethereum'
+ assert fiat_convert._get_gecko_id("btc") == "bitcoin"
+ assert fiat_convert._get_gecko_id("hnt") is None
+ assert fiat_convert._get_gecko_id("eth") == "ethereum"
- assert log_has('Found multiple mappings in CoinGecko for hnt.', caplog)
+ assert log_has("Found multiple mappings in CoinGecko for hnt.", caplog)
def test_fiat_invalid_response(mocker, caplog):
# Because CryptoToFiatConverter is a Singleton we reset the listings
listmock = MagicMock(return_value=None)
mocker.patch.multiple(
- 'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
+ "freqtrade.rpc.fiat_convert.CoinGeckoAPI",
get_coins_list=listmock,
)
# with pytest.raises(RequestEsxception):
@@ -172,31 +174,22 @@ def test_fiat_invalid_response(mocker, caplog):
fiat_convert._load_cryptomap()
assert len(fiat_convert._coinlistings) == 0
- assert log_has_re('Could not load FIAT Cryptocurrency map for the following problem: .*',
- caplog)
+ assert log_has_re(
+ "Could not load FIAT Cryptocurrency map for the following problem: .*", caplog
+ )
def test_convert_amount(mocker):
- mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter.get_price', return_value=12345.0)
+ mocker.patch("freqtrade.rpc.fiat_convert.CryptoToFiatConverter.get_price", return_value=12345.0)
fiat_convert = CryptoToFiatConverter()
- result = fiat_convert.convert_amount(
- crypto_amount=1.23,
- crypto_symbol="BTC",
- fiat_symbol="USD"
- )
+ result = fiat_convert.convert_amount(crypto_amount=1.23, crypto_symbol="BTC", fiat_symbol="USD")
assert result == 15184.35
- result = fiat_convert.convert_amount(
- crypto_amount=1.23,
- crypto_symbol="BTC",
- fiat_symbol="BTC"
- )
+ result = fiat_convert.convert_amount(crypto_amount=1.23, crypto_symbol="BTC", fiat_symbol="BTC")
assert result == 1.23
result = fiat_convert.convert_amount(
- crypto_amount="1.23",
- crypto_symbol="BTC",
- fiat_symbol="BTC"
+ crypto_amount="1.23", crypto_symbol="BTC", fiat_symbol="BTC"
)
assert result == 1.23
diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py
index a231cb06a..d421ba556 100644
--- a/tests/rpc/test_rpc.py
+++ b/tests/rpc/test_rpc.py
@@ -24,92 +24,108 @@ from tests.conftest import (
def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
gen_response = {
- 'trade_id': 1,
- 'pair': 'ETH/BTC',
- 'base_currency': 'ETH',
- 'quote_currency': 'BTC',
- 'open_date': ANY,
- 'open_timestamp': ANY,
- 'open_fill_date': ANY,
- 'open_fill_timestamp': ANY,
- 'is_open': ANY,
- 'fee_open': ANY,
- 'fee_open_cost': ANY,
- 'fee_open_currency': ANY,
- 'fee_close': fee.return_value,
- 'fee_close_cost': ANY,
- 'fee_close_currency': ANY,
- 'open_rate_requested': ANY,
- 'open_trade_value': 0.0010025,
- 'close_rate_requested': ANY,
- 'exit_reason': ANY,
- 'exit_order_status': ANY,
- 'min_rate': ANY,
- 'max_rate': ANY,
- 'strategy': ANY,
- 'enter_tag': ANY,
- 'timeframe': 5,
- 'close_date': None,
- 'close_timestamp': None,
- 'open_rate': 1.098e-05,
- 'close_rate': None,
- 'current_rate': 1.099e-05,
- 'amount': 91.07468123,
- 'amount_requested': 91.07468124,
- 'stake_amount': 0.001,
- 'max_stake_amount': None,
- 'trade_duration': None,
- 'trade_duration_s': None,
- 'close_profit': None,
- 'close_profit_pct': None,
- 'close_profit_abs': None,
- 'profit_ratio': -0.00408133,
- 'profit_pct': -0.41,
- 'profit_abs': -4.09e-06,
- 'profit_fiat': ANY,
- 'stop_loss_abs': 9.89e-06,
- 'stop_loss_pct': -10.0,
- 'stop_loss_ratio': -0.1,
- 'stoploss_last_update': ANY,
- 'stoploss_last_update_timestamp': ANY,
- 'initial_stop_loss_abs': 9.89e-06,
- 'initial_stop_loss_pct': -10.0,
- 'initial_stop_loss_ratio': -0.1,
- 'stoploss_current_dist': pytest.approx(-1.0999999e-06),
- 'stoploss_current_dist_ratio': -0.10009099,
- 'stoploss_current_dist_pct': -10.01,
- 'stoploss_entry_dist': -0.00010402,
- 'stoploss_entry_dist_ratio': -0.10376381,
- 'open_orders': '',
- 'realized_profit': 0.0,
- 'realized_profit_ratio': None,
- 'total_profit_abs': -4.09e-06,
- 'total_profit_fiat': ANY,
- 'total_profit_ratio': None,
- 'exchange': 'binance',
- 'leverage': 1.0,
- 'interest_rate': 0.0,
- 'liquidation_price': None,
- 'is_short': False,
- 'funding_fees': 0.0,
- 'trading_mode': TradingMode.SPOT,
- 'amount_precision': 8.0,
- 'price_precision': 8.0,
- 'precision_mode': 2,
- 'contract_size': 1,
- 'has_open_orders': False,
- 'orders': [{
- 'amount': 91.07468123, 'average': 1.098e-05, 'safe_price': 1.098e-05,
- 'cost': 0.0009999999999054, 'filled': 91.07468123, 'ft_order_side': 'buy',
- 'order_date': ANY, 'order_timestamp': ANY, 'order_filled_date': ANY,
- 'order_filled_timestamp': ANY, 'order_type': 'limit', 'price': 1.098e-05,
- 'is_open': False, 'pair': 'ETH/BTC', 'order_id': ANY,
- 'remaining': ANY, 'status': ANY, 'ft_is_entry': True, 'ft_fee_base': None,
- 'funding_fee': ANY, 'ft_order_tag': None,
- }],
+ "trade_id": 1,
+ "pair": "ETH/BTC",
+ "base_currency": "ETH",
+ "quote_currency": "BTC",
+ "open_date": ANY,
+ "open_timestamp": ANY,
+ "open_fill_date": ANY,
+ "open_fill_timestamp": ANY,
+ "is_open": ANY,
+ "fee_open": ANY,
+ "fee_open_cost": ANY,
+ "fee_open_currency": ANY,
+ "fee_close": fee.return_value,
+ "fee_close_cost": ANY,
+ "fee_close_currency": ANY,
+ "open_rate_requested": ANY,
+ "open_trade_value": 0.0010025,
+ "close_rate_requested": ANY,
+ "exit_reason": ANY,
+ "exit_order_status": ANY,
+ "min_rate": ANY,
+ "max_rate": ANY,
+ "strategy": ANY,
+ "enter_tag": ANY,
+ "timeframe": 5,
+ "close_date": None,
+ "close_timestamp": None,
+ "open_rate": 1.098e-05,
+ "close_rate": None,
+ "current_rate": 1.099e-05,
+ "amount": 91.07468123,
+ "amount_requested": 91.07468124,
+ "stake_amount": 0.001,
+ "max_stake_amount": None,
+ "trade_duration": None,
+ "trade_duration_s": None,
+ "close_profit": None,
+ "close_profit_pct": None,
+ "close_profit_abs": None,
+ "profit_ratio": -0.00408133,
+ "profit_pct": -0.41,
+ "profit_abs": -4.09e-06,
+ "profit_fiat": ANY,
+ "stop_loss_abs": 9.89e-06,
+ "stop_loss_pct": -10.0,
+ "stop_loss_ratio": -0.1,
+ "stoploss_last_update": ANY,
+ "stoploss_last_update_timestamp": ANY,
+ "initial_stop_loss_abs": 9.89e-06,
+ "initial_stop_loss_pct": -10.0,
+ "initial_stop_loss_ratio": -0.1,
+ "stoploss_current_dist": pytest.approx(-1.0999999e-06),
+ "stoploss_current_dist_ratio": -0.10009099,
+ "stoploss_current_dist_pct": -10.01,
+ "stoploss_entry_dist": -0.00010402,
+ "stoploss_entry_dist_ratio": -0.10376381,
+ "open_orders": "",
+ "realized_profit": 0.0,
+ "realized_profit_ratio": None,
+ "total_profit_abs": -4.09e-06,
+ "total_profit_fiat": ANY,
+ "total_profit_ratio": None,
+ "exchange": "binance",
+ "leverage": 1.0,
+ "interest_rate": 0.0,
+ "liquidation_price": None,
+ "is_short": False,
+ "funding_fees": 0.0,
+ "trading_mode": TradingMode.SPOT,
+ "amount_precision": 8.0,
+ "price_precision": 8.0,
+ "precision_mode": 2,
+ "contract_size": 1,
+ "has_open_orders": False,
+ "orders": [
+ {
+ "amount": 91.07468123,
+ "average": 1.098e-05,
+ "safe_price": 1.098e-05,
+ "cost": 0.0009999999999054,
+ "filled": 91.07468123,
+ "ft_order_side": "buy",
+ "order_date": ANY,
+ "order_timestamp": ANY,
+ "order_filled_date": ANY,
+ "order_filled_timestamp": ANY,
+ "order_type": "limit",
+ "price": 1.098e-05,
+ "is_open": False,
+ "pair": "ETH/BTC",
+ "order_id": ANY,
+ "remaining": ANY,
+ "status": ANY,
+ "ft_is_entry": True,
+ "ft_fee_base": None,
+ "funding_fee": ANY,
+ "ft_order_tag": None,
+ }
+ ],
}
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -121,7 +137,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
rpc = RPC(freqtradebot)
freqtradebot.state = State.RUNNING
- with pytest.raises(RPCException, match=r'.*no active trade*'):
+ with pytest.raises(RPCException, match=r".*no active trade*"):
rpc._rpc_trade_status()
freqtradebot.enter_positions()
@@ -130,20 +146,20 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
results = rpc._rpc_trade_status()
response_unfilled = deepcopy(gen_response)
# Different from "filled" response:
- response_unfilled.update({
- 'amount': 91.07468124,
- 'profit_ratio': 0.0,
- 'profit_pct': 0.0,
- 'profit_abs': 0.0,
- 'total_profit_abs': 0.0,
- 'open_orders': '(limit buy rem=91.07468123)',
- 'has_open_orders': True,
- })
- response_unfilled['orders'][0].update({
- 'is_open': True,
- 'filled': 0.0,
- 'remaining': 91.07468123
- })
+ response_unfilled.update(
+ {
+ "amount": 91.07468124,
+ "profit_ratio": 0.0,
+ "profit_pct": 0.0,
+ "profit_abs": 0.0,
+ "total_profit_abs": 0.0,
+ "open_orders": "(limit buy rem=91.07468123)",
+ "has_open_orders": True,
+ }
+ )
+ response_unfilled["orders"][0].update(
+ {"is_open": True, "filled": 0.0, "remaining": 91.07468123}
+ )
assert results[0] == response_unfilled
# Open order without remaining
@@ -154,9 +170,11 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
results = rpc._rpc_trade_status()
# Reuse above object, only remaining changed.
- response_unfilled['orders'][0].update({
- 'remaining': None,
- })
+ response_unfilled["orders"][0].update(
+ {
+ "remaining": None,
+ }
+ )
assert results[0] == response_unfilled
trade = Trade.get_open_trades()[0]
@@ -171,179 +189,179 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
results = rpc._rpc_trade_status()
response = deepcopy(gen_response)
- response.update({
- 'max_stake_amount': 0.001,
- 'total_profit_ratio': pytest.approx(-0.00409153),
- 'has_open_orders': False,
- })
+ response.update(
+ {
+ "max_stake_amount": 0.001,
+ "total_profit_ratio": pytest.approx(-0.00409153),
+ "has_open_orders": False,
+ }
+ )
assert results[0] == response
- mocker.patch(f'{EXMS}.get_rate',
- MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
+ mocker.patch(
+ f"{EXMS}.get_rate", MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))
+ )
results = rpc._rpc_trade_status()
- assert isnan(results[0]['profit_ratio'])
- assert isnan(results[0]['current_rate'])
+ assert isnan(results[0]["profit_ratio"])
+ assert isnan(results[0]["current_rate"])
response_norate = deepcopy(gen_response)
# Update elements that are NaN when no rate is available.
- response_norate.update({
- 'stoploss_current_dist': ANY,
- 'stoploss_current_dist_ratio': ANY,
- 'stoploss_current_dist_pct': ANY,
- 'max_stake_amount': 0.001,
- 'profit_ratio': ANY,
- 'profit_pct': ANY,
- 'profit_abs': ANY,
- 'total_profit_abs': ANY,
- 'total_profit_ratio': ANY,
- 'current_rate': ANY,
- })
+ response_norate.update(
+ {
+ "stoploss_current_dist": ANY,
+ "stoploss_current_dist_ratio": ANY,
+ "stoploss_current_dist_pct": ANY,
+ "max_stake_amount": 0.001,
+ "profit_ratio": ANY,
+ "profit_pct": ANY,
+ "profit_abs": ANY,
+ "total_profit_abs": ANY,
+ "total_profit_ratio": ANY,
+ "current_rate": ANY,
+ }
+ )
assert results[0] == response_norate
def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
mocker.patch.multiple(
- 'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
- get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
+ "freqtrade.rpc.fiat_convert.CoinGeckoAPI",
+ get_price=MagicMock(return_value={"bitcoin": {"usd": 15000.0}}),
)
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0)
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
get_fee=fee,
)
- del default_conf['fiat_display_currency']
+ del default_conf["fiat_display_currency"]
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
freqtradebot.state = State.RUNNING
- with pytest.raises(RPCException, match=r'.*no active trade*'):
- rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
- mocker.patch(f'{EXMS}._dry_is_price_crossed', return_value=False)
+ with pytest.raises(RPCException, match=r".*no active trade*"):
+ rpc._rpc_status_table(default_conf["stake_currency"], "USD")
+ mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=False)
freqtradebot.enter_positions()
- result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
+ result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD")
assert "Since" in headers
assert "Pair" in headers
- assert 'now' == result[0][2]
- assert 'ETH/BTC' in result[0][1]
- assert '0.00 (0.00)' == result[0][3]
- assert '0.00' == f'{fiat_profit_sum:.2f}'
+ assert "now" == result[0][2]
+ assert "ETH/BTC" in result[0][1]
+ assert "0.00 (0.00)" == result[0][3]
+ assert "0.00" == f"{fiat_profit_sum:.2f}"
- mocker.patch(f'{EXMS}._dry_is_price_crossed', return_value=True)
+ mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=True)
freqtradebot.process()
- result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
+ result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD")
assert "Since" in headers
assert "Pair" in headers
- assert 'now' == result[0][2]
- assert 'ETH/BTC' in result[0][1]
- assert '-0.41% (-0.00)' == result[0][3]
- assert '-0.00' == f'{fiat_profit_sum:.2f}'
+ assert "now" == result[0][2]
+ assert "ETH/BTC" in result[0][1]
+ assert "-0.41% (-0.00)" == result[0][3]
+ assert "-0.00" == f"{fiat_profit_sum:.2f}"
# Test with fiat convert
rpc._fiat_converter = CryptoToFiatConverter()
- result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
+ result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD")
assert "Since" in headers
assert "Pair" in headers
assert len(result[0]) == 4
- assert 'now' == result[0][2]
- assert 'ETH/BTC' in result[0][1]
- assert '-0.41% (-0.06)' == result[0][3]
- assert '-0.06' == f'{fiat_profit_sum:.2f}'
+ assert "now" == result[0][2]
+ assert "ETH/BTC" in result[0][1]
+ assert "-0.41% (-0.06)" == result[0][3]
+ assert "-0.06" == f"{fiat_profit_sum:.2f}"
- rpc._config['position_adjustment_enable'] = True
- rpc._config['max_entry_position_adjustment'] = 3
- result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
+ rpc._config["position_adjustment_enable"] = True
+ rpc._config["max_entry_position_adjustment"] = 3
+ result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD")
assert "# Entries" in headers
assert len(result[0]) == 5
# 4th column should be 1/4 - as 1 order filled (a total of 4 is possible)
# 3 on top of the initial one.
- assert result[0][4] == '1/4'
+ assert result[0][4] == "1/4"
- mocker.patch(f'{EXMS}.get_rate',
- MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
- result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
- assert 'now' == result[0][2]
- assert 'ETH/BTC' in result[0][1]
- assert 'nan%' == result[0][3]
+ mocker.patch(
+ f"{EXMS}.get_rate", MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))
+ )
+ result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD")
+ assert "now" == result[0][2]
+ assert "ETH/BTC" in result[0][1]
+ assert "nan%" == result[0][3]
assert isnan(fiat_profit_sum)
def test__rpc_timeunit_profit(
- default_conf_usdt, ticker, fee, markets, mocker, time_machine) -> None:
-
+ default_conf_usdt, ticker, fee, markets, mocker, time_machine
+) -> None:
time_machine.move_to("2023-09-05 10:00:00 +00:00", tick=False)
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
- EXMS,
- fetch_ticker=ticker,
- get_fee=fee,
- markets=PropertyMock(return_value=markets)
+ EXMS, fetch_ticker=ticker, get_fee=fee, markets=PropertyMock(return_value=markets)
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt)
create_mock_trades_usdt(fee)
- stake_currency = default_conf_usdt['stake_currency']
- fiat_display_currency = default_conf_usdt['fiat_display_currency']
+ stake_currency = default_conf_usdt["stake_currency"]
+ fiat_display_currency = default_conf_usdt["fiat_display_currency"]
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
# Try valid data
days = rpc._rpc_timeunit_profit(7, stake_currency, fiat_display_currency)
- assert len(days['data']) == 7
- assert days['stake_currency'] == default_conf_usdt['stake_currency']
- assert days['fiat_display_currency'] == default_conf_usdt['fiat_display_currency']
- for day in days['data']:
+ assert len(days["data"]) == 7
+ assert days["stake_currency"] == default_conf_usdt["stake_currency"]
+ assert days["fiat_display_currency"] == default_conf_usdt["fiat_display_currency"]
+ for day in days["data"]:
# {'date': datetime.date(2022, 6, 11), 'abs_profit': 13.8299999,
# 'starting_balance': 1055.37, 'rel_profit': 0.0131044,
# 'fiat_value': 0.0, 'trade_count': 2}
- assert day['abs_profit'] in (0.0, pytest.approx(6.83), pytest.approx(-4.09))
- assert day['rel_profit'] in (0.0, pytest.approx(0.00642902), pytest.approx(-0.00383512))
- assert day['trade_count'] in (0, 1, 2)
- assert day['starting_balance'] in (pytest.approx(1062.37), pytest.approx(1066.46))
- assert day['fiat_value'] in (0.0, )
+ assert day["abs_profit"] in (0.0, pytest.approx(6.83), pytest.approx(-4.09))
+ assert day["rel_profit"] in (0.0, pytest.approx(0.00642902), pytest.approx(-0.00383512))
+ assert day["trade_count"] in (0, 1, 2)
+ assert day["starting_balance"] in (pytest.approx(1062.37), pytest.approx(1066.46))
+ assert day["fiat_value"] in (0.0,)
# ensure first day is current date
- assert str(days['data'][0]['date']) == str(datetime.now(timezone.utc).date())
+ assert str(days["data"][0]["date"]) == str(datetime.now(timezone.utc).date())
# Try invalid data
- with pytest.raises(RPCException, match=r'.*must be an integer greater than 0*'):
+ with pytest.raises(RPCException, match=r".*must be an integer greater than 0*"):
rpc._rpc_timeunit_profit(0, stake_currency, fiat_display_currency)
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_rpc_trade_history(mocker, default_conf, markets, fee, is_short):
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
- mocker.patch.multiple(
- EXMS,
- markets=PropertyMock(return_value=markets)
- )
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
+ mocker.patch.multiple(EXMS, markets=PropertyMock(return_value=markets))
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
create_mock_trades(fee, is_short)
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
trades = rpc._rpc_trade_history(2)
- assert len(trades['trades']) == 2
- assert trades['trades_count'] == 2
- assert isinstance(trades['trades'][0], dict)
- assert isinstance(trades['trades'][1], dict)
+ assert len(trades["trades"]) == 2
+ assert trades["trades_count"] == 2
+ assert isinstance(trades["trades"][0], dict)
+ assert isinstance(trades["trades"][1], dict)
trades = rpc._rpc_trade_history(0)
- assert len(trades['trades']) == 2
- assert trades['trades_count'] == 2
+ 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'
+ assert trades["trades"][-1]["pair"] == "ETC/BTC"
+ assert trades["trades"][0]["pair"] == "XRP/BTC"
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short):
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
stoploss_mock = MagicMock()
cancel_mock = MagicMock()
mocker.patch.multiple(
@@ -354,57 +372,57 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short):
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
- freqtradebot.strategy.order_types['stoploss_on_exchange'] = True
+ freqtradebot.strategy.order_types["stoploss_on_exchange"] = True
create_mock_trades(fee, is_short)
rpc = RPC(freqtradebot)
- with pytest.raises(RPCException, match='invalid argument'):
- rpc._rpc_delete('200')
+ with pytest.raises(RPCException, match="invalid argument"):
+ rpc._rpc_delete("200")
trades = Trade.session.scalars(select(Trade)).all()
trades[2].orders.append(
Order(
- ft_order_side='stoploss',
+ ft_order_side="stoploss",
ft_pair=trades[2].pair,
ft_is_open=True,
ft_amount=trades[2].amount,
ft_price=trades[2].stop_loss,
- order_id='102',
- status='open',
+ order_id="102",
+ status="open",
)
)
assert len(trades) > 2
- res = rpc._rpc_delete('1')
+ 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 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('5')
+ res = rpc._rpc_delete("5")
assert isinstance(res, dict)
assert stoploss_mock.call_count == 1
- assert res['cancel_order_count'] == 1
+ assert res["cancel_order_count"] == 1
- stoploss_mock = mocker.patch(f'{EXMS}.cancel_stoploss_order', side_effect=InvalidOrderException)
+ stoploss_mock = mocker.patch(f"{EXMS}.cancel_stoploss_order", side_effect=InvalidOrderException)
- res = rpc._rpc_delete('3')
+ res = rpc._rpc_delete("3")
assert stoploss_mock.call_count == 1
stoploss_mock.reset_mock()
- cancel_mock = mocker.patch(f'{EXMS}.cancel_order', side_effect=InvalidOrderException)
+ cancel_mock = mocker.patch(f"{EXMS}.cancel_order", side_effect=InvalidOrderException)
- res = rpc._rpc_delete('4')
+ res = rpc._rpc_delete("4")
assert cancel_mock.call_count == 1
assert stoploss_mock.call_count == 0
def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=1.1)
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1)
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -412,79 +430,80 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt)
- stake_currency = default_conf_usdt['stake_currency']
- fiat_display_currency = default_conf_usdt['fiat_display_currency']
+ stake_currency = default_conf_usdt["stake_currency"]
+ fiat_display_currency = default_conf_usdt["fiat_display_currency"]
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
res = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
- assert res['trade_count'] == 0
- assert res['first_trade_date'] == ''
- assert res['first_trade_timestamp'] == 0
- assert res['latest_trade_date'] == ''
- assert res['latest_trade_timestamp'] == 0
- assert res['expectancy'] == 0
- assert res['expectancy_ratio'] == 100
+ assert res["trade_count"] == 0
+ assert res["first_trade_date"] == ""
+ assert res["first_trade_timestamp"] == 0
+ assert res["latest_trade_date"] == ""
+ assert res["latest_trade_timestamp"] == 0
+ assert res["expectancy"] == 0
+ assert res["expectancy_ratio"] == 100
# Create some test data
create_mock_trades_usdt(fee)
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
- assert pytest.approx(stats['profit_closed_coin']) == 2.74
- assert pytest.approx(stats['profit_closed_percent_mean']) == -1.67
- assert pytest.approx(stats['profit_closed_fiat']) == 3.014
- assert pytest.approx(stats['profit_all_coin']) == -57.40975881
- assert pytest.approx(stats['profit_all_percent_mean']) == -50.83
- assert pytest.approx(stats['profit_all_fiat']) == -63.150734691
- assert pytest.approx(stats['winrate']) == 0.666666667
- assert pytest.approx(stats['expectancy']) == 0.913333333
- assert pytest.approx(stats['expectancy_ratio']) == 0.223308883
- assert stats['trade_count'] == 7
- assert stats['first_trade_humanized'] == '2 days ago'
- assert stats['latest_trade_humanized'] == '17 minutes ago'
- assert stats['avg_duration'] in ('0:17:40')
- assert stats['best_pair'] == 'XRP/USDT'
- assert stats['best_rate'] == 10.0
+ assert pytest.approx(stats["profit_closed_coin"]) == 2.74
+ assert pytest.approx(stats["profit_closed_percent_mean"]) == -1.67
+ assert pytest.approx(stats["profit_closed_fiat"]) == 3.014
+ assert pytest.approx(stats["profit_all_coin"]) == -57.40975881
+ assert pytest.approx(stats["profit_all_percent_mean"]) == -50.83
+ assert pytest.approx(stats["profit_all_fiat"]) == -63.150734691
+ assert pytest.approx(stats["winrate"]) == 0.666666667
+ assert pytest.approx(stats["expectancy"]) == 0.913333333
+ assert pytest.approx(stats["expectancy_ratio"]) == 0.223308883
+ assert stats["trade_count"] == 7
+ assert stats["first_trade_humanized"] == "2 days ago"
+ assert stats["latest_trade_humanized"] == "17 minutes ago"
+ assert stats["avg_duration"] in ("0:17:40")
+ assert stats["best_pair"] == "XRP/USDT"
+ assert stats["best_rate"] == 10.0
# Test non-available pair
- mocker.patch(f'{EXMS}.get_rate',
- MagicMock(side_effect=ExchangeError("Pair 'XRP/USDT' not available")))
+ mocker.patch(
+ f"{EXMS}.get_rate", MagicMock(side_effect=ExchangeError("Pair 'XRP/USDT' not available"))
+ )
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
- assert stats['trade_count'] == 7
- assert stats['first_trade_humanized'] == '2 days ago'
- assert stats['latest_trade_humanized'] == '17 minutes ago'
- assert stats['avg_duration'] in ('0:17:40')
- assert stats['best_pair'] == 'XRP/USDT'
- assert stats['best_rate'] == 10.0
- assert isnan(stats['profit_all_coin'])
+ assert stats["trade_count"] == 7
+ assert stats["first_trade_humanized"] == "2 days ago"
+ assert stats["latest_trade_humanized"] == "17 minutes ago"
+ assert stats["avg_duration"] in ("0:17:40")
+ assert stats["best_pair"] == "XRP/USDT"
+ assert stats["best_rate"] == 10.0
+ assert isnan(stats["profit_all_coin"])
def test_rpc_balance_handle_error(default_conf, mocker):
mock_balance = {
- 'BTC': {
- 'free': 10.0,
- 'total': 12.0,
- 'used': 2.0,
+ "BTC": {
+ "free": 10.0,
+ "total": 12.0,
+ "used": 2.0,
+ },
+ "ETH": {
+ "free": 1.0,
+ "total": 5.0,
+ "used": 4.0,
},
- 'ETH': {
- 'free': 1.0,
- 'total': 5.0,
- 'used': 4.0,
- }
}
# ETH will be skipped due to mocked Error below
mocker.patch.multiple(
- 'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
- get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
+ "freqtrade.rpc.fiat_convert.CoinGeckoAPI",
+ get_price=MagicMock(return_value={"bitcoin": {"usd": 15000.0}}),
)
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0)
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=mock_balance),
- get_tickers=MagicMock(side_effect=TemporaryError('Could not load ticker due to xxx'))
+ get_tickers=MagicMock(side_effect=TemporaryError("Could not load ticker due to xxx")),
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
@@ -492,26 +511,26 @@ def test_rpc_balance_handle_error(default_conf, mocker):
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
with pytest.raises(RPCException, match="Error getting current tickers."):
- rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
+ rpc._rpc_balance(default_conf["stake_currency"], default_conf["fiat_display_currency"])
def test_rpc_balance_handle(default_conf_usdt, mocker, tickers):
mock_balance = {
- 'BTC': {
- 'free': 0.01,
- 'total': 0.012,
- 'used': 0.002,
+ "BTC": {
+ "free": 0.01,
+ "total": 0.012,
+ "used": 0.002,
},
- 'ETH': {
- 'free': 1.0,
- 'total': 5.0,
- 'used': 4.0,
+ "ETH": {
+ "free": 1.0,
+ "total": 5.0,
+ "used": 4.0,
+ },
+ "USDT": {
+ "free": 50.0,
+ "total": 100.0,
+ "used": 5.0,
},
- 'USDT': {
- 'free': 50.0,
- 'total': 100.0,
- 'used': 5.0,
- }
}
mock_pos = [
{
@@ -533,17 +552,17 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers):
"markPrice": 2896.41,
"collateral": 20,
"marginType": "isolated",
- "side": 'short',
- "percentage": None
+ "side": "short",
+ "percentage": None,
}
]
mocker.patch.multiple(
- 'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
- get_price=MagicMock(return_value={'bitcoin': {'usd': 1.2}}),
+ "freqtrade.rpc.fiat_convert.CoinGeckoAPI",
+ get_price=MagicMock(return_value={"bitcoin": {"usd": 1.2}}),
)
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=1.2)
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.2)
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
validate_trading_mode_and_margin_mode=MagicMock(),
@@ -551,96 +570,95 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers):
fetch_positions=MagicMock(return_value=mock_pos),
get_tickers=tickers,
get_valid_pair_combination=MagicMock(
- side_effect=lambda a, b: f"{b}/{a}" if a == "USDT" else f"{a}/{b}")
+ side_effect=lambda a, b: f"{b}/{a}" if a == "USDT" else f"{a}/{b}"
+ ),
)
- default_conf_usdt['dry_run'] = False
- default_conf_usdt['trading_mode'] = 'futures'
+ default_conf_usdt["dry_run"] = False
+ default_conf_usdt["trading_mode"] = "futures"
freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
result = rpc._rpc_balance(
- default_conf_usdt['stake_currency'], default_conf_usdt['fiat_display_currency'])
+ default_conf_usdt["stake_currency"], default_conf_usdt["fiat_display_currency"]
+ )
- assert pytest.approx(result['total']) == 2824.83464
- assert pytest.approx(result['value']) == 2824.83464 * 1.2
+ assert pytest.approx(result["total"]) == 2824.83464
+ assert pytest.approx(result["value"]) == 2824.83464 * 1.2
assert tickers.call_count == 1
- assert tickers.call_args_list[0][1]['cached'] is True
- assert 'USD' == result['symbol']
- assert result['currencies'] == [
+ assert tickers.call_args_list[0][1]["cached"] is True
+ assert "USD" == result["symbol"]
+ assert result["currencies"] == [
{
- 'currency': 'BTC',
- 'free': 0.01,
- 'balance': 0.012,
- 'used': 0.002,
- 'bot_owned': 0,
- 'est_stake': 103.78464,
- 'est_stake_bot': 0,
- 'stake': 'USDT',
- 'side': 'long',
- 'leverage': 1,
- 'position': 0,
- 'is_bot_managed': False,
- 'is_position': False
+ "currency": "BTC",
+ "free": 0.01,
+ "balance": 0.012,
+ "used": 0.002,
+ "bot_owned": 0,
+ "est_stake": 103.78464,
+ "est_stake_bot": 0,
+ "stake": "USDT",
+ "side": "long",
+ "leverage": 1,
+ "position": 0,
+ "is_bot_managed": False,
+ "is_position": False,
},
{
- 'currency': 'ETH',
- 'free': 1.0,
- 'balance': 5.0,
- 'used': 4.0,
- 'bot_owned': 0,
- 'est_stake': 2651.05,
- 'est_stake_bot': 0,
- 'stake': 'USDT',
- 'side': 'long',
- 'leverage': 1,
- 'position': 0,
- 'is_bot_managed': False,
- 'is_position': False
+ "currency": "ETH",
+ "free": 1.0,
+ "balance": 5.0,
+ "used": 4.0,
+ "bot_owned": 0,
+ "est_stake": 2651.05,
+ "est_stake_bot": 0,
+ "stake": "USDT",
+ "side": "long",
+ "leverage": 1,
+ "position": 0,
+ "is_bot_managed": False,
+ "is_position": False,
},
{
- 'currency': 'USDT',
- 'free': 50.0,
- 'balance': 100.0,
- 'used': 5.0,
- 'bot_owned': 49.5,
- 'est_stake': 50.0,
- 'est_stake_bot': 49.5,
- 'stake': 'USDT',
- 'side': 'long',
- 'leverage': 1,
- 'position': 0,
- 'is_bot_managed': True,
- 'is_position': False
+ "currency": "USDT",
+ "free": 50.0,
+ "balance": 100.0,
+ "used": 5.0,
+ "bot_owned": 49.5,
+ "est_stake": 50.0,
+ "est_stake_bot": 49.5,
+ "stake": "USDT",
+ "side": "long",
+ "leverage": 1,
+ "position": 0,
+ "is_bot_managed": True,
+ "is_position": False,
},
{
- 'currency': 'ETH/USDT:USDT',
- 'free': 0,
- 'balance': 0,
- 'used': 0,
- 'position': 10.0,
- 'est_stake': 20,
- 'est_stake_bot': 20,
- 'stake': 'USDT',
- 'leverage': 5.0,
- 'side': 'short',
- 'is_bot_managed': True,
- 'is_position': True
- }
+ "currency": "ETH/USDT:USDT",
+ "free": 0,
+ "balance": 0,
+ "used": 0,
+ "position": 10.0,
+ "est_stake": 20,
+ "est_stake_bot": 20,
+ "stake": "USDT",
+ "leverage": 5.0,
+ "side": "short",
+ "is_bot_managed": True,
+ "is_position": True,
+ },
]
- assert pytest.approx(result['total_bot']) == 69.5
- assert pytest.approx(result['total']) == 2824.83464 # ETH stake is missing.
- assert result['starting_capital'] == 50
- assert result['starting_capital_ratio'] == pytest.approx(0.3899999)
+ assert pytest.approx(result["total_bot"]) == 69.5
+ assert pytest.approx(result["total"]) == 2824.83464 # ETH stake is missing.
+ assert result["starting_capital"] == 50
+ assert result["starting_capital_ratio"] == pytest.approx(0.3899999)
def test_rpc_start(mocker, default_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
- mocker.patch.multiple(
- EXMS,
- fetch_ticker=MagicMock()
- )
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
+ mocker.patch.multiple(EXMS, fetch_ticker=MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -648,20 +666,17 @@ def test_rpc_start(mocker, default_conf) -> None:
freqtradebot.state = State.STOPPED
result = rpc._rpc_start()
- assert {'status': 'starting trader ...'} == result
+ assert {"status": "starting trader ..."} == result
assert freqtradebot.state == State.RUNNING
result = rpc._rpc_start()
- assert {'status': 'already running'} == result
+ assert {"status": "already running"} == result
assert freqtradebot.state == State.RUNNING
def test_rpc_stop(mocker, default_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
- mocker.patch.multiple(
- EXMS,
- fetch_ticker=MagicMock()
- )
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
+ mocker.patch.multiple(EXMS, fetch_ticker=MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -669,35 +684,32 @@ def test_rpc_stop(mocker, default_conf) -> None:
freqtradebot.state = State.RUNNING
result = rpc._rpc_stop()
- assert {'status': 'stopping trader ...'} == result
+ assert {"status": "stopping trader ..."} == result
assert freqtradebot.state == State.STOPPED
result = rpc._rpc_stop()
- assert {'status': 'already stopped'} == result
+ assert {"status": "already stopped"} == result
assert freqtradebot.state == State.STOPPED
def test_rpc_stopentry(mocker, default_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
- mocker.patch.multiple(
- EXMS,
- fetch_ticker=MagicMock()
- )
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
+ mocker.patch.multiple(EXMS, fetch_ticker=MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
freqtradebot.state = State.RUNNING
- assert freqtradebot.config['max_open_trades'] != 0
+ assert freqtradebot.config["max_open_trades"] != 0
result = rpc._rpc_stopentry()
- assert {'status': 'No more entries will occur from now. Run /reload_config to reset.'} == result
- assert freqtradebot.config['max_open_trades'] == 0
+ assert {"status": "No more entries will occur from now. Run /reload_config to reset."} == result
+ assert freqtradebot.config["max_open_trades"] == 0
def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
cancel_order_mock = MagicMock()
mocker.patch.multiple(
@@ -706,157 +718,151 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
cancel_order=cancel_order_mock,
fetch_order=MagicMock(
return_value={
- 'status': 'closed',
- 'type': 'limit',
- 'side': 'buy',
- 'filled': 0.0,
+ "status": "closed",
+ "type": "limit",
+ "side": "buy",
+ "filled": 0.0,
}
),
_dry_is_price_crossed=MagicMock(return_value=True),
get_fee=fee,
)
- mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000)
+ mocker.patch("freqtrade.wallets.Wallets.get_free", return_value=1000)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
freqtradebot.state = State.STOPPED
- with pytest.raises(RPCException, match=r'.*trader is not running*'):
+ with pytest.raises(RPCException, match=r".*trader is not running*"):
rpc._rpc_force_exit(None)
freqtradebot.state = State.RUNNING
- with pytest.raises(RPCException, match=r'.*invalid argument*'):
+ with pytest.raises(RPCException, match=r".*invalid argument*"):
rpc._rpc_force_exit(None)
- msg = rpc._rpc_force_exit('all')
- assert msg == {'result': 'Created exit orders for all open trades.'}
+ msg = rpc._rpc_force_exit("all")
+ assert msg == {"result": "Created exit orders for all open trades."}
freqtradebot.enter_positions()
- msg = rpc._rpc_force_exit('all')
- assert msg == {'result': 'Created exit orders for all open trades.'}
+ msg = rpc._rpc_force_exit("all")
+ assert msg == {"result": "Created exit orders for all open trades."}
freqtradebot.enter_positions()
- msg = rpc._rpc_force_exit('2')
- assert msg == {'result': 'Created exit order for trade 2.'}
+ msg = rpc._rpc_force_exit("2")
+ assert msg == {"result": "Created exit order for trade 2."}
freqtradebot.state = State.STOPPED
- with pytest.raises(RPCException, match=r'.*trader is not running*'):
+ with pytest.raises(RPCException, match=r".*trader is not running*"):
rpc._rpc_force_exit(None)
- with pytest.raises(RPCException, match=r'.*trader is not running*'):
- rpc._rpc_force_exit('all')
+ with pytest.raises(RPCException, match=r".*trader is not running*"):
+ rpc._rpc_force_exit("all")
freqtradebot.state = State.RUNNING
assert cancel_order_mock.call_count == 0
- mocker.patch(f'{EXMS}._dry_is_price_crossed', MagicMock(return_value=False))
+ mocker.patch(f"{EXMS}._dry_is_price_crossed", MagicMock(return_value=False))
freqtradebot.enter_positions()
# make an limit-buy open trade
- trade = Trade.session.scalars(select(Trade).filter(Trade.id == '3')).first()
+ trade = Trade.session.scalars(select(Trade).filter(Trade.id == "3")).first()
filled_amount = trade.amount / 2
# Fetch order - it's open first, and closed after cancel_order is called.
mocker.patch(
- f'{EXMS}.fetch_order',
- side_effect=[{
- 'id': trade.orders[0].order_id,
- 'status': 'open',
- 'type': 'limit',
- 'side': 'buy',
- 'filled': filled_amount
- }, {
- 'id': trade.orders[0].order_id,
- 'status': 'closed',
- 'type': 'limit',
- 'side': 'buy',
- 'filled': filled_amount
- }]
+ f"{EXMS}.fetch_order",
+ side_effect=[
+ {
+ "id": trade.orders[0].order_id,
+ "status": "open",
+ "type": "limit",
+ "side": "buy",
+ "filled": filled_amount,
+ },
+ {
+ "id": trade.orders[0].order_id,
+ "status": "closed",
+ "type": "limit",
+ "side": "buy",
+ "filled": filled_amount,
+ },
+ ],
)
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
# and trade amount is updated
- rpc._rpc_force_exit('3')
+ rpc._rpc_force_exit("3")
assert cancel_order_mock.call_count == 1
assert pytest.approx(trade.amount) == filled_amount
mocker.patch(
- f'{EXMS}.fetch_order',
- return_value={
- 'status': 'open',
- 'type': 'limit',
- 'side': 'buy',
- 'filled': filled_amount
- })
+ f"{EXMS}.fetch_order",
+ return_value={"status": "open", "type": "limit", "side": "buy", "filled": filled_amount},
+ )
- freqtradebot.config['max_open_trades'] = 3
+ freqtradebot.config["max_open_trades"] = 3
freqtradebot.enter_positions()
cancel_order_mock.reset_mock()
- trade = Trade.session.scalars(select(Trade).filter(Trade.id == '3')).first()
+ trade = Trade.session.scalars(select(Trade).filter(Trade.id == "3")).first()
amount = trade.amount
# make an limit-sell open order trade
mocker.patch(
- f'{EXMS}.fetch_order',
+ f"{EXMS}.fetch_order",
return_value={
- 'status': 'open',
- 'type': 'limit',
- 'side': 'sell',
- 'amount': amount,
- 'remaining': amount,
- 'filled': 0.0,
- 'id': trade.orders[-1].order_id,
- }
+ "status": "open",
+ "type": "limit",
+ "side": "sell",
+ "amount": amount,
+ "remaining": amount,
+ "filled": 0.0,
+ "id": trade.orders[-1].order_id,
+ },
)
cancel_order_3 = mocker.patch(
- f'{EXMS}.cancel_order_with_result',
+ f"{EXMS}.cancel_order_with_result",
return_value={
- 'status': 'canceled',
- 'type': 'limit',
- 'side': 'sell',
- 'amount': amount,
- 'remaining': amount,
- 'filled': 0.0,
- 'id': trade.orders[-1].order_id,
- }
+ "status": "canceled",
+ "type": "limit",
+ "side": "sell",
+ "amount": amount,
+ "remaining": amount,
+ "filled": 0.0,
+ "id": trade.orders[-1].order_id,
+ },
)
- msg = rpc._rpc_force_exit('3')
- assert msg == {'result': 'Created exit order for trade 3.'}
+ msg = rpc._rpc_force_exit("3")
+ assert msg == {"result": "Created exit order for trade 3."}
# status quo, no exchange calls
assert cancel_order_3.call_count == 1
assert cancel_order_mock.call_count == 0
- trade = Trade.session.scalars(select(Trade).filter(Trade.id == '4')).first()
+ trade = Trade.session.scalars(select(Trade).filter(Trade.id == "4")).first()
amount = trade.amount
# make an limit-buy open trade, if there is no 'filled', don't sell it
mocker.patch(
- f'{EXMS}.fetch_order',
- return_value={
- 'status': 'open',
- 'type': 'limit',
- 'side': 'buy',
- 'filled': None
- }
+ f"{EXMS}.fetch_order",
+ return_value={"status": "open", "type": "limit", "side": "buy", "filled": None},
)
cancel_order_4 = mocker.patch(
- f'{EXMS}.cancel_order_with_result',
- return_value={
- 'status': 'canceled',
- 'type': 'limit',
- 'side': 'sell',
- 'amount': amount,
- 'remaining': 0.0,
- 'filled': amount,
- 'id': trade.orders[0].order_id,
- }
- )
+ f"{EXMS}.cancel_order_with_result",
+ return_value={
+ "status": "canceled",
+ "type": "limit",
+ "side": "sell",
+ "amount": amount,
+ "remaining": 0.0,
+ "filled": amount,
+ "id": trade.orders[0].order_id,
+ },
+ )
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
- msg = rpc._rpc_force_exit('4')
- assert msg == {'result': 'Created exit order for trade 4.'}
+ msg = rpc._rpc_force_exit("4")
+ assert msg == {"result": "Created exit order for trade 4."}
assert cancel_order_4.call_count == 1
assert cancel_order_mock.call_count == 0
assert pytest.approx(trade.amount) == amount
def test_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=ticker),
@@ -872,14 +878,13 @@ def test_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None:
res = rpc._rpc_performance()
assert len(res) == 3
- assert res[0]['pair'] == 'NEO/USDT'
- assert res[0]['count'] == 1
- assert res[0]['profit_pct'] == 5.0
+ assert res[0]["pair"] == "NEO/USDT"
+ assert res[0]["count"] == 1
+ assert res[0]["profit_pct"] == 5.0
def test_enter_tag_performance_handle(default_conf, ticker, fee, mocker) -> None:
-
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=ticker),
@@ -898,24 +903,21 @@ def test_enter_tag_performance_handle(default_conf, ticker, fee, mocker) -> None
res = rpc._rpc_enter_tag_performance(None)
assert len(res) == 3
- assert res[0]['enter_tag'] == 'TEST1'
- assert res[0]['count'] == 1
- assert res[0]['profit_pct'] == 5.0
+ assert res[0]["enter_tag"] == "TEST1"
+ assert res[0]["count"] == 1
+ assert res[0]["profit_pct"] == 5.0
res = rpc._rpc_enter_tag_performance(None)
assert len(res) == 3
- assert res[0]['enter_tag'] == 'TEST1'
- assert res[0]['count'] == 1
- assert res[0]['profit_pct'] == 5.0
+ assert res[0]["enter_tag"] == "TEST1"
+ assert res[0]["count"] == 1
+ assert res[0]["profit_pct"] == 5.0
def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee):
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
- mocker.patch.multiple(
- EXMS,
- markets=PropertyMock(return_value=markets)
- )
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
+ mocker.patch.multiple(EXMS, markets=PropertyMock(return_value=markets))
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
create_mock_trades(fee)
@@ -924,23 +926,23 @@ def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee):
res = rpc._rpc_enter_tag_performance(None)
assert len(res) == 2
- assert res[0]['enter_tag'] == 'TEST1'
- assert res[0]['count'] == 1
- assert pytest.approx(res[0]['profit_pct']) == 0.5
- assert res[1]['enter_tag'] == 'Other'
- assert res[1]['count'] == 1
- assert pytest.approx(res[1]['profit_pct']) == 1.0
+ assert res[0]["enter_tag"] == "TEST1"
+ assert res[0]["count"] == 1
+ assert pytest.approx(res[0]["profit_pct"]) == 0.5
+ assert res[1]["enter_tag"] == "Other"
+ assert res[1]["count"] == 1
+ assert pytest.approx(res[1]["profit_pct"]) == 1.0
# Test for a specific pair
- res = rpc._rpc_enter_tag_performance('ETC/BTC')
+ res = rpc._rpc_enter_tag_performance("ETC/BTC")
assert len(res) == 1
- assert res[0]['count'] == 1
- assert res[0]['enter_tag'] == 'TEST1'
- assert pytest.approx(res[0]['profit_pct']) == 0.5
+ assert res[0]["count"] == 1
+ assert res[0]["enter_tag"] == "TEST1"
+ assert pytest.approx(res[0]["profit_pct"]) == 0.5
def test_exit_reason_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=ticker),
@@ -958,20 +960,17 @@ def test_exit_reason_performance_handle(default_conf_usdt, ticker, fee, mocker)
res = rpc._rpc_exit_reason_performance(None)
assert len(res) == 3
- assert res[0]['exit_reason'] == 'exit_signal'
- assert res[0]['count'] == 1
- assert res[0]['profit_pct'] == 5.0
+ assert res[0]["exit_reason"] == "exit_signal"
+ assert res[0]["count"] == 1
+ assert res[0]["profit_pct"] == 5.0
- assert res[1]['exit_reason'] == 'roi'
- assert res[2]['exit_reason'] == 'Other'
+ assert res[1]["exit_reason"] == "roi"
+ assert res[2]["exit_reason"] == "Other"
def test_exit_reason_performance_handle_2(mocker, default_conf, markets, fee):
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
- mocker.patch.multiple(
- EXMS,
- markets=PropertyMock(return_value=markets)
- )
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
+ mocker.patch.multiple(EXMS, markets=PropertyMock(return_value=markets))
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
create_mock_trades(fee)
@@ -980,23 +979,23 @@ def test_exit_reason_performance_handle_2(mocker, default_conf, markets, fee):
res = rpc._rpc_exit_reason_performance(None)
assert len(res) == 2
- assert res[0]['exit_reason'] == 'sell_signal'
- assert res[0]['count'] == 1
- assert pytest.approx(res[0]['profit_pct']) == 0.5
- assert res[1]['exit_reason'] == 'roi'
- assert res[1]['count'] == 1
- assert pytest.approx(res[1]['profit_pct']) == 1.0
+ assert res[0]["exit_reason"] == "sell_signal"
+ assert res[0]["count"] == 1
+ assert pytest.approx(res[0]["profit_pct"]) == 0.5
+ assert res[1]["exit_reason"] == "roi"
+ assert res[1]["count"] == 1
+ assert pytest.approx(res[1]["profit_pct"]) == 1.0
# Test for a specific pair
- res = rpc._rpc_exit_reason_performance('ETC/BTC')
+ res = rpc._rpc_exit_reason_performance("ETC/BTC")
assert len(res) == 1
- assert res[0]['count'] == 1
- assert res[0]['exit_reason'] == 'sell_signal'
- assert pytest.approx(res[0]['profit_pct']) == 0.5
+ assert res[0]["count"] == 1
+ assert res[0]["exit_reason"] == "sell_signal"
+ assert pytest.approx(res[0]["profit_pct"]) == 0.5
def test_mix_tag_performance_handle(default_conf, ticker, fee, mocker) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=ticker),
@@ -1014,17 +1013,14 @@ def test_mix_tag_performance_handle(default_conf, ticker, fee, mocker) -> None:
res = rpc._rpc_mix_tag_performance(None)
assert len(res) == 3
- assert res[0]['mix_tag'] == 'TEST1 exit_signal'
- assert res[0]['count'] == 1
- assert res[0]['profit_pct'] == 5.0
+ assert res[0]["mix_tag"] == "TEST1 exit_signal"
+ assert res[0]["count"] == 1
+ assert res[0]["profit_pct"] == 5.0
def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
- mocker.patch.multiple(
- EXMS,
- markets=PropertyMock(return_value=markets)
- )
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
+ mocker.patch.multiple(EXMS, markets=PropertyMock(return_value=markets))
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
create_mock_trades(fee)
@@ -1033,24 +1029,24 @@ def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
res = rpc._rpc_mix_tag_performance(None)
assert len(res) == 2
- assert res[0]['mix_tag'] == 'TEST1 sell_signal'
- assert res[0]['count'] == 1
- assert pytest.approx(res[0]['profit_pct']) == 0.5
- assert res[1]['mix_tag'] == 'Other roi'
- assert res[1]['count'] == 1
- assert pytest.approx(res[1]['profit_pct']) == 1.0
+ assert res[0]["mix_tag"] == "TEST1 sell_signal"
+ assert res[0]["count"] == 1
+ assert pytest.approx(res[0]["profit_pct"]) == 0.5
+ assert res[1]["mix_tag"] == "Other roi"
+ assert res[1]["count"] == 1
+ assert pytest.approx(res[1]["profit_pct"]) == 1.0
# Test for a specific pair
- res = rpc._rpc_mix_tag_performance('ETC/BTC')
+ res = rpc._rpc_mix_tag_performance("ETC/BTC")
assert len(res) == 1
- assert res[0]['count'] == 1
- assert res[0]['mix_tag'] == 'TEST1 sell_signal'
- assert pytest.approx(res[0]['profit_pct']) == 0.5
+ assert res[0]["count"] == 1
+ assert res[0]["mix_tag"] == "TEST1 sell_signal"
+ assert pytest.approx(res[0]["profit_pct"]) == 0.5
def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=ticker),
@@ -1072,104 +1068,104 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
def test_rpc_force_entry(mocker, default_conf, ticker, fee, limit_buy_order_open) -> None:
- default_conf['force_entry_enable'] = True
- default_conf['max_open_trades'] = 0
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ default_conf["force_entry_enable"] = True
+ default_conf["max_open_trades"] = 0
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
buy_mm = MagicMock(return_value=limit_buy_order_open)
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
- create_order=buy_mm
+ create_order=buy_mm,
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
- pair = 'ETH/BTC'
- with pytest.raises(RPCException, match='Maximum number of trades is reached.'):
+ pair = "ETH/BTC"
+ with pytest.raises(RPCException, match="Maximum number of trades is reached."):
rpc._rpc_force_entry(pair, None)
- freqtradebot.config['max_open_trades'] = 5
+ freqtradebot.config["max_open_trades"] = 5
trade = rpc._rpc_force_entry(pair, None)
assert isinstance(trade, Trade)
assert trade.pair == pair
- assert trade.open_rate == ticker()['bid']
+ assert trade.open_rate == ticker()["bid"]
# Test buy duplicate
- with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
+ with pytest.raises(RPCException, match=r"position for ETH/BTC already open - id: 1"):
rpc._rpc_force_entry(pair, 0.0001)
- pair = 'XRP/BTC'
- trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit')
+ pair = "XRP/BTC"
+ trade = rpc._rpc_force_entry(pair, 0.0001, order_type="limit")
assert isinstance(trade, Trade)
assert trade.pair == pair
assert trade.open_rate == 0.0001
- with pytest.raises(RPCException,
- match=r'Symbol does not exist or market is not active.'):
- rpc._rpc_force_entry('LTC/NOTHING', 0.0001)
+ with pytest.raises(RPCException, match=r"Symbol does not exist or market is not active."):
+ rpc._rpc_force_entry("LTC/NOTHING", 0.0001)
# Test buy pair not with stakes
- with pytest.raises(RPCException,
- match=r'Wrong pair selected. Only pairs with stake-currency.*'):
- rpc._rpc_force_entry('LTC/ETH', 0.0001)
+ with pytest.raises(
+ RPCException, match=r"Wrong pair selected. Only pairs with stake-currency.*"
+ ):
+ rpc._rpc_force_entry("LTC/ETH", 0.0001)
# Test with defined stake_amount
- pair = 'LTC/BTC'
- trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit', stake_amount=0.05)
+ pair = "LTC/BTC"
+ trade = rpc._rpc_force_entry(pair, 0.0001, order_type="limit", stake_amount=0.05)
assert trade.stake_amount == 0.05
- assert trade.buy_tag == 'force_entry'
+ assert trade.buy_tag == "force_entry"
- assert trade.open_orders_ids[-1] == 'mocked_limit_buy'
+ assert trade.open_orders_ids[-1] == "mocked_limit_buy"
freqtradebot.strategy.position_adjustment_enable = True
- with pytest.raises(RPCException, match=r'position for LTC/BTC already open.*open order.*'):
- rpc._rpc_force_entry(pair, 0.0001, order_type='limit', stake_amount=0.05)
+ with pytest.raises(RPCException, match=r"position for LTC/BTC already open.*open order.*"):
+ rpc._rpc_force_entry(pair, 0.0001, order_type="limit", stake_amount=0.05)
# Test not buying
- pair = 'XRP/BTC'
+ pair = "XRP/BTC"
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
- freqtradebot.config['stake_amount'] = 0
+ freqtradebot.config["stake_amount"] = 0
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
- pair = 'TKN/BTC'
+ pair = "TKN/BTC"
with pytest.raises(RPCException, match=r"Failed to enter position for TKN/BTC."):
trade = rpc._rpc_force_entry(pair, None)
def test_rpc_force_entry_stopped(mocker, default_conf) -> None:
- default_conf['force_entry_enable'] = True
- default_conf['initial_state'] = 'stopped'
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ default_conf["force_entry_enable"] = True
+ default_conf["initial_state"] = "stopped"
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
- pair = 'ETH/BTC'
- with pytest.raises(RPCException, match=r'trader is not running'):
+ pair = "ETH/BTC"
+ with pytest.raises(RPCException, match=r"trader is not running"):
rpc._rpc_force_entry(pair, None)
def test_rpc_force_entry_disabled(mocker, default_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
- pair = 'ETH/BTC'
- with pytest.raises(RPCException, match=r'Force_entry not enabled.'):
+ pair = "ETH/BTC"
+ with pytest.raises(RPCException, match=r"Force_entry not enabled."):
rpc._rpc_force_entry(pair, None)
def test_rpc_force_entry_wrong_mode(mocker, default_conf) -> None:
- default_conf['force_entry_enable'] = True
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ default_conf["force_entry_enable"] = True
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
- pair = 'ETH/BTC'
+ pair = "ETH/BTC"
with pytest.raises(RPCException, match="Can't go short on Spot markets."):
rpc._rpc_force_entry(pair, None, order_side=SignalDirection.SHORT)
@@ -1178,134 +1174,140 @@ def test_rpc_force_entry_wrong_mode(mocker, default_conf) -> None:
def test_rpc_add_and_delete_lock(mocker, default_conf):
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
- pair = 'ETH/BTC'
+ pair = "ETH/BTC"
- rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=4), '', '*')
- rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=5), '', '*')
- rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=10), '', '*')
+ rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=4), "", "*")
+ rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=5), "", "*")
+ rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=10), "", "*")
locks = rpc._rpc_locks()
- assert locks['lock_count'] == 3
- locks1 = rpc._rpc_delete_lock(lockid=locks['locks'][0]['id'])
- assert locks1['lock_count'] == 2
+ assert locks["lock_count"] == 3
+ locks1 = rpc._rpc_delete_lock(lockid=locks["locks"][0]["id"])
+ assert locks1["lock_count"] == 2
locks2 = rpc._rpc_delete_lock(pair=pair)
- assert locks2['lock_count'] == 0
+ assert locks2["lock_count"] == 0
def test_rpc_whitelist(mocker, default_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
ret = rpc._rpc_whitelist()
- assert len(ret['method']) == 1
- assert 'StaticPairList' in ret['method']
- assert ret['whitelist'] == default_conf['exchange']['pair_whitelist']
+ assert len(ret["method"]) == 1
+ assert "StaticPairList" in ret["method"]
+ assert ret["whitelist"] == default_conf["exchange"]["pair_whitelist"]
def test_rpc_whitelist_dynamic(mocker, default_conf) -> None:
- default_conf['pairlists'] = [{'method': 'VolumePairList',
- 'number_assets': 4,
- }]
- mocker.patch(f'{EXMS}.exchange_has', MagicMock(return_value=True))
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ default_conf["pairlists"] = [
+ {
+ "method": "VolumePairList",
+ "number_assets": 4,
+ }
+ ]
+ mocker.patch(f"{EXMS}.exchange_has", MagicMock(return_value=True))
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
ret = rpc._rpc_whitelist()
- assert len(ret['method']) == 1
- assert 'VolumePairList' in ret['method']
- assert ret['length'] == 4
- assert ret['whitelist'] == default_conf['exchange']['pair_whitelist']
+ assert len(ret["method"]) == 1
+ assert "VolumePairList" in ret["method"]
+ assert ret["length"] == 4
+ assert ret["whitelist"] == default_conf["exchange"]["pair_whitelist"]
def test_rpc_blacklist(mocker, default_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
ret = rpc._rpc_blacklist(None)
- assert len(ret['method']) == 1
- assert 'StaticPairList' in ret['method']
- assert len(ret['blacklist']) == 2
- assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
- assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC']
+ assert len(ret["method"]) == 1
+ assert "StaticPairList" in ret["method"]
+ assert len(ret["blacklist"]) == 2
+ assert ret["blacklist"] == default_conf["exchange"]["pair_blacklist"]
+ assert ret["blacklist"] == ["DOGE/BTC", "HOT/BTC"]
ret = rpc._rpc_blacklist(["ETH/BTC"])
- assert 'StaticPairList' in ret['method']
- assert len(ret['blacklist']) == 3
- assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
- assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC']
+ assert "StaticPairList" in ret["method"]
+ assert len(ret["blacklist"]) == 3
+ assert ret["blacklist"] == default_conf["exchange"]["pair_blacklist"]
+ assert ret["blacklist"] == ["DOGE/BTC", "HOT/BTC", "ETH/BTC"]
ret = rpc._rpc_blacklist(["ETH/BTC"])
- assert 'errors' in ret
- assert isinstance(ret['errors'], dict)
- assert ret['errors']['ETH/BTC']['error_msg'] == 'Pair ETH/BTC already in pairlist.'
+ assert "errors" in ret
+ assert isinstance(ret["errors"], dict)
+ assert ret["errors"]["ETH/BTC"]["error_msg"] == "Pair ETH/BTC already in pairlist."
ret = rpc._rpc_blacklist(["*/BTC"])
- assert 'StaticPairList' in ret['method']
- assert len(ret['blacklist']) == 3
- assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
- assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC']
- assert ret['blacklist_expanded'] == ['ETH/BTC']
- assert 'errors' in ret
- assert isinstance(ret['errors'], dict)
- assert ret['errors'] == {'*/BTC': {'error_msg': 'Pair */BTC is not a valid wildcard.'}}
+ assert "StaticPairList" in ret["method"]
+ assert len(ret["blacklist"]) == 3
+ assert ret["blacklist"] == default_conf["exchange"]["pair_blacklist"]
+ assert ret["blacklist"] == ["DOGE/BTC", "HOT/BTC", "ETH/BTC"]
+ assert ret["blacklist_expanded"] == ["ETH/BTC"]
+ assert "errors" in ret
+ assert isinstance(ret["errors"], dict)
+ assert ret["errors"] == {"*/BTC": {"error_msg": "Pair */BTC is not a valid wildcard."}}
ret = rpc._rpc_blacklist(["XRP/.*"])
- assert 'StaticPairList' in ret['method']
- assert len(ret['blacklist']) == 4
- assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
- assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC', 'XRP/.*']
- assert ret['blacklist_expanded'] == ['ETH/BTC', 'XRP/BTC', 'XRP/USDT']
- assert 'errors' in ret
- assert isinstance(ret['errors'], dict)
+ assert "StaticPairList" in ret["method"]
+ assert len(ret["blacklist"]) == 4
+ assert ret["blacklist"] == default_conf["exchange"]["pair_blacklist"]
+ assert ret["blacklist"] == ["DOGE/BTC", "HOT/BTC", "ETH/BTC", "XRP/.*"]
+ assert ret["blacklist_expanded"] == ["ETH/BTC", "XRP/BTC", "XRP/USDT"]
+ assert "errors" in ret
+ assert isinstance(ret["errors"], dict)
- ret = rpc._rpc_blacklist_delete(["DOGE/BTC", 'HOT/BTC'])
+ ret = rpc._rpc_blacklist_delete(["DOGE/BTC", "HOT/BTC"])
- assert 'StaticPairList' in ret['method']
- assert len(ret['blacklist']) == 2
- assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
- assert ret['blacklist'] == ['ETH/BTC', 'XRP/.*']
- assert ret['blacklist_expanded'] == ['ETH/BTC', 'XRP/BTC', 'XRP/USDT']
- assert 'errors' in ret
- assert isinstance(ret['errors'], dict)
+ assert "StaticPairList" in ret["method"]
+ assert len(ret["blacklist"]) == 2
+ assert ret["blacklist"] == default_conf["exchange"]["pair_blacklist"]
+ assert ret["blacklist"] == ["ETH/BTC", "XRP/.*"]
+ assert ret["blacklist_expanded"] == ["ETH/BTC", "XRP/BTC", "XRP/USDT"]
+ assert "errors" in ret
+ assert isinstance(ret["errors"], dict)
def test_rpc_edge_disabled(mocker, default_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
- with pytest.raises(RPCException, match=r'Edge is not enabled.'):
+ with pytest.raises(RPCException, match=r"Edge is not enabled."):
rpc._rpc_edge()
def test_rpc_edge_enabled(mocker, edge_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
- mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
- return_value={
- 'E/F': PairInfo(-0.02, 0.66, 3.71, 0.50, 1.71, 10, 60),
- }
- ))
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
+ mocker.patch(
+ "freqtrade.edge.Edge._cached_pairs",
+ mocker.PropertyMock(
+ return_value={
+ "E/F": PairInfo(-0.02, 0.66, 3.71, 0.50, 1.71, 10, 60),
+ }
+ ),
+ )
freqtradebot = get_patched_freqtradebot(mocker, edge_conf)
rpc = RPC(freqtradebot)
ret = rpc._rpc_edge()
assert len(ret) == 1
- assert ret[0]['Pair'] == 'E/F'
- assert ret[0]['Winrate'] == 0.66
- assert ret[0]['Expectancy'] == 1.71
- assert ret[0]['Stoploss'] == -0.02
+ assert ret[0]["Pair"] == "E/F"
+ assert ret[0]["Winrate"] == 0.66
+ assert ret[0]["Expectancy"] == 1.71
+ assert ret[0]["Stoploss"] == -0.02
def test_rpc_health(mocker, default_conf) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
set_startup_time()
rpc = RPC(freqtradebot)
result = rpc.health()
- assert result['last_process'] is None
- assert result['last_process_ts'] is None
+ assert result["last_process"] is None
+ assert result["last_process_ts"] is None
diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py
index c5e7d9a89..ffb2408f1 100644
--- a/tests/rpc/test_rpc_apiserver.py
+++ b/tests/rpc/test_rpc_apiserver.py
@@ -1,6 +1,7 @@
"""
Unit test file for rpc/api_server.py
"""
+
import asyncio
import logging
import time
@@ -52,19 +53,24 @@ _TEST_WS_TOKEN = "secret_Ws_t0ken"
def botclient(default_conf, mocker):
setup_logging_pre()
setup_logging(default_conf)
- default_conf['runmode'] = RunMode.DRY_RUN
- default_conf.update({"api_server": {"enabled": True,
- "listen_ip_address": "127.0.0.1",
- "listen_port": 8080,
- "CORS_origins": ['http://example.com'],
- "username": _TEST_USER,
- "password": _TEST_PASS,
- "ws_token": _TEST_WS_TOKEN
- }})
+ default_conf["runmode"] = RunMode.DRY_RUN
+ default_conf.update(
+ {
+ "api_server": {
+ "enabled": True,
+ "listen_ip_address": "127.0.0.1",
+ "listen_port": 8080,
+ "CORS_origins": ["http://example.com"],
+ "username": _TEST_USER,
+ "password": _TEST_PASS,
+ "ws_token": _TEST_WS_TOKEN,
+ }
+ }
+ )
ftbot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(ftbot)
- mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', MagicMock())
+ mocker.patch("freqtrade.rpc.api_server.ApiServer.start_api", MagicMock())
apiserver = None
try:
apiserver = ApiServer(default_conf)
@@ -81,47 +87,61 @@ def botclient(default_conf, mocker):
def client_post(client: TestClient, url, data=None):
-
if data is None:
data = {}
- return client.post(url,
- json=data,
- headers={'Authorization': _basic_auth_str(_TEST_USER, _TEST_PASS),
- 'Origin': 'http://example.com',
- 'content-type': 'application/json'
- })
+ return client.post(
+ url,
+ json=data,
+ headers={
+ "Authorization": _basic_auth_str(_TEST_USER, _TEST_PASS),
+ "Origin": "http://example.com",
+ "content-type": "application/json",
+ },
+ )
def client_patch(client: TestClient, url, data=None):
-
if data is None:
data = {}
- return client.patch(url,
- json=data,
- headers={'Authorization': _basic_auth_str(_TEST_USER, _TEST_PASS),
- 'Origin': 'http://example.com',
- 'content-type': 'application/json'
- })
+ return client.patch(
+ url,
+ json=data,
+ headers={
+ "Authorization": _basic_auth_str(_TEST_USER, _TEST_PASS),
+ "Origin": "http://example.com",
+ "content-type": "application/json",
+ },
+ )
def client_get(client: TestClient, url):
# Add fake Origin to ensure CORS kicks in
- return client.get(url, headers={'Authorization': _basic_auth_str(_TEST_USER, _TEST_PASS),
- 'Origin': 'http://example.com'})
+ return client.get(
+ url,
+ headers={
+ "Authorization": _basic_auth_str(_TEST_USER, _TEST_PASS),
+ "Origin": "http://example.com",
+ },
+ )
def client_delete(client: TestClient, 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'})
+ 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.headers.get('content-type') == "application/json"
+ assert response.headers.get("content-type") == "application/json"
if needs_cors:
- assert ('access-control-allow-credentials', 'true') in response.headers.items()
- assert ('access-control-allow-origin', 'http://example.com') in response.headers.items()
+ assert ("access-control-allow-credentials", "true") in response.headers.items()
+ assert ("access-control-allow-origin", "http://example.com") in response.headers.items()
def test_api_not_found(botclient):
@@ -140,7 +160,7 @@ def test_api_ui_fallback(botclient, mocker):
rc = client_get(client, "/fallback_file.html")
assert rc.status_code == 200
- assert '`freqtrade install-ui`' in rc.text
+ assert "`freqtrade install-ui`" in rc.text
# Forwarded to fallback_html or index.html (depending if it's installed or not)
rc = client_get(client, "/something")
@@ -150,45 +170,45 @@ def test_api_ui_fallback(botclient, mocker):
assert rc.status_code == 200
# Test directory traversal without mock
- rc = client_get(client, '%2F%2F%2Fetc/passwd')
+ rc = client_get(client, "%2F%2F%2Fetc/passwd")
assert rc.status_code == 200
# Allow both fallback or real UI
- assert '`freqtrade install-ui`' in rc.text or '' in rc.text
+ assert "`freqtrade install-ui`" in rc.text or "" in rc.text
- mocker.patch.object(Path, 'is_file', MagicMock(side_effect=[True, False]))
- rc = client_get(client, '%2F%2F%2Fetc/passwd')
+ mocker.patch.object(Path, "is_file", MagicMock(side_effect=[True, False]))
+ rc = client_get(client, "%2F%2F%2Fetc/passwd")
assert rc.status_code == 200
- assert '`freqtrade install-ui`' in rc.text
+ assert "`freqtrade install-ui`" in rc.text
def test_api_ui_version(botclient, mocker):
_ftbot, client = botclient
- mocker.patch('freqtrade.commands.deploy_commands.read_ui_version', return_value='0.1.2')
+ mocker.patch("freqtrade.commands.deploy_commands.read_ui_version", return_value="0.1.2")
rc = client_get(client, "/ui_version")
assert rc.status_code == 200
- assert rc.json()['version'] == '0.1.2'
+ assert rc.json()["version"] == "0.1.2"
def test_api_auth():
with pytest.raises(ValueError):
- create_token({'identity': {'u': 'Freqtrade'}}, 'secret1234', token_type="NotATokenType")
+ create_token({"identity": {"u": "Freqtrade"}}, "secret1234", token_type="NotATokenType")
- token = create_token({'identity': {'u': 'Freqtrade'}}, 'secret1234')
+ token = create_token({"identity": {"u": "Freqtrade"}}, "secret1234")
assert isinstance(token, str)
- u = get_user_from_token(token, 'secret1234')
- assert u == 'Freqtrade'
+ u = get_user_from_token(token, "secret1234")
+ assert u == "Freqtrade"
with pytest.raises(HTTPException):
- get_user_from_token(token, 'secret1234', token_type='refresh')
+ get_user_from_token(token, "secret1234", token_type="refresh")
# Create invalid token
- token = create_token({'identity': {'u1': 'Freqrade'}}, 'secret1234')
+ token = create_token({"identity": {"u1": "Freqrade"}}, "secret1234")
with pytest.raises(HTTPException):
- get_user_from_token(token, 'secret1234')
+ get_user_from_token(token, "secret1234")
with pytest.raises(HTTPException):
- get_user_from_token(b'not_a_token', 'secret1234')
+ get_user_from_token(b"not_a_token", "secret1234")
def test_api_ws_auth(botclient):
@@ -206,8 +226,8 @@ def test_api_ws_auth(botclient):
with client.websocket_connect(url(good_token)) as websocket:
pass
- jwt_secret = ftbot.config['api_server'].get('jwt_secret_key', 'super-secret')
- jwt_token = create_token({'identity': {'u': 'Freqtrade'}}, jwt_secret)
+ jwt_secret = ftbot.config["api_server"].get("jwt_secret_key", "super-secret")
+ jwt_token = create_token({"identity": {"u": "Freqtrade"}}, jwt_secret)
with client.websocket_connect(url(jwt_token)) as websocket:
pass
@@ -216,50 +236,58 @@ def test_api_unauthorized(botclient):
ftbot, client = botclient
rc = client.get(f"{BASE_URI}/ping")
assert_response(rc, needs_cors=False)
- assert rc.json() == {'status': 'pong'}
+ assert rc.json() == {"status": "pong"}
# Don't send user/pass information
rc = client.get(f"{BASE_URI}/version")
assert_response(rc, 401, needs_cors=False)
- assert rc.json() == {'detail': 'Unauthorized'}
+ assert rc.json() == {"detail": "Unauthorized"}
# Change only username
- ftbot.config['api_server']['username'] = 'Ftrader'
+ ftbot.config["api_server"]["username"] = "Ftrader"
rc = client_get(client, f"{BASE_URI}/version")
assert_response(rc, 401)
- assert rc.json() == {'detail': 'Unauthorized'}
+ assert rc.json() == {"detail": "Unauthorized"}
# Change only password
- ftbot.config['api_server']['username'] = _TEST_USER
- ftbot.config['api_server']['password'] = 'WrongPassword'
+ ftbot.config["api_server"]["username"] = _TEST_USER
+ ftbot.config["api_server"]["password"] = "WrongPassword"
rc = client_get(client, f"{BASE_URI}/version")
assert_response(rc, 401)
- assert rc.json() == {'detail': 'Unauthorized'}
+ assert rc.json() == {"detail": "Unauthorized"}
- ftbot.config['api_server']['username'] = 'Ftrader'
- ftbot.config['api_server']['password'] = 'WrongPassword'
+ ftbot.config["api_server"]["username"] = "Ftrader"
+ ftbot.config["api_server"]["password"] = "WrongPassword"
rc = client_get(client, f"{BASE_URI}/version")
assert_response(rc, 401)
- assert rc.json() == {'detail': 'Unauthorized'}
+ assert rc.json() == {"detail": "Unauthorized"}
def test_api_token_login(botclient):
_ftbot, client = botclient
- rc = client.post(f"{BASE_URI}/token/login",
- data=None,
- headers={'Authorization': _basic_auth_str('WRONG_USER', 'WRONG_PASS'),
- 'Origin': 'http://example.com'})
+ rc = client.post(
+ f"{BASE_URI}/token/login",
+ data=None,
+ headers={
+ "Authorization": _basic_auth_str("WRONG_USER", "WRONG_PASS"),
+ "Origin": "http://example.com",
+ },
+ )
assert_response(rc, 401)
rc = client_post(client, f"{BASE_URI}/token/login")
assert_response(rc)
- assert 'access_token' in rc.json()
- assert 'refresh_token' in rc.json()
+ assert "access_token" in rc.json()
+ assert "refresh_token" in rc.json()
# test Authentication is working with JWT tokens too
- rc = client.get(f"{BASE_URI}/count",
- headers={'Authorization': f'Bearer {rc.json()["access_token"]}',
- 'Origin': 'http://example.com'})
+ rc = client.get(
+ f"{BASE_URI}/count",
+ headers={
+ "Authorization": f'Bearer {rc.json()["access_token"]}',
+ "Origin": "http://example.com",
+ },
+ )
assert_response(rc)
@@ -267,13 +295,17 @@ def test_api_token_refresh(botclient):
_ftbot, client = botclient
rc = client_post(client, f"{BASE_URI}/token/login")
assert_response(rc)
- rc = client.post(f"{BASE_URI}/token/refresh",
- data=None,
- headers={'Authorization': f'Bearer {rc.json()["refresh_token"]}',
- 'Origin': 'http://example.com'})
+ rc = client.post(
+ f"{BASE_URI}/token/refresh",
+ data=None,
+ headers={
+ "Authorization": f'Bearer {rc.json()["refresh_token"]}',
+ "Origin": "http://example.com",
+ },
+ )
assert_response(rc)
- assert 'access_token' in rc.json()
- assert 'refresh_token' not in rc.json()
+ assert "access_token" in rc.json()
+ assert "refresh_token" not in rc.json()
def test_api_stop_workflow(botclient):
@@ -281,38 +313,43 @@ def test_api_stop_workflow(botclient):
assert ftbot.state == State.RUNNING
rc = client_post(client, f"{BASE_URI}/stop")
assert_response(rc)
- assert rc.json() == {'status': 'stopping trader ...'}
+ assert rc.json() == {"status": "stopping trader ..."}
assert ftbot.state == State.STOPPED
# Stop bot again
rc = client_post(client, f"{BASE_URI}/stop")
assert_response(rc)
- assert rc.json() == {'status': 'already stopped'}
+ assert rc.json() == {"status": "already stopped"}
# Start bot
rc = client_post(client, f"{BASE_URI}/start")
assert_response(rc)
- assert rc.json() == {'status': 'starting trader ...'}
+ assert rc.json() == {"status": "starting trader ..."}
assert ftbot.state == State.RUNNING
# Call start again
rc = client_post(client, f"{BASE_URI}/start")
assert_response(rc)
- assert rc.json() == {'status': 'already running'}
+ assert rc.json() == {"status": "already running"}
def test_api__init__(default_conf, mocker):
"""
Test __init__() method
"""
- default_conf.update({"api_server": {"enabled": True,
- "listen_ip_address": "127.0.0.1",
- "listen_port": 8080,
- "username": "TestUser",
- "password": "testPass",
- }})
- mocker.patch('freqtrade.rpc.telegram.Telegram._init')
- mocker.patch('freqtrade.rpc.api_server.webserver.ApiServer.start_api', MagicMock())
+ default_conf.update(
+ {
+ "api_server": {
+ "enabled": True,
+ "listen_ip_address": "127.0.0.1",
+ "listen_port": 8080,
+ "username": "TestUser",
+ "password": "testPass",
+ }
+ }
+ )
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init")
+ mocker.patch("freqtrade.rpc.api_server.webserver.ApiServer.start_api", MagicMock())
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
assert apiserver._config == default_conf
@@ -324,8 +361,8 @@ def test_api__init__(default_conf, mocker):
def test_api_UvicornServer(mocker):
- thread_mock = mocker.patch('freqtrade.rpc.api_server.uvicorn_threaded.threading.Thread')
- s = UvicornServer(uvicorn.Config(MagicMock(), port=8080, host='127.0.0.1'))
+ thread_mock = mocker.patch("freqtrade.rpc.api_server.uvicorn_threaded.threading.Thread")
+ s = UvicornServer(uvicorn.Config(MagicMock(), port=8080, host="127.0.0.1"))
assert thread_mock.call_count == 0
# Fake started to avoid sleeping forever
@@ -338,9 +375,10 @@ def test_api_UvicornServer(mocker):
def test_api_UvicornServer_run(mocker):
- serve_mock = mocker.patch('freqtrade.rpc.api_server.uvicorn_threaded.UvicornServer.serve',
- get_mock_coro(None))
- s = UvicornServer(uvicorn.Config(MagicMock(), port=8080, host='127.0.0.1'))
+ serve_mock = mocker.patch(
+ "freqtrade.rpc.api_server.uvicorn_threaded.UvicornServer.serve", get_mock_coro(None)
+ )
+ s = UvicornServer(uvicorn.Config(MagicMock(), port=8080, host="127.0.0.1"))
assert serve_mock.call_count == 0
# Fake started to avoid sleeping forever
@@ -350,10 +388,11 @@ def test_api_UvicornServer_run(mocker):
def test_api_UvicornServer_run_no_uvloop(mocker, import_fails):
- serve_mock = mocker.patch('freqtrade.rpc.api_server.uvicorn_threaded.UvicornServer.serve',
- get_mock_coro(None))
+ serve_mock = mocker.patch(
+ "freqtrade.rpc.api_server.uvicorn_threaded.UvicornServer.serve", get_mock_coro(None)
+ )
asyncio.set_event_loop(asyncio.new_event_loop())
- s = UvicornServer(uvicorn.Config(MagicMock(), port=8080, host='127.0.0.1'))
+ s = UvicornServer(uvicorn.Config(MagicMock(), port=8080, host="127.0.0.1"))
assert serve_mock.call_count == 0
# Fake started to avoid sleeping forever
@@ -363,19 +402,24 @@ def test_api_UvicornServer_run_no_uvloop(mocker, import_fails):
def test_api_run(default_conf, mocker, caplog):
- default_conf.update({"api_server": {"enabled": True,
- "listen_ip_address": "127.0.0.1",
- "listen_port": 8080,
- "username": "TestUser",
- "password": "testPass",
- }})
- mocker.patch('freqtrade.rpc.telegram.Telegram._init')
+ default_conf.update(
+ {
+ "api_server": {
+ "enabled": True,
+ "listen_ip_address": "127.0.0.1",
+ "listen_port": 8080,
+ "username": "TestUser",
+ "password": "testPass",
+ }
+ }
+ )
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init")
server_inst_mock = MagicMock()
server_inst_mock.run_in_thread = MagicMock()
server_inst_mock.run = MagicMock()
server_mock = MagicMock(return_value=server_inst_mock)
- mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock)
+ mocker.patch("freqtrade.rpc.api_server.webserver.UvicornServer", server_mock)
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
@@ -396,11 +440,16 @@ def test_api_run(default_conf, mocker, caplog):
# Test binding to public
caplog.clear()
server_mock.reset_mock()
- apiserver._config.update({"api_server": {"enabled": True,
- "listen_ip_address": "0.0.0.0",
- "listen_port": 8089,
- "password": "",
- }})
+ apiserver._config.update(
+ {
+ "api_server": {
+ "enabled": True,
+ "listen_ip_address": "0.0.0.0",
+ "listen_port": 8089,
+ "password": "",
+ }
+ }
+ )
apiserver.start_api()
assert server_mock.call_count == 1
@@ -411,12 +460,17 @@ def test_api_run(default_conf, mocker, caplog):
assert isinstance(server_mock.call_args_list[0][0][0].app, FastAPI)
assert log_has("Starting HTTP Server at 0.0.0.0:8089", caplog)
assert log_has("Starting Local Rest Server.", caplog)
- assert log_has("SECURITY WARNING - Local Rest Server listening to external connections",
- caplog)
- assert log_has("SECURITY WARNING - This is insecure please set to your loopback,"
- "e.g 127.0.0.1 in config.json", caplog)
- assert log_has("SECURITY WARNING - No password for local REST Server defined. "
- "Please make sure that this is intentional!", caplog)
+ assert log_has("SECURITY WARNING - Local Rest Server listening to external connections", caplog)
+ assert log_has(
+ "SECURITY WARNING - This is insecure please set to your loopback,"
+ "e.g 127.0.0.1 in config.json",
+ caplog,
+ )
+ assert log_has(
+ "SECURITY WARNING - No password for local REST Server defined. "
+ "Please make sure that this is intentional!",
+ caplog,
+ )
assert log_has_re("SECURITY WARNING - `jwt_secret_key` seems to be default.*", caplog)
server_mock.reset_mock()
@@ -432,8 +486,9 @@ def test_api_run(default_conf, mocker, caplog):
# Test crashing API server
caplog.clear()
- mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer',
- MagicMock(side_effect=Exception))
+ mocker.patch(
+ "freqtrade.rpc.api_server.webserver.UvicornServer", MagicMock(side_effect=Exception)
+ )
apiserver.start_api()
assert log_has("Api server failed to start.", caplog)
apiserver.cleanup()
@@ -441,17 +496,22 @@ def test_api_run(default_conf, mocker, caplog):
def test_api_cleanup(default_conf, mocker, caplog):
- default_conf.update({"api_server": {"enabled": True,
- "listen_ip_address": "127.0.0.1",
- "listen_port": 8080,
- "username": "TestUser",
- "password": "testPass",
- }})
- mocker.patch('freqtrade.rpc.telegram.Telegram._init')
+ default_conf.update(
+ {
+ "api_server": {
+ "enabled": True,
+ "listen_ip_address": "127.0.0.1",
+ "listen_port": 8080,
+ "username": "TestUser",
+ "password": "testPass",
+ }
+ }
+ )
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init")
server_mock = MagicMock()
server_mock.cleanup = MagicMock()
- mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock)
+ mocker.patch("freqtrade.rpc.api_server.webserver.UvicornServer", server_mock)
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
@@ -467,35 +527,36 @@ def test_api_reloadconf(botclient):
rc = client_post(client, f"{BASE_URI}/reload_config")
assert_response(rc)
- assert rc.json() == {'status': 'Reloading config ...'}
+ assert rc.json() == {"status": "Reloading config ..."}
assert ftbot.state == State.RELOAD_CONFIG
def test_api_stopentry(botclient):
ftbot, client = botclient
- assert ftbot.config['max_open_trades'] != 0
+ assert ftbot.config["max_open_trades"] != 0
rc = client_post(client, f"{BASE_URI}/stopbuy")
assert_response(rc)
assert rc.json() == {
- 'status': 'No more entries will occur from now. Run /reload_config to reset.'}
- assert ftbot.config['max_open_trades'] == 0
+ "status": "No more entries will occur from now. Run /reload_config to reset."
+ }
+ assert ftbot.config["max_open_trades"] == 0
rc = client_post(client, f"{BASE_URI}/stopentry")
assert_response(rc)
assert rc.json() == {
- 'status': 'No more entries will occur from now. Run /reload_config to reset.'}
- assert ftbot.config['max_open_trades'] == 0
+ "status": "No more entries will occur from now. Run /reload_config to reset."
+ }
+ assert ftbot.config["max_open_trades"] == 0
def test_api_balance(botclient, mocker, rpc_balance, tickers):
ftbot, client = botclient
- ftbot.config['dry_run'] = False
- mocker.patch(f'{EXMS}.get_balances', return_value=rpc_balance)
- mocker.patch(f'{EXMS}.get_tickers', tickers)
- mocker.patch(f'{EXMS}.get_valid_pair_combination',
- side_effect=lambda a, b: f"{a}/{b}")
+ ftbot.config["dry_run"] = False
+ mocker.patch(f"{EXMS}.get_balances", return_value=rpc_balance)
+ mocker.patch(f"{EXMS}.get_tickers", tickers)
+ mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: f"{a}/{b}")
ftbot.wallets.update()
rc = client_get(client, f"{BASE_URI}/balance")
@@ -503,30 +564,30 @@ def test_api_balance(botclient, mocker, rpc_balance, tickers):
response = rc.json()
assert "currencies" in response
assert len(response["currencies"]) == 5
- assert response['currencies'][0] == {
- 'currency': 'BTC',
- 'free': 12.0,
- 'balance': 12.0,
- 'used': 0.0,
- 'bot_owned': pytest.approx(11.879999),
- 'est_stake': 12.0,
- 'est_stake_bot': pytest.approx(11.879999),
- 'stake': 'BTC',
- 'is_position': False,
- 'leverage': 1.0,
- 'position': 0.0,
- 'side': 'long',
- 'is_bot_managed': True,
+ assert response["currencies"][0] == {
+ "currency": "BTC",
+ "free": 12.0,
+ "balance": 12.0,
+ "used": 0.0,
+ "bot_owned": pytest.approx(11.879999),
+ "est_stake": 12.0,
+ "est_stake_bot": pytest.approx(11.879999),
+ "stake": "BTC",
+ "is_position": False,
+ "leverage": 1.0,
+ "position": 0.0,
+ "side": "long",
+ "is_bot_managed": True,
}
- assert response['total'] == 12.159513094
- assert response['total_bot'] == pytest.approx(11.879999)
- assert 'starting_capital' in response
- assert 'starting_capital_fiat' in response
- assert 'starting_capital_pct' in response
- assert 'starting_capital_ratio' in response
+ assert response["total"] == 12.159513094
+ assert response["total_bot"] == pytest.approx(11.879999)
+ assert "starting_capital" in response
+ assert "starting_capital_fiat" in response
+ assert "starting_capital_pct" in response
+ assert "starting_capital_ratio" in response
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_api_count(botclient, mocker, ticker, fee, markets, is_short):
ftbot, client = botclient
patch_get_signal(ftbot)
@@ -535,7 +596,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets, is_short):
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
- markets=PropertyMock(return_value=markets)
+ markets=PropertyMock(return_value=markets),
)
rc = client_get(client, f"{BASE_URI}/count")
assert_response(rc)
@@ -550,7 +611,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets, is_short):
assert rc.json()["current"] == 4
assert rc.json()["max"] == 1
- ftbot.config['max_open_trades'] = float('inf')
+ ftbot.config["max_open_trades"] = float("inf")
rc = client_get(client, f"{BASE_URI}/count")
assert rc.json()["max"] == -1
@@ -561,43 +622,47 @@ def test_api_locks(botclient):
rc = client_get(client, f"{BASE_URI}/locks")
assert_response(rc)
- assert 'locks' in rc.json()
+ assert "locks" in rc.json()
- assert rc.json()['lock_count'] == 0
- assert rc.json()['lock_count'] == len(rc.json()['locks'])
+ assert rc.json()["lock_count"] == 0
+ assert rc.json()["lock_count"] == len(rc.json()["locks"])
- rc = client_post(client, f"{BASE_URI}/locks", [
- {
- "pair": "ETH/BTC",
- "until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=4))}Z",
- "reason": "randreason"
- }, {
- "pair": "XRP/BTC",
- "until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=20))}Z",
- "reason": "deadbeef"
- }
- ])
+ rc = client_post(
+ client,
+ f"{BASE_URI}/locks",
+ [
+ {
+ "pair": "ETH/BTC",
+ "until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=4))}Z",
+ "reason": "randreason",
+ },
+ {
+ "pair": "XRP/BTC",
+ "until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=20))}Z",
+ "reason": "deadbeef",
+ },
+ ],
+ )
assert_response(rc)
- assert rc.json()['lock_count'] == 2
+ assert rc.json()["lock_count"] == 2
rc = client_get(client, f"{BASE_URI}/locks")
assert_response(rc)
- assert rc.json()['lock_count'] == 2
- assert rc.json()['lock_count'] == len(rc.json()['locks'])
- assert 'ETH/BTC' in (rc.json()['locks'][0]['pair'], rc.json()['locks'][1]['pair'])
- assert 'randreason' in (rc.json()['locks'][0]['reason'], rc.json()['locks'][1]['reason'])
- assert 'deadbeef' in (rc.json()['locks'][0]['reason'], rc.json()['locks'][1]['reason'])
+ assert rc.json()["lock_count"] == 2
+ assert rc.json()["lock_count"] == len(rc.json()["locks"])
+ assert "ETH/BTC" in (rc.json()["locks"][0]["pair"], rc.json()["locks"][1]["pair"])
+ assert "randreason" in (rc.json()["locks"][0]["reason"], rc.json()["locks"][1]["reason"])
+ assert "deadbeef" in (rc.json()["locks"][0]["reason"], rc.json()["locks"][1]["reason"])
# Test deletions
rc = client_delete(client, f"{BASE_URI}/locks/1")
assert_response(rc)
- assert rc.json()['lock_count'] == 1
+ assert rc.json()["lock_count"] == 1
- rc = client_post(client, f"{BASE_URI}/locks/delete",
- data={"pair": "XRP/BTC"})
+ rc = client_post(client, f"{BASE_URI}/locks/delete", data={"pair": "XRP/BTC"})
assert_response(rc)
- assert rc.json()['lock_count'] == 0
+ assert rc.json()["lock_count"] == 0
def test_api_show_config(botclient):
@@ -607,22 +672,22 @@ def test_api_show_config(botclient):
rc = client_get(client, f"{BASE_URI}/show_config")
assert_response(rc)
response = rc.json()
- assert 'dry_run' in response
- assert response['exchange'] == 'binance'
- assert response['timeframe'] == '5m'
- assert response['timeframe_ms'] == 300000
- assert response['timeframe_min'] == 5
- assert response['state'] == 'running'
- assert response['bot_name'] == 'freqtrade'
- assert response['trading_mode'] == 'spot'
- assert response['strategy_version'] is None
- assert not response['trailing_stop']
- assert 'entry_pricing' in response
- assert 'exit_pricing' in response
- assert 'unfilledtimeout' in response
- assert 'version' in response
- assert 'api_version' in response
- assert 2.1 <= response['api_version'] < 3.0
+ assert "dry_run" in response
+ assert response["exchange"] == "binance"
+ assert response["timeframe"] == "5m"
+ assert response["timeframe_ms"] == 300000
+ assert response["timeframe_min"] == 5
+ assert response["state"] == "running"
+ assert response["bot_name"] == "freqtrade"
+ assert response["trading_mode"] == "spot"
+ assert response["strategy_version"] is None
+ assert not response["trailing_stop"]
+ assert "entry_pricing" in response
+ assert "exit_pricing" in response
+ assert "unfilledtimeout" in response
+ assert "version" in response
+ assert "api_version" in response
+ assert 2.1 <= response["api_version"] < 3.0
def test_api_daily(botclient, mocker, ticker, fee, markets):
@@ -633,14 +698,14 @@ def test_api_daily(botclient, mocker, ticker, fee, markets):
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
- markets=PropertyMock(return_value=markets)
+ markets=PropertyMock(return_value=markets),
)
rc = client_get(client, f"{BASE_URI}/daily")
assert_response(rc)
- assert len(rc.json()['data']) == 7
- assert rc.json()['stake_currency'] == 'BTC'
- assert rc.json()['fiat_display_currency'] == 'USD'
- assert rc.json()['data'][0]['date'] == str(datetime.now(timezone.utc).date())
+ assert len(rc.json()["data"]) == 7
+ assert rc.json()["stake_currency"] == "BTC"
+ assert rc.json()["fiat_display_currency"] == "USD"
+ assert rc.json()["data"][0]["date"] == str(datetime.now(timezone.utc).date())
def test_api_weekly(botclient, mocker, ticker, fee, markets, time_machine):
@@ -651,17 +716,17 @@ def test_api_weekly(botclient, mocker, ticker, fee, markets, time_machine):
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
- markets=PropertyMock(return_value=markets)
+ markets=PropertyMock(return_value=markets),
)
time_machine.move_to("2023-03-31 21:45:05 +00:00")
rc = client_get(client, f"{BASE_URI}/weekly")
assert_response(rc)
- assert len(rc.json()['data']) == 4
- assert rc.json()['stake_currency'] == 'BTC'
- assert rc.json()['fiat_display_currency'] == 'USD'
+ assert len(rc.json()["data"]) == 4
+ assert rc.json()["stake_currency"] == "BTC"
+ assert rc.json()["fiat_display_currency"] == "USD"
# Moved to monday
- assert rc.json()['data'][0]['date'] == '2023-03-27'
- assert rc.json()['data'][1]['date'] == '2023-03-20'
+ assert rc.json()["data"][0]["date"] == "2023-03-27"
+ assert rc.json()["data"][1]["date"] == "2023-03-20"
def test_api_monthly(botclient, mocker, ticker, fee, markets, time_machine):
@@ -672,50 +737,47 @@ def test_api_monthly(botclient, mocker, ticker, fee, markets, time_machine):
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
- markets=PropertyMock(return_value=markets)
+ markets=PropertyMock(return_value=markets),
)
time_machine.move_to("2023-03-31 21:45:05 +00:00")
rc = client_get(client, f"{BASE_URI}/monthly")
assert_response(rc)
- assert len(rc.json()['data']) == 3
- assert rc.json()['stake_currency'] == 'BTC'
- assert rc.json()['fiat_display_currency'] == 'USD'
- assert rc.json()['data'][0]['date'] == '2023-03-01'
- assert rc.json()['data'][1]['date'] == '2023-02-01'
+ assert len(rc.json()["data"]) == 3
+ assert rc.json()["stake_currency"] == "BTC"
+ assert rc.json()["fiat_display_currency"] == "USD"
+ assert rc.json()["data"][0]["date"] == "2023-03-01"
+ assert rc.json()["data"][1]["date"] == "2023-02-01"
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_api_trades(botclient, mocker, fee, markets, is_short):
ftbot, client = botclient
patch_get_signal(ftbot)
- mocker.patch.multiple(
- EXMS,
- markets=PropertyMock(return_value=markets)
- )
+ mocker.patch.multiple(EXMS, markets=PropertyMock(return_value=markets))
rc = client_get(client, f"{BASE_URI}/trades")
assert_response(rc)
assert len(rc.json()) == 4
- assert rc.json()['trades_count'] == 0
- assert rc.json()['total_trades'] == 0
- assert rc.json()['offset'] == 0
+ assert rc.json()["trades_count"] == 0
+ assert rc.json()["total_trades"] == 0
+ assert rc.json()["offset"] == 0
create_mock_trades(fee, is_short=is_short)
Trade.session.flush()
rc = client_get(client, f"{BASE_URI}/trades")
assert_response(rc)
- assert len(rc.json()['trades']) == 2
- assert rc.json()['trades_count'] == 2
- assert rc.json()['total_trades'] == 2
- assert rc.json()['trades'][0]['is_short'] == is_short
+ assert len(rc.json()["trades"]) == 2
+ assert rc.json()["trades_count"] == 2
+ assert rc.json()["total_trades"] == 2
+ assert rc.json()["trades"][0]["is_short"] == is_short
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
- assert rc.json()['total_trades'] == 2
+ assert len(rc.json()["trades"]) == 1
+ assert rc.json()["trades_count"] == 1
+ assert rc.json()["total_trades"] == 2
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_api_trade_single(botclient, mocker, fee, ticker, markets, is_short):
ftbot, client = botclient
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
@@ -726,18 +788,18 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets, is_short):
)
rc = client_get(client, f"{BASE_URI}/trade/3")
assert_response(rc, 404)
- assert rc.json()['detail'] == 'Trade not found.'
+ assert rc.json()["detail"] == "Trade not found."
Trade.rollback()
create_mock_trades(fee, is_short=is_short)
rc = client_get(client, f"{BASE_URI}/trade/3")
assert_response(rc)
- assert rc.json()['trade_id'] == 3
- assert rc.json()['is_short'] == is_short
+ assert rc.json()["trade_id"] == 3
+ assert rc.json()["is_short"] == is_short
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
ftbot, client = botclient
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
@@ -752,14 +814,14 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
create_mock_trades(fee, is_short=is_short)
- ftbot.strategy.order_types['stoploss_on_exchange'] = True
+ ftbot.strategy.order_types["stoploss_on_exchange"] = True
trades = Trade.session.scalars(select(Trade)).all()
Trade.commit()
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 rc.json()["result_msg"] == "Deleted trade 1. Closed 1 open orders."
assert len(trades) - 1 == len(Trade.session.scalars(select(Trade)).all())
assert cancel_mock.call_count == 1
@@ -772,7 +834,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
assert len(trades) - 1 == len(Trade.session.scalars(select(Trade)).all())
rc = client_delete(client, f"{BASE_URI}/trades/5")
assert_response(rc)
- assert rc.json()['result_msg'] == 'Deleted trade 5. Closed 1 open orders.'
+ assert rc.json()["result_msg"] == "Deleted trade 5. Closed 1 open orders."
assert len(trades) - 2 == len(Trade.session.scalars(select(Trade)).all())
assert stoploss_mock.call_count == 1
@@ -781,7 +843,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
assert_response(rc, 502)
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_api_delete_open_order(botclient, mocker, fee, markets, ticker, is_short):
ftbot, client = botclient
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
@@ -797,29 +859,29 @@ def test_api_delete_open_order(botclient, mocker, fee, markets, ticker, is_short
rc = client_delete(client, f"{BASE_URI}/trades/10/open-order")
assert_response(rc, 502)
- assert 'Invalid trade_id.' in rc.json()['error']
+ assert "Invalid trade_id." in rc.json()["error"]
create_mock_trades(fee, is_short=is_short)
Trade.commit()
rc = client_delete(client, f"{BASE_URI}/trades/5/open-order")
assert_response(rc, 502)
- assert 'No open order for trade_id' in rc.json()['error']
+ assert "No open order for trade_id" in rc.json()["error"]
trade = Trade.get_trades([Trade.id == 6]).first()
- mocker.patch(f'{EXMS}.fetch_order', side_effect=ExchangeError)
+ mocker.patch(f"{EXMS}.fetch_order", side_effect=ExchangeError)
rc = client_delete(client, f"{BASE_URI}/trades/6/open-order")
assert_response(rc, 502)
- assert 'Order not found.' in rc.json()['error']
+ assert "Order not found." in rc.json()["error"]
trade = Trade.get_trades([Trade.id == 6]).first()
- mocker.patch(f'{EXMS}.fetch_order', return_value=trade.orders[-1].to_ccxt_object())
+ mocker.patch(f"{EXMS}.fetch_order", return_value=trade.orders[-1].to_ccxt_object())
rc = client_delete(client, f"{BASE_URI}/trades/6/open-order")
assert_response(rc)
assert cancel_mock.call_count == 1
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_api_trade_reload_trade(botclient, mocker, fee, markets, ticker, is_short):
ftbot, client = botclient
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
@@ -836,7 +898,7 @@ def test_api_trade_reload_trade(botclient, mocker, fee, markets, ticker, is_shor
rc = client_post(client, f"{BASE_URI}/trades/10/reload")
assert_response(rc, 502)
- assert 'Could not find trade with id 10.' in rc.json()['error']
+ assert "Could not find trade with id 10." in rc.json()["error"]
assert ftbot.handle_onexchange_order.call_count == 0
create_mock_trades(fee, is_short=is_short)
@@ -851,31 +913,31 @@ def test_api_logs(botclient):
rc = client_get(client, f"{BASE_URI}/logs")
assert_response(rc)
assert len(rc.json()) == 2
- assert 'logs' in rc.json()
+ assert "logs" in rc.json()
# Using a fixed comparison here would make this test fail!
- assert rc.json()['log_count'] > 1
- assert len(rc.json()['logs']) == rc.json()['log_count']
+ assert rc.json()["log_count"] > 1
+ assert len(rc.json()["logs"]) == rc.json()["log_count"]
- assert isinstance(rc.json()['logs'][0], list)
+ assert isinstance(rc.json()["logs"][0], list)
# date
- assert isinstance(rc.json()['logs'][0][0], str)
+ assert isinstance(rc.json()["logs"][0][0], str)
# created_timestamp
- assert isinstance(rc.json()['logs'][0][1], float)
- assert isinstance(rc.json()['logs'][0][2], str)
- assert isinstance(rc.json()['logs'][0][3], str)
- assert isinstance(rc.json()['logs'][0][4], str)
+ assert isinstance(rc.json()["logs"][0][1], float)
+ assert isinstance(rc.json()["logs"][0][2], str)
+ assert isinstance(rc.json()["logs"][0][3], str)
+ assert isinstance(rc.json()["logs"][0][4], str)
rc1 = client_get(client, f"{BASE_URI}/logs?limit=5")
assert_response(rc1)
assert len(rc1.json()) == 2
- assert 'logs' in rc1.json()
+ assert "logs" in rc1.json()
# Using a fixed comparison here would make this test fail!
- if rc1.json()['log_count'] < 5:
+ if rc1.json()["log_count"] < 5:
# Help debugging random test failure
print(f"rc={rc.json()}")
print(f"rc1={rc1.json()}")
- assert rc1.json()['log_count'] > 2
- assert len(rc1.json()['logs']) == rc1.json()['log_count']
+ assert rc1.json()["log_count"] > 2
+ assert len(rc1.json()["logs"]) == rc1.json()["log_count"]
def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
@@ -886,64 +948,111 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
- markets=PropertyMock(return_value=markets)
+ markets=PropertyMock(return_value=markets),
)
rc = client_get(client, f"{BASE_URI}/edge")
assert_response(rc, 502)
assert rc.json() == {"error": "Error querying /api/v1/edge: Edge is not enabled."}
-@pytest.mark.parametrize('is_short,expected', [
- (
- True,
- {'best_pair': 'ETC/BTC', 'best_rate': -0.5, 'best_pair_profit_ratio': -0.005,
- 'profit_all_coin': 15.382312,
- 'profit_all_fiat': 189894.6470718, 'profit_all_percent_mean': 49.62,
- 'profit_all_ratio_mean': 0.49620917, 'profit_all_percent_sum': 198.48,
- 'profit_all_ratio_sum': 1.98483671, 'profit_all_percent': 1.54,
- 'profit_all_ratio': 0.01538214, 'profit_closed_coin': -0.00673913,
- 'profit_closed_fiat': -83.19455985, 'profit_closed_ratio_mean': -0.0075,
- 'profit_closed_percent_mean': -0.75, 'profit_closed_ratio_sum': -0.015,
- 'profit_closed_percent_sum': -1.5, 'profit_closed_ratio': -6.739057628404269e-06,
- 'profit_closed_percent': -0.0, 'winning_trades': 0, 'losing_trades': 2,
- 'profit_factor': 0.0, 'winrate': 0.0, 'expectancy': -0.0033695635,
- 'expectancy_ratio': -1.0, 'trading_volume': 75.945,
- }
- ),
- (
- False,
- {'best_pair': 'XRP/BTC', 'best_rate': 1.0, 'best_pair_profit_ratio': 0.01,
- 'profit_all_coin': -15.46546305,
- 'profit_all_fiat': -190921.14135225, 'profit_all_percent_mean': -49.62,
- 'profit_all_ratio_mean': -0.49620955, 'profit_all_percent_sum': -198.48,
- 'profit_all_ratio_sum': -1.9848382, 'profit_all_percent': -1.55,
- 'profit_all_ratio': -0.0154654126, 'profit_closed_coin': 0.00073913,
- 'profit_closed_fiat': 9.124559849999999, 'profit_closed_ratio_mean': 0.0075,
- 'profit_closed_percent_mean': 0.75, 'profit_closed_ratio_sum': 0.015,
- 'profit_closed_percent_sum': 1.5, 'profit_closed_ratio': 7.391275897987988e-07,
- 'profit_closed_percent': 0.0, 'winning_trades': 2, 'losing_trades': 0,
- 'profit_factor': None, 'winrate': 1.0, 'expectancy': 0.0003695635,
- 'expectancy_ratio': 100, 'trading_volume': 75.945,
- }
- ),
- (
- None,
- {'best_pair': 'XRP/BTC', 'best_rate': 1.0, 'best_pair_profit_ratio': 0.01,
- 'profit_all_coin': -14.87167525,
- 'profit_all_fiat': -183590.83096125, 'profit_all_percent_mean': 0.13,
- 'profit_all_ratio_mean': 0.0012538324, 'profit_all_percent_sum': 0.5,
- 'profit_all_ratio_sum': 0.005015329, 'profit_all_percent': -1.49,
- 'profit_all_ratio': -0.0148715350, 'profit_closed_coin': -0.00542913,
- 'profit_closed_fiat': -67.02260985, 'profit_closed_ratio_mean': 0.0025,
- 'profit_closed_percent_mean': 0.25, 'profit_closed_ratio_sum': 0.005,
- 'profit_closed_percent_sum': 0.5, 'profit_closed_ratio': -5.429078808526421e-06,
- 'profit_closed_percent': -0.0, 'winning_trades': 1, 'losing_trades': 1,
- 'profit_factor': 0.02775724835771106, 'winrate': 0.5,
- 'expectancy': -0.0027145635000000003, 'expectancy_ratio': -0.48612137582114445,
- 'trading_volume': 75.945,
- }
- )
-])
+@pytest.mark.parametrize(
+ "is_short,expected",
+ [
+ (
+ True,
+ {
+ "best_pair": "ETC/BTC",
+ "best_rate": -0.5,
+ "best_pair_profit_ratio": -0.005,
+ "profit_all_coin": 15.382312,
+ "profit_all_fiat": 189894.6470718,
+ "profit_all_percent_mean": 49.62,
+ "profit_all_ratio_mean": 0.49620917,
+ "profit_all_percent_sum": 198.48,
+ "profit_all_ratio_sum": 1.98483671,
+ "profit_all_percent": 1.54,
+ "profit_all_ratio": 0.01538214,
+ "profit_closed_coin": -0.00673913,
+ "profit_closed_fiat": -83.19455985,
+ "profit_closed_ratio_mean": -0.0075,
+ "profit_closed_percent_mean": -0.75,
+ "profit_closed_ratio_sum": -0.015,
+ "profit_closed_percent_sum": -1.5,
+ "profit_closed_ratio": -6.739057628404269e-06,
+ "profit_closed_percent": -0.0,
+ "winning_trades": 0,
+ "losing_trades": 2,
+ "profit_factor": 0.0,
+ "winrate": 0.0,
+ "expectancy": -0.0033695635,
+ "expectancy_ratio": -1.0,
+ "trading_volume": 75.945,
+ },
+ ),
+ (
+ False,
+ {
+ "best_pair": "XRP/BTC",
+ "best_rate": 1.0,
+ "best_pair_profit_ratio": 0.01,
+ "profit_all_coin": -15.46546305,
+ "profit_all_fiat": -190921.14135225,
+ "profit_all_percent_mean": -49.62,
+ "profit_all_ratio_mean": -0.49620955,
+ "profit_all_percent_sum": -198.48,
+ "profit_all_ratio_sum": -1.9848382,
+ "profit_all_percent": -1.55,
+ "profit_all_ratio": -0.0154654126,
+ "profit_closed_coin": 0.00073913,
+ "profit_closed_fiat": 9.124559849999999,
+ "profit_closed_ratio_mean": 0.0075,
+ "profit_closed_percent_mean": 0.75,
+ "profit_closed_ratio_sum": 0.015,
+ "profit_closed_percent_sum": 1.5,
+ "profit_closed_ratio": 7.391275897987988e-07,
+ "profit_closed_percent": 0.0,
+ "winning_trades": 2,
+ "losing_trades": 0,
+ "profit_factor": None,
+ "winrate": 1.0,
+ "expectancy": 0.0003695635,
+ "expectancy_ratio": 100,
+ "trading_volume": 75.945,
+ },
+ ),
+ (
+ None,
+ {
+ "best_pair": "XRP/BTC",
+ "best_rate": 1.0,
+ "best_pair_profit_ratio": 0.01,
+ "profit_all_coin": -14.87167525,
+ "profit_all_fiat": -183590.83096125,
+ "profit_all_percent_mean": 0.13,
+ "profit_all_ratio_mean": 0.0012538324,
+ "profit_all_percent_sum": 0.5,
+ "profit_all_ratio_sum": 0.005015329,
+ "profit_all_percent": -1.49,
+ "profit_all_ratio": -0.0148715350,
+ "profit_closed_coin": -0.00542913,
+ "profit_closed_fiat": -67.02260985,
+ "profit_closed_ratio_mean": 0.0025,
+ "profit_closed_percent_mean": 0.25,
+ "profit_closed_ratio_sum": 0.005,
+ "profit_closed_percent_sum": 0.5,
+ "profit_closed_ratio": -5.429078808526421e-06,
+ "profit_closed_percent": -0.0,
+ "winning_trades": 1,
+ "losing_trades": 1,
+ "profit_factor": 0.02775724835771106,
+ "winrate": 0.5,
+ "expectancy": -0.0027145635000000003,
+ "expectancy_ratio": -0.48612137582114445,
+ "trading_volume": 75.945,
+ },
+ ),
+ ],
+)
def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected):
ftbot, client = botclient
patch_get_signal(ftbot)
@@ -952,12 +1061,12 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected)
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
- markets=PropertyMock(return_value=markets)
+ markets=PropertyMock(return_value=markets),
)
rc = client_get(client, f"{BASE_URI}/profit")
assert_response(rc, 200)
- assert rc.json()['trade_count'] == 0
+ assert rc.json()["trade_count"] == 0
create_mock_trades(fee, is_short=is_short)
# Simulate fulfilled LIMIT_BUY order for trade
@@ -966,53 +1075,53 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected)
assert_response(rc)
# raise ValueError(rc.json())
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_humanized': ANY,
- 'first_trade_timestamp': ANY,
- 'latest_trade_date': ANY,
- 'latest_trade_humanized': '5 minutes ago',
- 'latest_trade_timestamp': ANY,
- 'profit_all_coin': pytest.approx(expected['profit_all_coin']),
- 'profit_all_fiat': pytest.approx(expected['profit_all_fiat']),
- 'profit_all_percent_mean': pytest.approx(expected['profit_all_percent_mean']),
- 'profit_all_ratio_mean': pytest.approx(expected['profit_all_ratio_mean']),
- 'profit_all_percent_sum': pytest.approx(expected['profit_all_percent_sum']),
- 'profit_all_ratio_sum': pytest.approx(expected['profit_all_ratio_sum']),
- 'profit_all_percent': pytest.approx(expected['profit_all_percent']),
- 'profit_all_ratio': pytest.approx(expected['profit_all_ratio']),
- 'profit_closed_coin': pytest.approx(expected['profit_closed_coin']),
- 'profit_closed_fiat': pytest.approx(expected['profit_closed_fiat']),
- 'profit_closed_ratio_mean': pytest.approx(expected['profit_closed_ratio_mean']),
- 'profit_closed_percent_mean': pytest.approx(expected['profit_closed_percent_mean']),
- 'profit_closed_ratio_sum': pytest.approx(expected['profit_closed_ratio_sum']),
- 'profit_closed_percent_sum': pytest.approx(expected['profit_closed_percent_sum']),
- 'profit_closed_ratio': pytest.approx(expected['profit_closed_ratio']),
- 'profit_closed_percent': pytest.approx(expected['profit_closed_percent']),
- 'trade_count': 6,
- 'closed_trade_count': 2,
- 'winning_trades': expected['winning_trades'],
- 'losing_trades': expected['losing_trades'],
- 'profit_factor': expected['profit_factor'],
- 'winrate': expected['winrate'],
- 'expectancy': expected['expectancy'],
- 'expectancy_ratio': expected['expectancy_ratio'],
- 'max_drawdown': ANY,
- 'max_drawdown_abs': ANY,
- 'max_drawdown_start': ANY,
- 'max_drawdown_start_timestamp': ANY,
- 'max_drawdown_end': ANY,
- 'max_drawdown_end_timestamp': ANY,
- 'trading_volume': expected['trading_volume'],
- 'bot_start_timestamp': 0,
- 'bot_start_date': '',
+ "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_humanized": ANY,
+ "first_trade_timestamp": ANY,
+ "latest_trade_date": ANY,
+ "latest_trade_humanized": "5 minutes ago",
+ "latest_trade_timestamp": ANY,
+ "profit_all_coin": pytest.approx(expected["profit_all_coin"]),
+ "profit_all_fiat": pytest.approx(expected["profit_all_fiat"]),
+ "profit_all_percent_mean": pytest.approx(expected["profit_all_percent_mean"]),
+ "profit_all_ratio_mean": pytest.approx(expected["profit_all_ratio_mean"]),
+ "profit_all_percent_sum": pytest.approx(expected["profit_all_percent_sum"]),
+ "profit_all_ratio_sum": pytest.approx(expected["profit_all_ratio_sum"]),
+ "profit_all_percent": pytest.approx(expected["profit_all_percent"]),
+ "profit_all_ratio": pytest.approx(expected["profit_all_ratio"]),
+ "profit_closed_coin": pytest.approx(expected["profit_closed_coin"]),
+ "profit_closed_fiat": pytest.approx(expected["profit_closed_fiat"]),
+ "profit_closed_ratio_mean": pytest.approx(expected["profit_closed_ratio_mean"]),
+ "profit_closed_percent_mean": pytest.approx(expected["profit_closed_percent_mean"]),
+ "profit_closed_ratio_sum": pytest.approx(expected["profit_closed_ratio_sum"]),
+ "profit_closed_percent_sum": pytest.approx(expected["profit_closed_percent_sum"]),
+ "profit_closed_ratio": pytest.approx(expected["profit_closed_ratio"]),
+ "profit_closed_percent": pytest.approx(expected["profit_closed_percent"]),
+ "trade_count": 6,
+ "closed_trade_count": 2,
+ "winning_trades": expected["winning_trades"],
+ "losing_trades": expected["losing_trades"],
+ "profit_factor": expected["profit_factor"],
+ "winrate": expected["winrate"],
+ "expectancy": expected["expectancy"],
+ "expectancy_ratio": expected["expectancy_ratio"],
+ "max_drawdown": ANY,
+ "max_drawdown_abs": ANY,
+ "max_drawdown_start": ANY,
+ "max_drawdown_start_timestamp": ANY,
+ "max_drawdown_end": ANY,
+ "max_drawdown_end_timestamp": ANY,
+ "trading_volume": expected["trading_volume"],
+ "bot_start_timestamp": 0,
+ "bot_start_date": "",
}
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
def test_api_stats(botclient, mocker, ticker, fee, markets, is_short):
ftbot, client = botclient
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
@@ -1021,24 +1130,24 @@ def test_api_stats(botclient, mocker, ticker, fee, markets, is_short):
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
- markets=PropertyMock(return_value=markets)
+ markets=PropertyMock(return_value=markets),
)
rc = client_get(client, f"{BASE_URI}/stats")
assert_response(rc, 200)
- assert 'durations' in rc.json()
- assert 'exit_reasons' in rc.json()
+ assert "durations" in rc.json()
+ assert "exit_reasons" in rc.json()
create_mock_trades(fee, is_short=is_short)
rc = client_get(client, f"{BASE_URI}/stats")
assert_response(rc, 200)
- assert 'durations' in rc.json()
- assert 'exit_reasons' in rc.json()
+ assert "durations" in rc.json()
+ assert "exit_reasons" in rc.json()
- assert 'wins' in rc.json()['durations']
- assert 'losses' in rc.json()['durations']
- assert 'draws' in rc.json()['durations']
+ assert "wins" in rc.json()["durations"]
+ assert "losses" in rc.json()["durations"]
+ assert "draws" in rc.json()["durations"]
def test_api_performance(botclient, fee):
@@ -1046,9 +1155,9 @@ def test_api_performance(botclient, fee):
patch_get_signal(ftbot)
trade = Trade(
- pair='LTC/ETH',
+ pair="LTC/ETH",
amount=1,
- exchange='binance',
+ exchange="binance",
stake_amount=1,
open_rate=0.245441,
is_open=False,
@@ -1062,10 +1171,10 @@ def test_api_performance(botclient, fee):
Trade.session.add(trade)
trade = Trade(
- pair='XRP/ETH',
+ pair="XRP/ETH",
amount=5,
stake_amount=1,
- exchange='binance',
+ exchange="binance",
open_rate=0.412,
is_open=False,
fee_close=fee.return_value,
@@ -1082,10 +1191,24 @@ def test_api_performance(botclient, fee):
rc = client_get(client, f"{BASE_URI}/performance")
assert_response(rc)
assert len(rc.json()) == 2
- assert rc.json() == [{'count': 1, 'pair': 'LTC/ETH', 'profit': 7.61, 'profit_pct': 7.61,
- 'profit_ratio': 0.07609203, 'profit_abs': 0.0187228},
- {'count': 1, 'pair': 'XRP/ETH', 'profit': -5.57, 'profit_pct': -5.57,
- 'profit_ratio': -0.05570419, 'profit_abs': -0.1150375}]
+ assert rc.json() == [
+ {
+ "count": 1,
+ "pair": "LTC/ETH",
+ "profit": 7.61,
+ "profit_pct": 7.61,
+ "profit_ratio": 0.07609203,
+ "profit_abs": 0.0187228,
+ },
+ {
+ "count": 1,
+ "pair": "XRP/ETH",
+ "profit": -5.57,
+ "profit_pct": -5.57,
+ "profit_ratio": -0.05570419,
+ "profit_abs": -0.1150375,
+ },
+ ]
def test_api_entries(botclient, fee):
@@ -1102,9 +1225,9 @@ def test_api_entries(botclient, fee):
response = rc.json()
assert len(response) == 2
resp = response[0]
- assert resp['enter_tag'] == 'TEST1'
- assert resp['count'] == 1
- assert resp['profit_pct'] == 0.5
+ assert resp["enter_tag"] == "TEST1"
+ assert resp["count"] == 1
+ assert resp["profit_pct"] == 0.5
def test_api_exits(botclient, fee):
@@ -1121,9 +1244,9 @@ def test_api_exits(botclient, fee):
response = rc.json()
assert len(response) == 2
resp = response[0]
- assert resp['exit_reason'] == 'sell_signal'
- assert resp['count'] == 1
- assert resp['profit_pct'] == 0.5
+ assert resp["exit_reason"] == "sell_signal"
+ assert resp["count"] == 1
+ assert resp["profit_pct"] == 0.5
def test_api_mix_tag(botclient, fee):
@@ -1140,17 +1263,18 @@ def test_api_mix_tag(botclient, fee):
response = rc.json()
assert len(response) == 2
resp = response[0]
- assert resp['mix_tag'] == 'TEST1 sell_signal'
- assert resp['count'] == 1
- assert resp['profit_pct'] == 0.5
+ assert resp["mix_tag"] == "TEST1 sell_signal"
+ assert resp["count"] == 1
+ assert resp["profit_pct"] == 0.5
@pytest.mark.parametrize(
- 'is_short,current_rate,open_trade_value',
- [(True, 1.098e-05, 15.0911775),
- (False, 1.099e-05, 15.1668225)])
-def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
- current_rate, open_trade_value):
+ "is_short,current_rate,open_trade_value",
+ [(True, 1.098e-05, 15.0911775), (False, 1.099e-05, 15.1668225)],
+)
+def test_api_status(
+ botclient, mocker, ticker, fee, markets, is_short, current_rate, open_trade_value
+):
ftbot, client = botclient
patch_get_signal(ftbot)
mocker.patch.multiple(
@@ -1171,87 +1295,88 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
assert_response(rc)
assert len(rc.json()) == 4
assert rc.json()[0] == {
- 'amount': 123.0,
- 'amount_requested': 123.0,
- 'close_date': None,
- 'close_timestamp': None,
- 'close_profit': None,
- 'close_profit_pct': None,
- 'close_profit_abs': None,
- 'close_rate': None,
- 'profit_ratio': ANY,
- 'profit_pct': ANY,
- 'profit_abs': ANY,
- 'profit_fiat': ANY,
- 'total_profit_abs': ANY,
- 'total_profit_fiat': ANY,
- 'total_profit_ratio': ANY,
- 'realized_profit': 0.0,
- 'realized_profit_ratio': None,
- 'current_rate': current_rate,
- 'open_date': ANY,
- 'open_timestamp': ANY,
- 'open_fill_date': ANY,
- 'open_fill_timestamp': ANY,
- 'open_rate': 0.123,
- 'pair': 'ETH/BTC',
- 'base_currency': 'ETH',
- 'quote_currency': 'BTC',
- 'stake_amount': 0.001,
- 'max_stake_amount': ANY,
- 'stop_loss_abs': ANY,
- 'stop_loss_pct': ANY,
- 'stop_loss_ratio': ANY,
- 'stoploss_last_update': ANY,
- 'stoploss_last_update_timestamp': ANY,
- 'initial_stop_loss_abs': 0.0,
- 'initial_stop_loss_pct': ANY,
- 'initial_stop_loss_ratio': ANY,
- 'stoploss_current_dist': ANY,
- 'stoploss_current_dist_ratio': ANY,
- 'stoploss_current_dist_pct': ANY,
- 'stoploss_entry_dist': ANY,
- 'stoploss_entry_dist_ratio': ANY,
- 'trade_id': 1,
- 'close_rate_requested': ANY,
- 'fee_close': 0.0025,
- 'fee_close_cost': None,
- 'fee_close_currency': None,
- 'fee_open': 0.0025,
- 'fee_open_cost': None,
- 'fee_open_currency': None,
- 'is_open': True,
+ "amount": 123.0,
+ "amount_requested": 123.0,
+ "close_date": None,
+ "close_timestamp": None,
+ "close_profit": None,
+ "close_profit_pct": None,
+ "close_profit_abs": None,
+ "close_rate": None,
+ "profit_ratio": ANY,
+ "profit_pct": ANY,
+ "profit_abs": ANY,
+ "profit_fiat": ANY,
+ "total_profit_abs": ANY,
+ "total_profit_fiat": ANY,
+ "total_profit_ratio": ANY,
+ "realized_profit": 0.0,
+ "realized_profit_ratio": None,
+ "current_rate": current_rate,
+ "open_date": ANY,
+ "open_timestamp": ANY,
+ "open_fill_date": ANY,
+ "open_fill_timestamp": ANY,
+ "open_rate": 0.123,
+ "pair": "ETH/BTC",
+ "base_currency": "ETH",
+ "quote_currency": "BTC",
+ "stake_amount": 0.001,
+ "max_stake_amount": ANY,
+ "stop_loss_abs": ANY,
+ "stop_loss_pct": ANY,
+ "stop_loss_ratio": ANY,
+ "stoploss_last_update": ANY,
+ "stoploss_last_update_timestamp": ANY,
+ "initial_stop_loss_abs": 0.0,
+ "initial_stop_loss_pct": ANY,
+ "initial_stop_loss_ratio": ANY,
+ "stoploss_current_dist": ANY,
+ "stoploss_current_dist_ratio": ANY,
+ "stoploss_current_dist_pct": ANY,
+ "stoploss_entry_dist": ANY,
+ "stoploss_entry_dist_ratio": ANY,
+ "trade_id": 1,
+ "close_rate_requested": ANY,
+ "fee_close": 0.0025,
+ "fee_close_cost": None,
+ "fee_close_currency": None,
+ "fee_open": 0.0025,
+ "fee_open_cost": None,
+ "fee_open_currency": None,
+ "is_open": True,
"is_short": is_short,
- 'max_rate': ANY,
- 'min_rate': ANY,
- 'open_rate_requested': ANY,
- 'open_trade_value': open_trade_value,
- 'exit_reason': None,
- 'exit_order_status': None,
- 'strategy': CURRENT_TEST_STRATEGY,
- 'enter_tag': None,
- 'timeframe': 5,
- 'exchange': 'binance',
- 'leverage': 1.0,
- 'interest_rate': 0.0,
- 'liquidation_price': None,
- 'funding_fees': None,
- 'trading_mode': ANY,
- 'amount_precision': None,
- 'price_precision': None,
- 'precision_mode': None,
- 'orders': [ANY],
- 'has_open_orders': True,
+ "max_rate": ANY,
+ "min_rate": ANY,
+ "open_rate_requested": ANY,
+ "open_trade_value": open_trade_value,
+ "exit_reason": None,
+ "exit_order_status": None,
+ "strategy": CURRENT_TEST_STRATEGY,
+ "enter_tag": None,
+ "timeframe": 5,
+ "exchange": "binance",
+ "leverage": 1.0,
+ "interest_rate": 0.0,
+ "liquidation_price": None,
+ "funding_fees": None,
+ "trading_mode": ANY,
+ "amount_precision": None,
+ "price_precision": None,
+ "precision_mode": None,
+ "orders": [ANY],
+ "has_open_orders": True,
}
- mocker.patch(f'{EXMS}.get_rate',
- MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
+ mocker.patch(
+ f"{EXMS}.get_rate", MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))
+ )
rc = client_get(client, f"{BASE_URI}/status")
assert_response(rc)
resp_values = rc.json()
assert len(resp_values) == 4
- assert resp_values[0]['profit_abs'] == 0.0
+ assert resp_values[0]["profit_abs"] == 0.0
def test_api_version(botclient):
@@ -1268,65 +1393,67 @@ def test_api_blacklist(botclient, mocker):
rc = client_get(client, f"{BASE_URI}/blacklist")
assert_response(rc)
# DOGE and HOT are not in the markets mock!
- assert rc.json() == {"blacklist": ["DOGE/BTC", "HOT/BTC"],
- "blacklist_expanded": [],
- "length": 2,
- "method": ["StaticPairList"],
- "errors": {},
- }
+ assert rc.json() == {
+ "blacklist": ["DOGE/BTC", "HOT/BTC"],
+ "blacklist_expanded": [],
+ "length": 2,
+ "method": ["StaticPairList"],
+ "errors": {},
+ }
# Add ETH/BTC to blacklist
- rc = client_post(client, f"{BASE_URI}/blacklist",
- data={"blacklist": ["ETH/BTC"]})
+ rc = client_post(client, f"{BASE_URI}/blacklist", data={"blacklist": ["ETH/BTC"]})
assert_response(rc)
- assert rc.json() == {"blacklist": ["DOGE/BTC", "HOT/BTC", "ETH/BTC"],
- "blacklist_expanded": ["ETH/BTC"],
- "length": 3,
- "method": ["StaticPairList"],
- "errors": {},
- }
+ assert rc.json() == {
+ "blacklist": ["DOGE/BTC", "HOT/BTC", "ETH/BTC"],
+ "blacklist_expanded": ["ETH/BTC"],
+ "length": 3,
+ "method": ["StaticPairList"],
+ "errors": {},
+ }
- rc = client_post(client, f"{BASE_URI}/blacklist",
- data={"blacklist": ["XRP/.*"]})
+ rc = client_post(client, f"{BASE_URI}/blacklist", data={"blacklist": ["XRP/.*"]})
assert_response(rc)
- assert rc.json() == {"blacklist": ["DOGE/BTC", "HOT/BTC", "ETH/BTC", "XRP/.*"],
- "blacklist_expanded": ["ETH/BTC", "XRP/BTC", "XRP/USDT"],
- "length": 4,
- "method": ["StaticPairList"],
- "errors": {},
- }
+ assert rc.json() == {
+ "blacklist": ["DOGE/BTC", "HOT/BTC", "ETH/BTC", "XRP/.*"],
+ "blacklist_expanded": ["ETH/BTC", "XRP/BTC", "XRP/USDT"],
+ "length": 4,
+ "method": ["StaticPairList"],
+ "errors": {},
+ }
rc = client_delete(client, f"{BASE_URI}/blacklist?pairs_to_delete=DOGE/BTC")
assert_response(rc)
- assert rc.json() == {"blacklist": ["HOT/BTC", "ETH/BTC", "XRP/.*"],
- "blacklist_expanded": ["ETH/BTC", "XRP/BTC", "XRP/USDT"],
- "length": 3,
- "method": ["StaticPairList"],
- "errors": {},
- }
+ assert rc.json() == {
+ "blacklist": ["HOT/BTC", "ETH/BTC", "XRP/.*"],
+ "blacklist_expanded": ["ETH/BTC", "XRP/BTC", "XRP/USDT"],
+ "length": 3,
+ "method": ["StaticPairList"],
+ "errors": {},
+ }
rc = client_delete(client, f"{BASE_URI}/blacklist?pairs_to_delete=NOTHING/BTC")
assert_response(rc)
- assert rc.json() == {"blacklist": ["HOT/BTC", "ETH/BTC", "XRP/.*"],
- "blacklist_expanded": ["ETH/BTC", "XRP/BTC", "XRP/USDT"],
- "length": 3,
- "method": ["StaticPairList"],
- "errors": {
- "NOTHING/BTC": {
- "error_msg": "Pair NOTHING/BTC is not in the current blacklist."
- }
- },
+ assert rc.json() == {
+ "blacklist": ["HOT/BTC", "ETH/BTC", "XRP/.*"],
+ "blacklist_expanded": ["ETH/BTC", "XRP/BTC", "XRP/USDT"],
+ "length": 3,
+ "method": ["StaticPairList"],
+ "errors": {
+ "NOTHING/BTC": {"error_msg": "Pair NOTHING/BTC is not in the current blacklist."}
+ },
}
rc = client_delete(
- client,
- f"{BASE_URI}/blacklist?pairs_to_delete=HOT/BTC&pairs_to_delete=ETH/BTC")
+ client, f"{BASE_URI}/blacklist?pairs_to_delete=HOT/BTC&pairs_to_delete=ETH/BTC"
+ )
assert_response(rc)
- assert rc.json() == {"blacklist": ["XRP/.*"],
- "blacklist_expanded": ["XRP/BTC", "XRP/USDT"],
- "length": 1,
- "method": ["StaticPairList"],
- "errors": {},
- }
+ assert rc.json() == {
+ "blacklist": ["XRP/.*"],
+ "blacklist_expanded": ["XRP/BTC", "XRP/USDT"],
+ "length": 1,
+ "method": ["StaticPairList"],
+ "errors": {},
+ }
def test_api_whitelist(botclient):
@@ -1335,121 +1462,123 @@ def test_api_whitelist(botclient):
rc = client_get(client, f"{BASE_URI}/whitelist")
assert_response(rc)
assert rc.json() == {
- "whitelist": ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC'],
+ "whitelist": ["ETH/BTC", "LTC/BTC", "XRP/BTC", "NEO/BTC"],
"length": 4,
- "method": ["StaticPairList"]
+ "method": ["StaticPairList"],
}
-@pytest.mark.parametrize('endpoint', [
- 'forcebuy',
- 'forceenter',
-])
+@pytest.mark.parametrize(
+ "endpoint",
+ [
+ "forcebuy",
+ "forceenter",
+ ],
+)
def test_api_force_entry(botclient, mocker, fee, endpoint):
ftbot, client = botclient
- rc = client_post(client, f"{BASE_URI}/{endpoint}",
- data={"pair": "ETH/BTC"})
+ rc = client_post(client, f"{BASE_URI}/{endpoint}", data={"pair": "ETH/BTC"})
assert_response(rc, 502)
assert rc.json() == {"error": f"Error querying /api/v1/{endpoint}: Force_entry not enabled."}
# enable forcebuy
- ftbot.config['force_entry_enable'] = True
+ ftbot.config["force_entry_enable"] = True
fbuy_mock = MagicMock(return_value=None)
mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_entry", fbuy_mock)
- rc = client_post(client, f"{BASE_URI}/{endpoint}",
- data={"pair": "ETH/BTC"})
+ rc = client_post(client, f"{BASE_URI}/{endpoint}", data={"pair": "ETH/BTC"})
assert_response(rc)
assert rc.json() == {"status": "Error entering long trade for pair ETH/BTC."}
# Test creating trade
- fbuy_mock = MagicMock(return_value=Trade(
- pair='ETH/BTC',
- amount=1,
- amount_requested=1,
- exchange='binance',
- stake_amount=1,
- open_rate=0.245441,
- open_date=datetime.now(timezone.utc),
- is_open=False,
- is_short=False,
- fee_close=fee.return_value,
- fee_open=fee.return_value,
- close_rate=0.265441,
- id=22,
- timeframe=5,
- strategy=CURRENT_TEST_STRATEGY,
- trading_mode=TradingMode.SPOT
- ))
+ fbuy_mock = MagicMock(
+ return_value=Trade(
+ pair="ETH/BTC",
+ amount=1,
+ amount_requested=1,
+ exchange="binance",
+ stake_amount=1,
+ open_rate=0.245441,
+ open_date=datetime.now(timezone.utc),
+ is_open=False,
+ is_short=False,
+ fee_close=fee.return_value,
+ fee_open=fee.return_value,
+ close_rate=0.265441,
+ id=22,
+ timeframe=5,
+ strategy=CURRENT_TEST_STRATEGY,
+ trading_mode=TradingMode.SPOT,
+ )
+ )
mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_entry", fbuy_mock)
- rc = client_post(client, f"{BASE_URI}/{endpoint}",
- data={"pair": "ETH/BTC"})
+ rc = client_post(client, f"{BASE_URI}/{endpoint}", data={"pair": "ETH/BTC"})
assert_response(rc)
assert rc.json() == {
- 'amount': 1.0,
- 'amount_requested': 1.0,
- 'trade_id': 22,
- 'close_date': None,
- 'close_timestamp': None,
- 'close_rate': 0.265441,
- 'open_date': ANY,
- 'open_timestamp': ANY,
- 'open_fill_date': ANY,
- 'open_fill_timestamp': ANY,
- 'open_rate': 0.245441,
- 'pair': 'ETH/BTC',
- 'base_currency': 'ETH',
- 'quote_currency': 'BTC',
- 'stake_amount': 1,
- 'max_stake_amount': ANY,
- 'stop_loss_abs': None,
- 'stop_loss_pct': None,
- 'stop_loss_ratio': None,
- 'stoploss_last_update': None,
- 'stoploss_last_update_timestamp': None,
- 'initial_stop_loss_abs': None,
- 'initial_stop_loss_pct': None,
- 'initial_stop_loss_ratio': None,
- 'close_profit': None,
- 'close_profit_pct': None,
- 'close_profit_abs': None,
- 'close_rate_requested': None,
- 'profit_ratio': None,
- 'profit_pct': None,
- 'profit_abs': None,
- 'profit_fiat': None,
- 'realized_profit': 0.0,
- 'realized_profit_ratio': None,
- 'fee_close': 0.0025,
- 'fee_close_cost': None,
- 'fee_close_currency': None,
- 'fee_open': 0.0025,
- 'fee_open_cost': None,
- 'fee_open_currency': None,
- 'is_open': False,
- 'is_short': False,
- 'max_rate': None,
- 'min_rate': None,
- 'open_rate_requested': None,
- 'open_trade_value': 0.24605460,
- 'exit_reason': None,
- 'exit_order_status': None,
- 'strategy': CURRENT_TEST_STRATEGY,
- 'enter_tag': None,
- 'timeframe': 5,
- 'exchange': 'binance',
- 'leverage': None,
- 'interest_rate': None,
- 'liquidation_price': None,
- 'funding_fees': None,
- 'trading_mode': 'spot',
- 'amount_precision': None,
- 'price_precision': None,
- 'precision_mode': None,
- 'has_open_orders': False,
- 'orders': [],
+ "amount": 1.0,
+ "amount_requested": 1.0,
+ "trade_id": 22,
+ "close_date": None,
+ "close_timestamp": None,
+ "close_rate": 0.265441,
+ "open_date": ANY,
+ "open_timestamp": ANY,
+ "open_fill_date": ANY,
+ "open_fill_timestamp": ANY,
+ "open_rate": 0.245441,
+ "pair": "ETH/BTC",
+ "base_currency": "ETH",
+ "quote_currency": "BTC",
+ "stake_amount": 1,
+ "max_stake_amount": ANY,
+ "stop_loss_abs": None,
+ "stop_loss_pct": None,
+ "stop_loss_ratio": None,
+ "stoploss_last_update": None,
+ "stoploss_last_update_timestamp": None,
+ "initial_stop_loss_abs": None,
+ "initial_stop_loss_pct": None,
+ "initial_stop_loss_ratio": None,
+ "close_profit": None,
+ "close_profit_pct": None,
+ "close_profit_abs": None,
+ "close_rate_requested": None,
+ "profit_ratio": None,
+ "profit_pct": None,
+ "profit_abs": None,
+ "profit_fiat": None,
+ "realized_profit": 0.0,
+ "realized_profit_ratio": None,
+ "fee_close": 0.0025,
+ "fee_close_cost": None,
+ "fee_close_currency": None,
+ "fee_open": 0.0025,
+ "fee_open_cost": None,
+ "fee_open_currency": None,
+ "is_open": False,
+ "is_short": False,
+ "max_rate": None,
+ "min_rate": None,
+ "open_rate_requested": None,
+ "open_trade_value": 0.24605460,
+ "exit_reason": None,
+ "exit_order_status": None,
+ "strategy": CURRENT_TEST_STRATEGY,
+ "enter_tag": None,
+ "timeframe": 5,
+ "exchange": "binance",
+ "leverage": None,
+ "interest_rate": None,
+ "liquidation_price": None,
+ "funding_fees": None,
+ "trading_mode": "spot",
+ "amount_precision": None,
+ "price_precision": None,
+ "precision_mode": None,
+ "has_open_orders": False,
+ "orders": [],
}
@@ -1465,8 +1594,7 @@ def test_api_forceexit(botclient, mocker, ticker, fee, markets):
)
patch_get_signal(ftbot)
- rc = client_post(client, f"{BASE_URI}/forceexit",
- data={"tradeid": "1"})
+ rc = client_post(client, f"{BASE_URI}/forceexit", data={"tradeid": "1"})
assert_response(rc, 502)
assert rc.json() == {"error": "Error querying /api/v1/forceexit: invalid argument"}
Trade.rollback()
@@ -1474,20 +1602,20 @@ def test_api_forceexit(botclient, mocker, ticker, fee, markets):
create_mock_trades(fee)
trade = Trade.get_trades([Trade.id == 5]).first()
assert pytest.approx(trade.amount) == 123
- rc = client_post(client, f"{BASE_URI}/forceexit",
- data={"tradeid": "5", "ordertype": "market", "amount": 23})
+ rc = client_post(
+ client, f"{BASE_URI}/forceexit", data={"tradeid": "5", "ordertype": "market", "amount": 23}
+ )
assert_response(rc)
- assert rc.json() == {'result': 'Created exit order for trade 5.'}
+ assert rc.json() == {"result": "Created exit order for trade 5."}
Trade.rollback()
trade = Trade.get_trades([Trade.id == 5]).first()
assert pytest.approx(trade.amount) == 100
assert trade.is_open is True
- rc = client_post(client, f"{BASE_URI}/forceexit",
- data={"tradeid": "5"})
+ rc = client_post(client, f"{BASE_URI}/forceexit", data={"tradeid": "5"})
assert_response(rc)
- assert rc.json() == {'result': 'Created exit order for trade 5.'}
+ assert rc.json() == {"result": "Created exit order for trade 5."}
Trade.rollback()
trade = Trade.get_trades([Trade.id == 5]).first()
@@ -1496,42 +1624,42 @@ def test_api_forceexit(botclient, mocker, ticker, fee, markets):
def test_api_pair_candles(botclient, ohlcv_history):
ftbot, client = botclient
- timeframe = '5m'
+ timeframe = "5m"
amount = 3
# No pair
- rc = client_get(client,
- f"{BASE_URI}/pair_candles?limit={amount}&timeframe={timeframe}")
+ rc = client_get(client, f"{BASE_URI}/pair_candles?limit={amount}&timeframe={timeframe}")
assert_response(rc, 422)
# No timeframe
- rc = client_get(client,
- f"{BASE_URI}/pair_candles?pair=XRP%2FBTC")
+ rc = client_get(client, f"{BASE_URI}/pair_candles?pair=XRP%2FBTC")
assert_response(rc, 422)
- rc = client_get(client,
- f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}")
+ rc = client_get(
+ client, f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}"
+ )
assert_response(rc)
- assert 'columns' in rc.json()
- assert 'data_start_ts' in rc.json()
- assert 'data_start' in rc.json()
- assert 'data_stop' in rc.json()
- assert 'data_stop_ts' in rc.json()
- assert len(rc.json()['data']) == 0
- ohlcv_history['sma'] = ohlcv_history['close'].rolling(2).mean()
- ohlcv_history['sma2'] = ohlcv_history['close'].rolling(2).mean()
- ohlcv_history['enter_long'] = 0
- ohlcv_history.loc[1, 'enter_long'] = 1
- ohlcv_history['exit_long'] = 0
- ohlcv_history['enter_short'] = 0
- ohlcv_history['exit_short'] = 0
+ assert "columns" in rc.json()
+ assert "data_start_ts" in rc.json()
+ assert "data_start" in rc.json()
+ assert "data_stop" in rc.json()
+ assert "data_stop_ts" in rc.json()
+ assert len(rc.json()["data"]) == 0
+ ohlcv_history["sma"] = ohlcv_history["close"].rolling(2).mean()
+ ohlcv_history["sma2"] = ohlcv_history["close"].rolling(2).mean()
+ ohlcv_history["enter_long"] = 0
+ ohlcv_history.loc[1, "enter_long"] = 1
+ ohlcv_history["exit_long"] = 0
+ ohlcv_history["enter_short"] = 0
+ ohlcv_history["exit_short"] = 0
ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history, CandleType.SPOT)
- for call in ('get', 'post'):
- if call == 'get':
+ for call in ("get", "post"):
+ if call == "get":
rc = client_get(
client,
- f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}")
+ f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}",
+ )
else:
rc = client_post(
client,
@@ -1540,133 +1668,315 @@ def test_api_pair_candles(botclient, ohlcv_history):
"pair": "XRP/BTC",
"timeframe": timeframe,
"limit": amount,
- "columns": ['sma'],
- }
+ "columns": ["sma"],
+ },
)
assert_response(rc)
resp = rc.json()
- assert 'strategy' in resp
- assert resp['strategy'] == CURRENT_TEST_STRATEGY
- assert 'columns' in resp
- assert 'data_start_ts' in resp
- assert 'data_start' in resp
- assert 'data_stop' in resp
- assert 'data_stop_ts' in resp
- assert resp['data_start'] == '2017-11-26 08:50:00+00:00'
- assert resp['data_start_ts'] == 1511686200000
- assert resp['data_stop'] == '2017-11-26 09:00:00+00:00'
- assert resp['data_stop_ts'] == 1511686800000
- assert isinstance(resp['columns'], list)
+ assert "strategy" in resp
+ assert resp["strategy"] == CURRENT_TEST_STRATEGY
+ assert "columns" in resp
+ assert "data_start_ts" in resp
+ assert "data_start" in resp
+ assert "data_stop" in resp
+ assert "data_stop_ts" in resp
+ assert resp["data_start"] == "2017-11-26 08:50:00+00:00"
+ assert resp["data_start_ts"] == 1511686200000
+ assert resp["data_stop"] == "2017-11-26 09:00:00+00:00"
+ assert resp["data_stop_ts"] == 1511686800000
+ assert isinstance(resp["columns"], list)
base_cols = {
- 'date', 'open', 'high', 'low', 'close', 'volume',
- 'sma', 'enter_long', 'exit_long', 'enter_short', 'exit_short', '__date_ts',
- '_enter_long_signal_close', '_exit_long_signal_close',
- '_enter_short_signal_close', '_exit_short_signal_close'
- }
- if call == 'get':
- assert set(resp['columns']) == base_cols.union({'sma2'})
+ "date",
+ "open",
+ "high",
+ "low",
+ "close",
+ "volume",
+ "sma",
+ "enter_long",
+ "exit_long",
+ "enter_short",
+ "exit_short",
+ "__date_ts",
+ "_enter_long_signal_close",
+ "_exit_long_signal_close",
+ "_enter_short_signal_close",
+ "_exit_short_signal_close",
+ }
+ if call == "get":
+ assert set(resp["columns"]) == base_cols.union({"sma2"})
else:
- assert set(resp['columns']) == base_cols
+ assert set(resp["columns"]) == base_cols
# All columns doesn't include the internal columns
- assert set(resp['all_columns']) == {
- 'date', 'open', 'high', 'low', 'close', 'volume',
- 'sma', 'sma2', 'enter_long', 'exit_long', 'enter_short', 'exit_short'
+ assert set(resp["all_columns"]) == {
+ "date",
+ "open",
+ "high",
+ "low",
+ "close",
+ "volume",
+ "sma",
+ "sma2",
+ "enter_long",
+ "exit_long",
+ "enter_short",
+ "exit_short",
}
- assert 'pair' in resp
- assert resp['pair'] == 'XRP/BTC'
+ assert "pair" in resp
+ assert resp["pair"] == "XRP/BTC"
- assert 'data' in resp
- assert len(resp['data']) == amount
- if call == 'get':
- assert len(resp['data'][0]) == 17
- assert resp['data'] == [
- ['2017-11-26T08:50:00Z', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05,
- 0.0877869, None, None, 0, 0, 0, 0, 1511686200000, None, None, None, None],
- ['2017-11-26T08:55:00Z', 8.88e-05, 8.942e-05, 8.88e-05, 8.893e-05, 0.05874751,
- 8.886500000000001e-05, 8.886500000000001e-05, 1, 0, 0, 0, 1511686500000,
- 8.893e-05, None, None, None],
- ['2017-11-26T09:00:00Z', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
- 0.7039405, 8.885e-05, 8.885e-05, 0, 0, 0, 0, 1511686800000, None, None, None, None
- ]
+ assert "data" in resp
+ assert len(resp["data"]) == amount
+ if call == "get":
+ assert len(resp["data"][0]) == 17
+ assert resp["data"] == [
+ [
+ "2017-11-26T08:50:00Z",
+ 8.794e-05,
+ 8.948e-05,
+ 8.794e-05,
+ 8.88e-05,
+ 0.0877869,
+ None,
+ None,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1511686200000,
+ None,
+ None,
+ None,
+ None,
+ ],
+ [
+ "2017-11-26T08:55:00Z",
+ 8.88e-05,
+ 8.942e-05,
+ 8.88e-05,
+ 8.893e-05,
+ 0.05874751,
+ 8.886500000000001e-05,
+ 8.886500000000001e-05,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1511686500000,
+ 8.893e-05,
+ None,
+ None,
+ None,
+ ],
+ [
+ "2017-11-26T09:00:00Z",
+ 8.891e-05,
+ 8.893e-05,
+ 8.875e-05,
+ 8.877e-05,
+ 0.7039405,
+ 8.885e-05,
+ 8.885e-05,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1511686800000,
+ None,
+ None,
+ None,
+ None,
+ ],
]
else:
- assert len(resp['data'][0]) == 16
- assert resp['data'] == [
- ['2017-11-26T08:50:00Z', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05,
- 0.0877869, None, 0, 0, 0, 0, 1511686200000, None, None, None, None],
- ['2017-11-26T08:55:00Z', 8.88e-05, 8.942e-05, 8.88e-05, 8.893e-05, 0.05874751,
- 8.886500000000001e-05, 1, 0, 0, 0, 1511686500000,
- 8.893e-05, None, None, None],
- ['2017-11-26T09:00:00Z', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
- 0.7039405, 8.885e-05, 0, 0, 0, 0, 1511686800000, None, None, None, None
- ]
+ assert len(resp["data"][0]) == 16
+ assert resp["data"] == [
+ [
+ "2017-11-26T08:50:00Z",
+ 8.794e-05,
+ 8.948e-05,
+ 8.794e-05,
+ 8.88e-05,
+ 0.0877869,
+ None,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1511686200000,
+ None,
+ None,
+ None,
+ None,
+ ],
+ [
+ "2017-11-26T08:55:00Z",
+ 8.88e-05,
+ 8.942e-05,
+ 8.88e-05,
+ 8.893e-05,
+ 0.05874751,
+ 8.886500000000001e-05,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1511686500000,
+ 8.893e-05,
+ None,
+ None,
+ None,
+ ],
+ [
+ "2017-11-26T09:00:00Z",
+ 8.891e-05,
+ 8.893e-05,
+ 8.875e-05,
+ 8.877e-05,
+ 0.7039405,
+ 8.885e-05,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1511686800000,
+ None,
+ None,
+ None,
+ None,
+ ],
]
# prep for next test
- ohlcv_history['exit_long'] = ohlcv_history['exit_long'].astype('float64')
- ohlcv_history.at[0, 'exit_long'] = float('inf')
- ohlcv_history['date1'] = ohlcv_history['date']
- ohlcv_history.at[0, 'date1'] = pd.NaT
+ ohlcv_history["exit_long"] = ohlcv_history["exit_long"].astype("float64")
+ ohlcv_history.at[0, "exit_long"] = float("inf")
+ ohlcv_history["date1"] = ohlcv_history["date"]
+ ohlcv_history.at[0, "date1"] = pd.NaT
ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history, CandleType.SPOT)
- rc = client_get(client,
- f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}")
+ rc = client_get(
+ client, f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}"
+ )
assert_response(rc)
- assert (rc.json()['data'] ==
- [['2017-11-26T08:50:00Z', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05, 0.0877869,
- None, None, 0, None, 0, 0, None, 1511686200000, None, None, None, None],
- ['2017-11-26T08:55:00Z', 8.88e-05, 8.942e-05, 8.88e-05,
- 8.893e-05, 0.05874751, 8.886500000000001e-05, 8.886500000000001e-05, 1, 0.0, 0,
- 0, '2017-11-26T08:55:00Z', 1511686500000, 8.893e-05, None, None, None],
- ['2017-11-26T09:00:00Z', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
- 0.7039405, 8.885e-05, 8.885e-05, 0, 0.0, 0, 0, '2017-11-26T09:00:00Z',
- 1511686800000, None, None, None, None]
- ])
+ assert rc.json()["data"] == [
+ [
+ "2017-11-26T08:50:00Z",
+ 8.794e-05,
+ 8.948e-05,
+ 8.794e-05,
+ 8.88e-05,
+ 0.0877869,
+ None,
+ None,
+ 0,
+ None,
+ 0,
+ 0,
+ None,
+ 1511686200000,
+ None,
+ None,
+ None,
+ None,
+ ],
+ [
+ "2017-11-26T08:55:00Z",
+ 8.88e-05,
+ 8.942e-05,
+ 8.88e-05,
+ 8.893e-05,
+ 0.05874751,
+ 8.886500000000001e-05,
+ 8.886500000000001e-05,
+ 1,
+ 0.0,
+ 0,
+ 0,
+ "2017-11-26T08:55:00Z",
+ 1511686500000,
+ 8.893e-05,
+ None,
+ None,
+ None,
+ ],
+ [
+ "2017-11-26T09:00:00Z",
+ 8.891e-05,
+ 8.893e-05,
+ 8.875e-05,
+ 8.877e-05,
+ 0.7039405,
+ 8.885e-05,
+ 8.885e-05,
+ 0,
+ 0.0,
+ 0,
+ 0,
+ "2017-11-26T09:00:00Z",
+ 1511686800000,
+ None,
+ None,
+ None,
+ None,
+ ],
+ ]
def test_api_pair_history(botclient, tmp_path, mocker):
_ftbot, client = botclient
- _ftbot.config['user_data_dir'] = tmp_path
+ _ftbot.config["user_data_dir"] = tmp_path
- timeframe = '5m'
- lfm = mocker.patch('freqtrade.strategy.interface.IStrategy.load_freqAI_model')
+ timeframe = "5m"
+ lfm = mocker.patch("freqtrade.strategy.interface.IStrategy.load_freqAI_model")
# No pair
- rc = client_get(client,
- f"{BASE_URI}/pair_history?timeframe={timeframe}"
- f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}")
+ rc = client_get(
+ client,
+ f"{BASE_URI}/pair_history?timeframe={timeframe}"
+ f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}",
+ )
assert_response(rc, 422)
# No Timeframe
- rc = client_get(client,
- f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC"
- f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}")
+ rc = client_get(
+ client,
+ f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC"
+ f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}",
+ )
assert_response(rc, 422)
# No timerange
- rc = client_get(client,
- f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
- f"&strategy={CURRENT_TEST_STRATEGY}")
+ rc = client_get(
+ client,
+ f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
+ f"&strategy={CURRENT_TEST_STRATEGY}",
+ )
assert_response(rc, 422)
# No strategy
- rc = client_get(client,
- f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
- "&timerange=20180111-20180112")
+ rc = client_get(
+ client,
+ f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
+ "&timerange=20180111-20180112",
+ )
assert_response(rc, 422)
# Invalid strategy
- rc = client_get(client,
- f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
- "&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}11")
+ rc = client_get(
+ client,
+ f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
+ "&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}11",
+ )
assert_response(rc, 502)
# Working
- for call in ('get', 'post'):
- if call == 'get':
- rc = client_get(client,
- f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
- f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}")
+ for call in ("get", "post"):
+ if call == "get":
+ rc = client_get(
+ client,
+ f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
+ f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}",
+ )
else:
rc = client_post(
client,
@@ -1676,42 +1986,45 @@ def test_api_pair_history(botclient, tmp_path, mocker):
"timeframe": timeframe,
"timerange": "20180111-20180112",
"strategy": CURRENT_TEST_STRATEGY,
- "columns": ['rsi', 'fastd', 'fastk'],
- })
+ "columns": ["rsi", "fastd", "fastk"],
+ },
+ )
assert_response(rc, 200)
result = rc.json()
- assert result['length'] == 289
- assert len(result['data']) == result['length']
- assert 'columns' in result
- assert 'data' in result
- data = result['data']
+ assert result["length"] == 289
+ assert len(result["data"]) == result["length"]
+ assert "columns" in result
+ assert "data" in result
+ data = result["data"]
assert len(data) == 289
- col_count = 30 if call == 'get' else 18
+ col_count = 30 if call == "get" else 18
# analyzed DF has 30 columns
- assert len(result['columns']) == col_count
- assert len(result['all_columns']) == 25
+ assert len(result["columns"]) == col_count
+ assert len(result["all_columns"]) == 25
assert len(data[0]) == col_count
- date_col_idx = [idx for idx, c in enumerate(result['columns']) if c == 'date'][0]
- rsi_col_idx = [idx for idx, c in enumerate(result['columns']) if c == 'rsi'][0]
+ date_col_idx = [idx for idx, c in enumerate(result["columns"]) if c == "date"][0]
+ rsi_col_idx = [idx for idx, c in enumerate(result["columns"]) if c == "rsi"][0]
- assert data[0][date_col_idx] == '2018-01-11T00:00:00Z'
+ assert data[0][date_col_idx] == "2018-01-11T00:00:00Z"
assert data[0][rsi_col_idx] is not None
assert data[0][rsi_col_idx] > 0
assert lfm.call_count == 1
- assert result['pair'] == 'UNITTEST/BTC'
- assert result['strategy'] == CURRENT_TEST_STRATEGY
- assert result['data_start'] == '2018-01-11 00:00:00+00:00'
- assert result['data_start_ts'] == 1515628800000
- assert result['data_stop'] == '2018-01-12 00:00:00+00:00'
- assert result['data_stop_ts'] == 1515715200000
+ assert result["pair"] == "UNITTEST/BTC"
+ assert result["strategy"] == CURRENT_TEST_STRATEGY
+ assert result["data_start"] == "2018-01-11 00:00:00+00:00"
+ assert result["data_start_ts"] == 1515628800000
+ assert result["data_stop"] == "2018-01-12 00:00:00+00:00"
+ assert result["data_stop_ts"] == 1515715200000
lfm.reset_mock()
# No data found
- if call == 'get':
- rc = client_get(client,
- f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
- f"&timerange=20200111-20200112&strategy={CURRENT_TEST_STRATEGY}")
+ if call == "get":
+ rc = client_get(
+ client,
+ f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
+ f"&timerange=20200111-20200112&strategy={CURRENT_TEST_STRATEGY}",
+ )
else:
rc = client_post(
client,
@@ -1721,52 +2034,53 @@ def test_api_pair_history(botclient, tmp_path, mocker):
"timeframe": timeframe,
"timerange": "20200111-20200112",
"strategy": CURRENT_TEST_STRATEGY,
- "columns": ['rsi', 'fastd', 'fastk'],
- })
+ "columns": ["rsi", "fastd", "fastk"],
+ },
+ )
assert_response(rc, 502)
- assert rc.json()['detail'] == ("No data for UNITTEST/BTC, 5m in 20200111-20200112 found.")
+ assert rc.json()["detail"] == ("No data for UNITTEST/BTC, 5m in 20200111-20200112 found.")
def test_api_plot_config(botclient, mocker, tmp_path):
ftbot, client = botclient
- ftbot.config['user_data_dir'] = tmp_path
+ ftbot.config["user_data_dir"] = tmp_path
rc = client_get(client, f"{BASE_URI}/plot_config")
assert_response(rc)
assert rc.json() == {}
ftbot.strategy.plot_config = {
- 'main_plot': {'sma': {}},
- 'subplots': {'RSI': {'rsi': {'color': 'red'}}}
+ "main_plot": {"sma": {}},
+ "subplots": {"RSI": {"rsi": {"color": "red"}}},
}
rc = client_get(client, f"{BASE_URI}/plot_config")
assert_response(rc)
assert rc.json() == ftbot.strategy.plot_config
- assert isinstance(rc.json()['main_plot'], dict)
- assert isinstance(rc.json()['subplots'], dict)
+ assert isinstance(rc.json()["main_plot"], dict)
+ assert isinstance(rc.json()["subplots"], dict)
- ftbot.strategy.plot_config = {'main_plot': {'sma': {}}}
+ ftbot.strategy.plot_config = {"main_plot": {"sma": {}}}
rc = client_get(client, f"{BASE_URI}/plot_config")
assert_response(rc)
- assert isinstance(rc.json()['main_plot'], dict)
- assert isinstance(rc.json()['subplots'], dict)
+ assert isinstance(rc.json()["main_plot"], dict)
+ assert isinstance(rc.json()["subplots"], dict)
rc = client_get(client, f"{BASE_URI}/plot_config?strategy=freqai_test_classifier")
assert_response(rc)
res = rc.json()
- assert 'target_roi' in res['subplots']
- assert 'do_predict' in res['subplots']
+ assert "target_roi" in res["subplots"]
+ assert "do_predict" in res["subplots"]
rc = client_get(client, f"{BASE_URI}/plot_config?strategy=HyperoptableStrategy")
assert_response(rc)
- assert rc.json()['subplots'] == {}
+ assert rc.json()["subplots"] == {}
rc = client_get(client, f"{BASE_URI}/plot_config?strategy=NotAStrategy")
assert_response(rc, 502)
- assert rc.json()['detail'] is not None
+ assert rc.json()["detail"] is not None
- mocker.patch('freqtrade.rpc.api_server.api_v1.get_rpc_optional', return_value=None)
+ mocker.patch("freqtrade.rpc.api_server.api_v1.get_rpc_optional", return_value=None)
rc = client_get(client, f"{BASE_URI}/plot_config")
assert_response(rc)
@@ -1774,40 +2088,42 @@ def test_api_plot_config(botclient, mocker, tmp_path):
def test_api_strategies(botclient, tmp_path):
ftbot, client = botclient
- ftbot.config['user_data_dir'] = tmp_path
+ ftbot.config["user_data_dir"] = tmp_path
rc = client_get(client, f"{BASE_URI}/strategies")
assert_response(rc)
- assert rc.json() == {'strategies': [
- 'HyperoptableStrategy',
- 'HyperoptableStrategyV2',
- 'InformativeDecoratorTest',
- 'StrategyTestV2',
- 'StrategyTestV3',
- 'StrategyTestV3CustomEntryPrice',
- 'StrategyTestV3Futures',
- 'freqai_rl_test_strat',
- 'freqai_test_classifier',
- 'freqai_test_multimodel_classifier_strat',
- 'freqai_test_multimodel_strat',
- 'freqai_test_strat',
- 'strategy_test_v3_recursive_issue'
- ]}
+ assert rc.json() == {
+ "strategies": [
+ "HyperoptableStrategy",
+ "HyperoptableStrategyV2",
+ "InformativeDecoratorTest",
+ "StrategyTestV2",
+ "StrategyTestV3",
+ "StrategyTestV3CustomEntryPrice",
+ "StrategyTestV3Futures",
+ "freqai_rl_test_strat",
+ "freqai_test_classifier",
+ "freqai_test_multimodel_classifier_strat",
+ "freqai_test_multimodel_strat",
+ "freqai_test_strat",
+ "strategy_test_v3_recursive_issue",
+ ]
+ }
def test_api_strategy(botclient, tmp_path, mocker):
_ftbot, client = botclient
- _ftbot.config['user_data_dir'] = tmp_path
+ _ftbot.config["user_data_dir"] = tmp_path
rc = client_get(client, f"{BASE_URI}/strategy/{CURRENT_TEST_STRATEGY}")
assert_response(rc)
- assert rc.json()['strategy'] == CURRENT_TEST_STRATEGY
+ assert rc.json()["strategy"] == CURRENT_TEST_STRATEGY
data = (Path(__file__).parents[1] / "strategy/strats/strategy_test_v3.py").read_text()
- assert rc.json()['code'] == data
+ assert rc.json()["code"] == data
rc = client_get(client, f"{BASE_URI}/strategy/NoStrat")
assert_response(rc, 404)
@@ -1815,8 +2131,10 @@ def test_api_strategy(botclient, tmp_path, mocker):
# Disallow base64 strategies
rc = client_get(client, f"{BASE_URI}/strategy/xx:cHJpbnQoImhlbGxvIHdvcmxkIik=")
assert_response(rc, 500)
- mocker.patch('freqtrade.resolvers.strategy_resolver.StrategyResolver._load_strategy',
- side_effect=Exception("Test"))
+ mocker.patch(
+ "freqtrade.resolvers.strategy_resolver.StrategyResolver._load_strategy",
+ side_effect=Exception("Test"),
+ )
rc = client_get(client, f"{BASE_URI}/strategy/NoStrat")
assert_response(rc, 502)
@@ -1828,149 +2146,142 @@ def test_api_exchanges(botclient):
rc = client_get(client, f"{BASE_URI}/exchanges")
assert_response(rc)
response = rc.json()
- assert isinstance(response['exchanges'], list)
- assert len(response['exchanges']) > 20
- okx = [x for x in response['exchanges'] if x['name'] == 'okx'][0]
+ assert isinstance(response["exchanges"], list)
+ assert len(response["exchanges"]) > 20
+ okx = [x for x in response["exchanges"] if x["name"] == "okx"][0]
assert okx == {
"name": "okx",
"valid": True,
"supported": True,
"comment": "",
"trade_modes": [
- {
- "trading_mode": "spot",
- "margin_mode": ""
- },
- {
- "trading_mode": "futures",
- "margin_mode": "isolated"
- }
- ]
+ {"trading_mode": "spot", "margin_mode": ""},
+ {"trading_mode": "futures", "margin_mode": "isolated"},
+ ],
}
- mexc = [x for x in response['exchanges'] if x['name'] == 'mexc'][0]
+ mexc = [x for x in response["exchanges"] if x["name"] == "mexc"][0]
assert mexc == {
"name": "mexc",
"valid": True,
"supported": False,
"comment": "",
- "trade_modes": [
- {
- "trading_mode": "spot",
- "margin_mode": ""
- }
- ]
+ "trade_modes": [{"trading_mode": "spot", "margin_mode": ""}],
}
def test_api_freqaimodels(botclient, tmp_path, mocker):
ftbot, client = botclient
- ftbot.config['user_data_dir'] = tmp_path
+ ftbot.config["user_data_dir"] = tmp_path
mocker.patch(
"freqtrade.resolvers.freqaimodel_resolver.FreqaiModelResolver.search_all_objects",
return_value=[
- {'name': 'LightGBMClassifier'},
- {'name': 'LightGBMClassifierMultiTarget'},
- {'name': 'LightGBMRegressor'},
- {'name': 'LightGBMRegressorMultiTarget'},
- {'name': 'ReinforcementLearner'},
- {'name': 'ReinforcementLearner_multiproc'},
- {'name': 'SKlearnRandomForestClassifier'},
- {'name': 'XGBoostClassifier'},
- {'name': 'XGBoostRFClassifier'},
- {'name': 'XGBoostRFRegressor'},
- {'name': 'XGBoostRegressor'},
- {'name': 'XGBoostRegressorMultiTarget'},
- ])
+ {"name": "LightGBMClassifier"},
+ {"name": "LightGBMClassifierMultiTarget"},
+ {"name": "LightGBMRegressor"},
+ {"name": "LightGBMRegressorMultiTarget"},
+ {"name": "ReinforcementLearner"},
+ {"name": "ReinforcementLearner_multiproc"},
+ {"name": "SKlearnRandomForestClassifier"},
+ {"name": "XGBoostClassifier"},
+ {"name": "XGBoostRFClassifier"},
+ {"name": "XGBoostRFRegressor"},
+ {"name": "XGBoostRegressor"},
+ {"name": "XGBoostRegressorMultiTarget"},
+ ],
+ )
rc = client_get(client, f"{BASE_URI}/freqaimodels")
assert_response(rc)
- assert rc.json() == {'freqaimodels': [
- 'LightGBMClassifier',
- 'LightGBMClassifierMultiTarget',
- 'LightGBMRegressor',
- 'LightGBMRegressorMultiTarget',
- 'ReinforcementLearner',
- 'ReinforcementLearner_multiproc',
- 'SKlearnRandomForestClassifier',
- 'XGBoostClassifier',
- 'XGBoostRFClassifier',
- 'XGBoostRFRegressor',
- 'XGBoostRegressor',
- 'XGBoostRegressorMultiTarget'
- ]}
+ assert rc.json() == {
+ "freqaimodels": [
+ "LightGBMClassifier",
+ "LightGBMClassifierMultiTarget",
+ "LightGBMRegressor",
+ "LightGBMRegressorMultiTarget",
+ "ReinforcementLearner",
+ "ReinforcementLearner_multiproc",
+ "SKlearnRandomForestClassifier",
+ "XGBoostClassifier",
+ "XGBoostRFClassifier",
+ "XGBoostRFRegressor",
+ "XGBoostRegressor",
+ "XGBoostRegressorMultiTarget",
+ ]
+ }
def test_api_pairlists_available(botclient, tmp_path):
ftbot, client = botclient
- ftbot.config['user_data_dir'] = tmp_path
+ ftbot.config["user_data_dir"] = tmp_path
rc = client_get(client, f"{BASE_URI}/pairlists/available")
assert_response(rc, 503)
- assert rc.json()['detail'] == 'Bot is not in the correct state.'
+ assert rc.json()["detail"] == "Bot is not in the correct state."
- ftbot.config['runmode'] = RunMode.WEBSERVER
+ ftbot.config["runmode"] = RunMode.WEBSERVER
rc = client_get(client, f"{BASE_URI}/pairlists/available")
assert_response(rc)
response = rc.json()
- assert isinstance(response['pairlists'], list)
- assert len(response['pairlists']) > 0
+ assert isinstance(response["pairlists"], list)
+ assert len(response["pairlists"]) > 0
- assert len([r for r in response['pairlists'] if r['name'] == 'AgeFilter']) == 1
- assert len([r for r in response['pairlists'] if r['name'] == 'VolumePairList']) == 1
- assert len([r for r in response['pairlists'] if r['name'] == 'StaticPairList']) == 1
+ assert len([r for r in response["pairlists"] if r["name"] == "AgeFilter"]) == 1
+ assert len([r for r in response["pairlists"] if r["name"] == "VolumePairList"]) == 1
+ assert len([r for r in response["pairlists"] if r["name"] == "StaticPairList"]) == 1
- volumepl = [r for r in response['pairlists'] if r['name'] == 'VolumePairList'][0]
- assert volumepl['is_pairlist_generator'] is True
- assert len(volumepl['params']) > 1
- age_pl = [r for r in response['pairlists'] if r['name'] == 'AgeFilter'][0]
- assert age_pl['is_pairlist_generator'] is False
- assert len(volumepl['params']) > 2
+ volumepl = [r for r in response["pairlists"] if r["name"] == "VolumePairList"][0]
+ assert volumepl["is_pairlist_generator"] is True
+ assert len(volumepl["params"]) > 1
+ age_pl = [r for r in response["pairlists"] if r["name"] == "AgeFilter"][0]
+ assert age_pl["is_pairlist_generator"] is False
+ assert len(volumepl["params"]) > 2
def test_api_pairlists_evaluate(botclient, tmp_path, mocker):
ftbot, client = botclient
- ftbot.config['user_data_dir'] = tmp_path
+ ftbot.config["user_data_dir"] = tmp_path
rc = client_get(client, f"{BASE_URI}/pairlists/evaluate/randomJob")
assert_response(rc, 503)
- assert rc.json()['detail'] == 'Bot is not in the correct state.'
+ assert rc.json()["detail"] == "Bot is not in the correct state."
- ftbot.config['runmode'] = RunMode.WEBSERVER
+ ftbot.config["runmode"] = RunMode.WEBSERVER
rc = client_get(client, f"{BASE_URI}/pairlists/evaluate/randomJob")
assert_response(rc, 404)
- assert rc.json()['detail'] == 'Job not found.'
+ assert rc.json()["detail"] == "Job not found."
body = {
"pairlists": [
- {"method": "StaticPairList", },
+ {
+ "method": "StaticPairList",
+ },
],
- "blacklist": [
- ],
- "stake_currency": "BTC"
+ "blacklist": [],
+ "stake_currency": "BTC",
}
# Fail, already running
ApiBG.pairlist_running = True
rc = client_post(client, f"{BASE_URI}/pairlists/evaluate", body)
assert_response(rc, 400)
- assert rc.json()['detail'] == 'Pairlist evaluation is already running.'
+ assert rc.json()["detail"] == "Pairlist evaluation is already running."
# should start the run
ApiBG.pairlist_running = False
rc = client_post(client, f"{BASE_URI}/pairlists/evaluate", body)
assert_response(rc)
- assert rc.json()['status'] == 'Pairlist evaluation started in background.'
- job_id = rc.json()['job_id']
+ assert rc.json()["status"] == "Pairlist evaluation started in background."
+ job_id = rc.json()["job_id"]
rc = client_get(client, f"{BASE_URI}/background/RandomJob")
assert_response(rc, 404)
- assert rc.json()['detail'] == 'Job not found.'
+ assert rc.json()["detail"] == "Job not found."
# Background list
rc = client_get(client, f"{BASE_URI}/background")
@@ -1978,42 +2289,47 @@ def test_api_pairlists_evaluate(botclient, tmp_path, mocker):
response = rc.json()
assert isinstance(response, list)
assert len(response) == 1
- assert response[0]['job_id'] == job_id
+ assert response[0]["job_id"] == job_id
# Get individual job
rc = client_get(client, f"{BASE_URI}/background/{job_id}")
assert_response(rc)
response = rc.json()
- assert response['job_id'] == job_id
- assert response['job_category'] == 'pairlist'
+ assert response["job_id"] == job_id
+ assert response["job_category"] == "pairlist"
rc = client_get(client, f"{BASE_URI}/pairlists/evaluate/{job_id}")
assert_response(rc)
response = rc.json()
- assert response['result']['whitelist'] == ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC']
- assert response['result']['length'] == 4
+ assert response["result"]["whitelist"] == ["ETH/BTC", "LTC/BTC", "XRP/BTC", "NEO/BTC"]
+ assert response["result"]["length"] == 4
# Restart with additional filter, reducing the list to 2
- body['pairlists'].append({"method": "OffsetFilter", "number_assets": 2})
+ body["pairlists"].append({"method": "OffsetFilter", "number_assets": 2})
rc = client_post(client, f"{BASE_URI}/pairlists/evaluate", body)
assert_response(rc)
- assert rc.json()['status'] == 'Pairlist evaluation started in background.'
- job_id = rc.json()['job_id']
+ assert rc.json()["status"] == "Pairlist evaluation started in background."
+ job_id = rc.json()["job_id"]
rc = client_get(client, f"{BASE_URI}/pairlists/evaluate/{job_id}")
assert_response(rc)
response = rc.json()
- assert response['result']['whitelist'] == ['ETH/BTC', 'LTC/BTC', ]
- assert response['result']['length'] == 2
+ assert response["result"]["whitelist"] == [
+ "ETH/BTC",
+ "LTC/BTC",
+ ]
+ assert response["result"]["length"] == 2
# Patch __run_pairlists
- plm = mocker.patch('freqtrade.rpc.api_server.api_background_tasks.__run_pairlist',
- return_value=None)
+ plm = mocker.patch(
+ "freqtrade.rpc.api_server.api_background_tasks.__run_pairlist", return_value=None
+ )
body = {
"pairlists": [
- {"method": "StaticPairList", },
- ],
- "blacklist": [
+ {
+ "method": "StaticPairList",
+ },
],
+ "blacklist": [],
"stake_currency": "BTC",
"exchange": "randomExchange",
"trading_mode": "futures",
@@ -2023,9 +2339,9 @@ def test_api_pairlists_evaluate(botclient, tmp_path, mocker):
assert_response(rc)
assert plm.call_count == 1
call_config = plm.call_args_list[0][0][1]
- assert call_config['exchange']['name'] == 'randomExchange'
- assert call_config['trading_mode'] == 'futures'
- assert call_config['margin_mode'] == 'isolated'
+ assert call_config["exchange"]["name"] == "randomExchange"
+ assert call_config["trading_mode"] == "futures"
+ assert call_config["margin_mode"] == "isolated"
def test_list_available_pairs(botclient):
@@ -2034,38 +2350,36 @@ def test_list_available_pairs(botclient):
rc = client_get(client, f"{BASE_URI}/available_pairs")
assert_response(rc)
- assert rc.json()['length'] == 12
- assert isinstance(rc.json()['pairs'], list)
+ assert rc.json()["length"] == 12
+ assert isinstance(rc.json()["pairs"], list)
rc = client_get(client, f"{BASE_URI}/available_pairs?timeframe=5m")
assert_response(rc)
- assert rc.json()['length'] == 12
+ assert rc.json()["length"] == 12
rc = client_get(client, f"{BASE_URI}/available_pairs?stake_currency=ETH")
assert_response(rc)
- assert rc.json()['length'] == 1
- assert rc.json()['pairs'] == ['XRP/ETH']
- assert len(rc.json()['pair_interval']) == 2
+ assert rc.json()["length"] == 1
+ assert rc.json()["pairs"] == ["XRP/ETH"]
+ assert len(rc.json()["pair_interval"]) == 2
rc = client_get(client, f"{BASE_URI}/available_pairs?stake_currency=ETH&timeframe=5m")
assert_response(rc)
- assert rc.json()['length'] == 1
- assert rc.json()['pairs'] == ['XRP/ETH']
- assert len(rc.json()['pair_interval']) == 1
+ assert rc.json()["length"] == 1
+ assert rc.json()["pairs"] == ["XRP/ETH"]
+ assert len(rc.json()["pair_interval"]) == 1
- ftbot.config['trading_mode'] = 'futures'
- rc = client_get(
- client, f"{BASE_URI}/available_pairs?timeframe=1h")
+ ftbot.config["trading_mode"] = "futures"
+ rc = client_get(client, f"{BASE_URI}/available_pairs?timeframe=1h")
assert_response(rc)
- assert rc.json()['length'] == 1
- assert rc.json()['pairs'] == ['XRP/USDT:USDT']
+ assert rc.json()["length"] == 1
+ assert rc.json()["pairs"] == ["XRP/USDT:USDT"]
- rc = client_get(
- client, f"{BASE_URI}/available_pairs?timeframe=1h&candletype=mark")
+ rc = client_get(client, f"{BASE_URI}/available_pairs?timeframe=1h&candletype=mark")
assert_response(rc)
- assert rc.json()['length'] == 2
- assert rc.json()['pairs'] == ['UNITTEST/USDT:USDT', 'XRP/USDT:USDT']
- assert len(rc.json()['pair_interval']) == 2
+ assert rc.json()["length"] == 2
+ assert rc.json()["pairs"] == ["UNITTEST/USDT:USDT", "XRP/USDT:USDT"]
+ assert len(rc.json()["pair_interval"]) == 2
def test_sysinfo(botclient):
@@ -2074,43 +2388,43 @@ def test_sysinfo(botclient):
rc = client_get(client, f"{BASE_URI}/sysinfo")
assert_response(rc)
result = rc.json()
- assert 'cpu_pct' in result
- assert 'ram_pct' in result
+ assert "cpu_pct" in result
+ assert "ram_pct" in result
def test_api_backtesting(botclient, mocker, fee, caplog, tmp_path):
try:
ftbot, client = botclient
- mocker.patch(f'{EXMS}.get_fee', fee)
+ mocker.patch(f"{EXMS}.get_fee", fee)
rc = client_get(client, f"{BASE_URI}/backtest")
# Backtest prevented in default mode
assert_response(rc, 503)
- assert rc.json()['detail'] == 'Bot is not in the correct state.'
+ assert rc.json()["detail"] == "Bot is not in the correct state."
- ftbot.config['runmode'] = RunMode.WEBSERVER
+ ftbot.config["runmode"] = RunMode.WEBSERVER
# Backtesting not started yet
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'not_started'
- assert not result['running']
- assert result['status_msg'] == 'Backtest not yet executed'
- assert result['progress'] == 0
+ assert result["status"] == "not_started"
+ assert not result["running"]
+ assert result["status_msg"] == "Backtest not yet executed"
+ assert result["progress"] == 0
# Reset backtesting
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'reset'
- assert not result['running']
- assert result['status_msg'] == 'Backtest reset'
- ftbot.config['export'] = 'trades'
- ftbot.config['backtest_cache'] = 'day'
- ftbot.config['user_data_dir'] = tmp_path
- ftbot.config['exportfilename'] = tmp_path / "backtest_results"
- ftbot.config['exportfilename'].mkdir()
+ assert result["status"] == "reset"
+ assert not result["running"]
+ assert result["status_msg"] == "Backtest reset"
+ ftbot.config["export"] = "trades"
+ ftbot.config["backtest_cache"] = "day"
+ ftbot.config["user_data_dir"] = tmp_path
+ ftbot.config["exportfilename"] = tmp_path / "backtest_results"
+ ftbot.config["exportfilename"].mkdir()
# start backtesting
data = {
@@ -2120,63 +2434,63 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmp_path):
"max_open_trades": 3,
"stake_amount": 100,
"dry_run_wallet": 1000,
- "enable_protections": False
+ "enable_protections": False,
}
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc)
result = rc.json()
- assert result['status'] == 'running'
- assert result['progress'] == 0
- assert result['running']
- assert result['status_msg'] == 'Backtest started'
+ assert result["status"] == "running"
+ assert result["progress"] == 0
+ assert result["running"]
+ assert result["status_msg"] == "Backtest started"
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'ended'
- assert not result['running']
- assert result['status_msg'] == 'Backtest ended'
- assert result['progress'] == 1
- assert result['backtest_result']
+ assert result["status"] == "ended"
+ assert not result["running"]
+ assert result["status_msg"] == "Backtest ended"
+ assert result["progress"] == 1
+ assert result["backtest_result"]
rc = client_get(client, f"{BASE_URI}/backtest/abort")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'not_running'
- assert not result['running']
- assert result['status_msg'] == 'Backtest ended'
+ assert result["status"] == "not_running"
+ assert not result["running"]
+ assert result["status_msg"] == "Backtest ended"
# Simulate running backtest
ApiBG.bgtask_running = True
rc = client_get(client, f"{BASE_URI}/backtest/abort")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'stopping'
- assert not result['running']
- assert result['status_msg'] == 'Backtest ended'
+ assert result["status"] == "stopping"
+ assert not result["running"]
+ assert result["status_msg"] == "Backtest ended"
# Get running backtest...
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'running'
- assert result['running']
- assert result['step'] == "backtest"
- assert result['status_msg'] == "Backtest running"
+ assert result["status"] == "running"
+ assert result["running"]
+ assert result["step"] == "backtest"
+ assert result["status_msg"] == "Backtest running"
# Try delete with task still running
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'running'
+ assert result["status"] == "running"
# Post to backtest that's still running
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc, 502)
result = rc.json()
- assert 'Bot Background task already running' in result['error']
+ assert "Bot Background task already running" in result["error"]
ApiBG.bgtask_running = False
@@ -2184,32 +2498,34 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmp_path):
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc)
result = rc.json()
- assert log_has_re('Reusing result of previous backtest.*', caplog)
+ assert log_has_re("Reusing result of previous backtest.*", caplog)
- data['stake_amount'] = 101
+ data["stake_amount"] = 101
- mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy',
- side_effect=DependencyException('DeadBeef'))
+ mocker.patch(
+ "freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy",
+ side_effect=DependencyException("DeadBeef"),
+ )
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert log_has("Backtesting caused an error: DeadBeef", caplog)
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'error'
- assert 'Backtest failed' in result['status_msg']
+ assert result["status"] == "error"
+ assert "Backtest failed" in result["status_msg"]
# Delete backtesting to avoid leakage since the backtest-object may stick around.
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
- assert result['status'] == 'reset'
- assert not result['running']
- assert result['status_msg'] == 'Backtest reset'
+ assert result["status"] == "reset"
+ assert not result["running"]
+ assert result["status_msg"] == "Backtest reset"
# Disallow base64 strategies
- data['strategy'] = "xx:cHJpbnQoImhlbGxvIHdvcmxkIik="
+ data["strategy"] = "xx:cHJpbnQoImhlbGxvIHdvcmxkIik="
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
assert_response(rc, 500)
finally:
@@ -2218,37 +2534,39 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmp_path):
def test_api_backtest_history(botclient, mocker, testdatadir):
ftbot, client = botclient
- mocker.patch('freqtrade.data.btanalysis._get_backtest_files',
- return_value=[
- testdatadir / 'backtest_results/backtest-result_multistrat.json',
- testdatadir / 'backtest_results/backtest-result.json'
- ])
+ mocker.patch(
+ "freqtrade.data.btanalysis._get_backtest_files",
+ return_value=[
+ testdatadir / "backtest_results/backtest-result_multistrat.json",
+ testdatadir / "backtest_results/backtest-result.json",
+ ],
+ )
rc = client_get(client, f"{BASE_URI}/backtest/history")
assert_response(rc, 503)
- assert rc.json()['detail'] == 'Bot is not in the correct state.'
+ assert rc.json()["detail"] == "Bot is not in the correct state."
- ftbot.config['user_data_dir'] = testdatadir
- ftbot.config['runmode'] = RunMode.WEBSERVER
+ ftbot.config["user_data_dir"] = testdatadir
+ ftbot.config["runmode"] = RunMode.WEBSERVER
rc = client_get(client, f"{BASE_URI}/backtest/history")
assert_response(rc)
result = rc.json()
assert len(result) == 3
- fn = result[0]['filename']
+ fn = result[0]["filename"]
assert fn == "backtest-result_multistrat"
- assert result[0]['notes'] == ''
- strategy = result[0]['strategy']
+ assert result[0]["notes"] == ""
+ strategy = result[0]["strategy"]
rc = client_get(client, f"{BASE_URI}/backtest/history/result?filename={fn}&strategy={strategy}")
assert_response(rc)
result2 = rc.json()
assert result2
- assert result2['status'] == 'ended'
- assert not result2['running']
- assert result2['progress'] == 1
+ assert result2["status"] == "ended"
+ assert not result2["running"]
+ assert result2["progress"] == 1
# Only one strategy loaded - even though we use multiresult
- assert len(result2['backtest_result']['strategy']) == 1
- assert result2['backtest_result']['strategy'][strategy]
+ assert len(result2["backtest_result"]["strategy"]) == 1
+ assert result2["backtest_result"]["strategy"][strategy]
def test_api_delete_backtest_history_entry(botclient, tmp_path: Path):
@@ -2259,18 +2577,18 @@ def test_api_delete_backtest_history_entry(botclient, tmp_path: Path):
bt_results_base.mkdir()
file_path = bt_results_base / "test.json"
file_path.touch()
- meta_path = file_path.with_suffix('.meta.json')
+ meta_path = file_path.with_suffix(".meta.json")
meta_path.touch()
rc = client_delete(client, f"{BASE_URI}/backtest/history/randomFile.json")
assert_response(rc, 503)
- assert rc.json()['detail'] == 'Bot is not in the correct state.'
+ assert rc.json()["detail"] == "Bot is not in the correct state."
- ftbot.config['user_data_dir'] = tmp_path
- ftbot.config['runmode'] = RunMode.WEBSERVER
+ ftbot.config["user_data_dir"] = tmp_path
+ ftbot.config["runmode"] = RunMode.WEBSERVER
rc = client_delete(client, f"{BASE_URI}/backtest/history/randomFile.json")
assert rc.status_code == 404
- assert rc.json()['detail'] == 'File not found.'
+ assert rc.json()["detail"] == "File not found."
rc = client_delete(client, f"{BASE_URI}/backtest/history/{file_path.name}")
assert rc.status_code == 200
@@ -2287,65 +2605,85 @@ def test_api_patch_backtest_history_entry(botclient, tmp_path: Path):
bt_results_base.mkdir()
file_path = bt_results_base / "test.json"
file_path.touch()
- meta_path = file_path.with_suffix('.meta.json')
- with meta_path.open('w') as metafile:
- rapidjson.dump({
- CURRENT_TEST_STRATEGY: {
- "run_id": "6e542efc8d5e62cef6e5be0ffbc29be81a6e751d",
- "backtest_start_time": 1690176003}
- }, metafile)
+ meta_path = file_path.with_suffix(".meta.json")
+ with meta_path.open("w") as metafile:
+ rapidjson.dump(
+ {
+ CURRENT_TEST_STRATEGY: {
+ "run_id": "6e542efc8d5e62cef6e5be0ffbc29be81a6e751d",
+ "backtest_start_time": 1690176003,
+ }
+ },
+ metafile,
+ )
def read_metadata():
- with meta_path.open('r') as metafile:
+ with meta_path.open("r") as metafile:
return rapidjson.load(metafile)
rc = client_patch(client, f"{BASE_URI}/backtest/history/randomFile.json")
assert_response(rc, 503)
- ftbot.config['user_data_dir'] = tmp_path
- ftbot.config['runmode'] = RunMode.WEBSERVER
+ ftbot.config["user_data_dir"] = tmp_path
+ ftbot.config["runmode"] = RunMode.WEBSERVER
- rc = client_patch(client, f"{BASE_URI}/backtest/history/randomFile.json", {
- "strategy": CURRENT_TEST_STRATEGY,
- })
+ rc = client_patch(
+ client,
+ f"{BASE_URI}/backtest/history/randomFile.json",
+ {
+ "strategy": CURRENT_TEST_STRATEGY,
+ },
+ )
assert rc.status_code == 404
# Nonexisting strategy
- rc = client_patch(client, f"{BASE_URI}/backtest/history/{file_path.name}", {
- "strategy": f"{CURRENT_TEST_STRATEGY}xxx",
- })
+ rc = client_patch(
+ client,
+ f"{BASE_URI}/backtest/history/{file_path.name}",
+ {
+ "strategy": f"{CURRENT_TEST_STRATEGY}xxx",
+ },
+ )
assert rc.status_code == 400
- assert rc.json()['detail'] == 'Strategy not in metadata.'
+ assert rc.json()["detail"] == "Strategy not in metadata."
# no Notes
- rc = client_patch(client, f"{BASE_URI}/backtest/history/{file_path.name}", {
- "strategy": CURRENT_TEST_STRATEGY,
- })
+ rc = client_patch(
+ client,
+ f"{BASE_URI}/backtest/history/{file_path.name}",
+ {
+ "strategy": CURRENT_TEST_STRATEGY,
+ },
+ )
assert rc.status_code == 200
res = rc.json()
assert isinstance(res, list)
assert len(res) == 1
- assert res[0]['strategy'] == CURRENT_TEST_STRATEGY
- assert res[0]['notes'] == ''
+ assert res[0]["strategy"] == CURRENT_TEST_STRATEGY
+ assert res[0]["notes"] == ""
fileres = read_metadata()
- assert fileres[CURRENT_TEST_STRATEGY]['run_id'] == res[0]['run_id']
- assert fileres[CURRENT_TEST_STRATEGY]['notes'] == ''
+ assert fileres[CURRENT_TEST_STRATEGY]["run_id"] == res[0]["run_id"]
+ assert fileres[CURRENT_TEST_STRATEGY]["notes"] == ""
- rc = client_patch(client, f"{BASE_URI}/backtest/history/{file_path.name}", {
- "strategy": CURRENT_TEST_STRATEGY,
- "notes": "FooBar",
- })
+ rc = client_patch(
+ client,
+ f"{BASE_URI}/backtest/history/{file_path.name}",
+ {
+ "strategy": CURRENT_TEST_STRATEGY,
+ "notes": "FooBar",
+ },
+ )
assert rc.status_code == 200
res = rc.json()
assert isinstance(res, list)
assert len(res) == 1
- assert res[0]['strategy'] == CURRENT_TEST_STRATEGY
- assert res[0]['notes'] == 'FooBar'
+ assert res[0]["strategy"] == CURRENT_TEST_STRATEGY
+ assert res[0]["notes"] == "FooBar"
fileres = read_metadata()
- assert fileres[CURRENT_TEST_STRATEGY]['run_id'] == res[0]['run_id']
- assert fileres[CURRENT_TEST_STRATEGY]['notes'] == 'FooBar'
+ assert fileres[CURRENT_TEST_STRATEGY]["run_id"] == res[0]["run_id"]
+ assert fileres[CURRENT_TEST_STRATEGY]["notes"] == "FooBar"
def test_api_patch_backtest_market_change(botclient, tmp_path: Path):
@@ -2355,20 +2693,22 @@ def test_api_patch_backtest_market_change(botclient, tmp_path: Path):
bt_results_base = tmp_path / "backtest_results"
bt_results_base.mkdir()
file_path = bt_results_base / "test_22_market_change.feather"
- df = pd.DataFrame({
- 'date': ['2018-01-01T00:00:00Z', '2018-01-01T00:05:00Z'],
- 'count': [2, 4],
- 'mean': [2555, 2556],
- 'rel_mean': [0, 0.022],
- })
- df['date'] = pd.to_datetime(df['date'])
- df.to_feather(file_path, compression_level=9, compression='lz4')
+ df = pd.DataFrame(
+ {
+ "date": ["2018-01-01T00:00:00Z", "2018-01-01T00:05:00Z"],
+ "count": [2, 4],
+ "mean": [2555, 2556],
+ "rel_mean": [0, 0.022],
+ }
+ )
+ df["date"] = pd.to_datetime(df["date"])
+ df.to_feather(file_path, compression_level=9, compression="lz4")
# Nonexisting file
rc = client_get(client, f"{BASE_URI}/backtest/history/randomFile.json/market_change")
assert_response(rc, 503)
- ftbot.config['user_data_dir'] = tmp_path
- ftbot.config['runmode'] = RunMode.WEBSERVER
+ ftbot.config["user_data_dir"] = tmp_path
+ ftbot.config["runmode"] = RunMode.WEBSERVER
rc = client_get(client, f"{BASE_URI}/backtest/history/randomFile.json/market_change")
assert_response(rc, 404)
@@ -2376,11 +2716,11 @@ def test_api_patch_backtest_market_change(botclient, tmp_path: Path):
rc = client_get(client, f"{BASE_URI}/backtest/history/test_22/market_change")
assert_response(rc, 200)
result = rc.json()
- assert result['length'] == 2
- assert result['columns'] == ['date', 'count', 'mean', 'rel_mean', '__date_ts']
- assert result['data'] == [
- ['2018-01-01T00:00:00Z', 2, 2555, 0.0, 1514764800000],
- ['2018-01-01T00:05:00Z', 4, 2556, 0.022, 1514765100000]
+ assert result["length"] == 2
+ assert result["columns"] == ["date", "count", "mean", "rel_mean", "__date_ts"]
+ assert result["data"] == [
+ ["2018-01-01T00:00:00Z", 2, 2555, 0.0, 1514764800000],
+ ["2018-01-01T00:05:00Z", 4, 2556, 0.022, 1514765100000],
]
@@ -2399,17 +2739,17 @@ def test_api_ws_subscribe(botclient, mocker):
_ftbot, client = botclient
ws_url = f"/api/v1/message/ws?token={_TEST_WS_TOKEN}"
- sub_mock = mocker.patch('freqtrade.rpc.api_server.ws.WebSocketChannel.set_subscriptions')
+ sub_mock = mocker.patch("freqtrade.rpc.api_server.ws.WebSocketChannel.set_subscriptions")
with client.websocket_connect(ws_url) as ws:
- ws.send_json({'type': 'subscribe', 'data': ['whitelist']})
+ ws.send_json({"type": "subscribe", "data": ["whitelist"]})
time.sleep(0.2)
# Check call count is now 1 as we sent a valid subscribe request
assert sub_mock.call_count == 1
with client.websocket_connect(ws_url) as ws:
- ws.send_json({'type': 'subscribe', 'data': 'whitelist'})
+ ws.send_json({"type": "subscribe", "data": "whitelist"})
time.sleep(0.2)
# Call count hasn't changed as the subscribe request was invalid
@@ -2428,7 +2768,7 @@ def test_api_ws_requests(botclient, caplog):
response = ws.receive_json()
assert log_has_re(r"Request of type whitelist from.+", caplog)
- assert response['type'] == "whitelist"
+ assert response["type"] == "whitelist"
# Test analyzed_df request
with client.websocket_connect(ws_url) as ws:
@@ -2436,7 +2776,7 @@ def test_api_ws_requests(botclient, caplog):
response = ws.receive_json()
assert log_has_re(r"Request of type analyzed_df from.+", caplog)
- assert response['type'] == "analyzed_df"
+ assert response["type"] == "analyzed_df"
caplog.clear()
# Test analyzed_df request with data
@@ -2445,23 +2785,28 @@ def test_api_ws_requests(botclient, caplog):
response = ws.receive_json()
assert log_has_re(r"Request of type analyzed_df from.+", caplog)
- assert response['type'] == "analyzed_df"
+ assert response["type"] == "analyzed_df"
def test_api_ws_send_msg(default_conf, mocker, caplog):
try:
caplog.set_level(logging.DEBUG)
- default_conf.update({"api_server": {"enabled": True,
- "listen_ip_address": "127.0.0.1",
- "listen_port": 8080,
- "CORS_origins": ['http://example.com'],
- "username": _TEST_USER,
- "password": _TEST_PASS,
- "ws_token": _TEST_WS_TOKEN
- }})
- mocker.patch('freqtrade.rpc.telegram.Telegram._init')
- mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api')
+ default_conf.update(
+ {
+ "api_server": {
+ "enabled": True,
+ "listen_ip_address": "127.0.0.1",
+ "listen_port": 8080,
+ "CORS_origins": ["http://example.com"],
+ "username": _TEST_USER,
+ "password": _TEST_PASS,
+ "ws_token": _TEST_WS_TOKEN,
+ }
+ }
+ )
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init")
+ mocker.patch("freqtrade.rpc.api_server.ApiServer.start_api")
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
diff --git a/tests/rpc/test_rpc_emc.py b/tests/rpc/test_rpc_emc.py
index 4cfa3e9db..678379e68 100644
--- a/tests/rpc/test_rpc_emc.py
+++ b/tests/rpc/test_rpc_emc.py
@@ -1,6 +1,7 @@
"""
Unit test file for rpc/external_message_consumer.py
"""
+
import asyncio
import logging
from datetime import datetime, timezone
@@ -21,19 +22,16 @@ _TEST_WS_PORT = 9989
@pytest.fixture
def patched_emc(default_conf, mocker):
- default_conf.update({
- "external_message_consumer": {
- "enabled": True,
- "producers": [
- {
- "name": "default",
- "host": "null",
- "port": 9891,
- "ws_token": _TEST_WS_TOKEN
- }
- ]
+ default_conf.update(
+ {
+ "external_message_consumer": {
+ "enabled": True,
+ "producers": [
+ {"name": "default", "host": "null", "port": 9891, "ws_token": _TEST_WS_TOKEN}
+ ],
+ }
}
- })
+ )
dataprovider = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dataprovider)
@@ -81,7 +79,7 @@ def test_emc_init(patched_emc):
# Parametrize this?
def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history):
test_producer = {"name": "test", "url": "ws://test", "ws_token": "test"}
- producer_name = test_producer['name']
+ producer_name = test_producer["name"]
invalid_msg = r"Invalid message .+"
caplog.set_level(logging.DEBUG)
@@ -92,7 +90,8 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history):
assert log_has(f"Received message of type `whitelist` from `{producer_name}`", caplog)
assert log_has(
- f"Consumed message from `{producer_name}` of type `RPCMessageType.WHITELIST`", caplog)
+ f"Consumed message from `{producer_name}` of type `RPCMessageType.WHITELIST`", caplog
+ )
# Test handle analyzed_df single candle message
df_message = {
@@ -100,8 +99,8 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history):
"data": {
"key": ("BTC/USDT", "5m", "spot"),
"df": ohlcv_history,
- "la": datetime.now(timezone.utc)
- }
+ "la": datetime.now(timezone.utc),
+ },
}
patched_emc.handle_producer_message(test_producer, df_message)
@@ -124,11 +123,7 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history):
malformed_message = {
"type": "analyzed_df",
- "data": {
- "key": "BTC/USDT",
- "df": ohlcv_history,
- "la": datetime.now(timezone.utc)
- }
+ "data": {"key": "BTC/USDT", "df": ohlcv_history, "la": datetime.now(timezone.utc)},
}
patched_emc.handle_producer_message(test_producer, malformed_message)
@@ -138,13 +133,13 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history):
# Empty dataframe
malformed_message = {
- "type": "analyzed_df",
- "data": {
- "key": ("BTC/USDT", "5m", "spot"),
- "df": ohlcv_history.loc[ohlcv_history['open'] < 0],
- "la": datetime.now(timezone.utc)
- }
- }
+ "type": "analyzed_df",
+ "data": {
+ "key": ("BTC/USDT", "5m", "spot"),
+ "df": ohlcv_history.loc[ohlcv_history["open"] < 0],
+ "la": datetime.now(timezone.utc),
+ },
+ }
patched_emc.handle_producer_message(test_producer, malformed_message)
assert log_has(f"Received message of type `analyzed_df` from `{producer_name}`", caplog)
@@ -166,29 +161,32 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history):
async def test_emc_create_connection_success(default_conf, caplog, mocker):
- default_conf.update({
- "external_message_consumer": {
- "enabled": True,
- "producers": [
- {
- "name": "default",
- "host": _TEST_WS_HOST,
- "port": _TEST_WS_PORT,
- "ws_token": _TEST_WS_TOKEN
- }
- ],
- "wait_timeout": 60,
- "ping_timeout": 60,
- "sleep_timeout": 60
+ default_conf.update(
+ {
+ "external_message_consumer": {
+ "enabled": True,
+ "producers": [
+ {
+ "name": "default",
+ "host": _TEST_WS_HOST,
+ "port": _TEST_WS_PORT,
+ "ws_token": _TEST_WS_TOKEN,
+ }
+ ],
+ "wait_timeout": 60,
+ "ping_timeout": 60,
+ "sleep_timeout": 60,
+ }
}
- })
+ )
- mocker.patch('freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start',
- MagicMock())
+ mocker.patch(
+ "freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start", MagicMock()
+ )
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
- test_producer = default_conf['external_message_consumer']['producers'][0]
+ test_producer = default_conf["external_message_consumer"]["producers"][0]
lock = asyncio.Lock()
emc._running = True
@@ -205,27 +203,27 @@ async def test_emc_create_connection_success(default_conf, caplog, mocker):
emc.shutdown()
-@pytest.mark.parametrize('host,port', [
- (_TEST_WS_HOST, -1),
- ("10000.1241..2121/", _TEST_WS_PORT),
-])
+@pytest.mark.parametrize(
+ "host,port",
+ [
+ (_TEST_WS_HOST, -1),
+ ("10000.1241..2121/", _TEST_WS_PORT),
+ ],
+)
async def test_emc_create_connection_invalid_url(default_conf, caplog, mocker, host, port):
- default_conf.update({
- "external_message_consumer": {
- "enabled": True,
- "producers": [
- {
- "name": "default",
- "host": host,
- "port": port,
- "ws_token": _TEST_WS_TOKEN
- }
- ],
- "wait_timeout": 60,
- "ping_timeout": 60,
- "sleep_timeout": 60
+ default_conf.update(
+ {
+ "external_message_consumer": {
+ "enabled": True,
+ "producers": [
+ {"name": "default", "host": host, "port": port, "ws_token": _TEST_WS_TOKEN}
+ ],
+ "wait_timeout": 60,
+ "ping_timeout": 60,
+ "sleep_timeout": 60,
+ }
}
- })
+ )
dp = DataProvider(default_conf, None, None, None)
# Handle start explicitly to avoid messing with threading in tests
@@ -242,25 +240,27 @@ async def test_emc_create_connection_invalid_url(default_conf, caplog, mocker, h
async def test_emc_create_connection_error(default_conf, caplog, mocker):
- default_conf.update({
- "external_message_consumer": {
- "enabled": True,
- "producers": [
- {
- "name": "default",
- "host": _TEST_WS_HOST,
- "port": _TEST_WS_PORT,
- "ws_token": _TEST_WS_TOKEN
- }
- ],
- "wait_timeout": 60,
- "ping_timeout": 60,
- "sleep_timeout": 60
+ default_conf.update(
+ {
+ "external_message_consumer": {
+ "enabled": True,
+ "producers": [
+ {
+ "name": "default",
+ "host": _TEST_WS_HOST,
+ "port": _TEST_WS_PORT,
+ "ws_token": _TEST_WS_TOKEN,
+ }
+ ],
+ "wait_timeout": 60,
+ "ping_timeout": 60,
+ "sleep_timeout": 60,
+ }
}
- })
+ )
# Test unexpected error
- mocker.patch('websockets.connect', side_effect=RuntimeError)
+ mocker.patch("websockets.connect", side_effect=RuntimeError)
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
@@ -275,28 +275,31 @@ async def test_emc_create_connection_error(default_conf, caplog, mocker):
async def test_emc_receive_messages_valid(default_conf, caplog, mocker):
caplog.set_level(logging.DEBUG)
- default_conf.update({
- "external_message_consumer": {
- "enabled": True,
- "producers": [
- {
- "name": "default",
- "host": _TEST_WS_HOST,
- "port": _TEST_WS_PORT,
- "ws_token": _TEST_WS_TOKEN
- }
- ],
- "wait_timeout": 1,
- "ping_timeout": 60,
- "sleep_time": 60
+ default_conf.update(
+ {
+ "external_message_consumer": {
+ "enabled": True,
+ "producers": [
+ {
+ "name": "default",
+ "host": _TEST_WS_HOST,
+ "port": _TEST_WS_PORT,
+ "ws_token": _TEST_WS_TOKEN,
+ }
+ ],
+ "wait_timeout": 1,
+ "ping_timeout": 60,
+ "sleep_time": 60,
+ }
}
- })
+ )
- mocker.patch('freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start',
- MagicMock())
+ mocker.patch(
+ "freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start", MagicMock()
+ )
lock = asyncio.Lock()
- test_producer = default_conf['external_message_consumer']['producers'][0]
+ test_producer = default_conf["external_message_consumer"]["producers"][0]
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
@@ -319,28 +322,31 @@ async def test_emc_receive_messages_valid(default_conf, caplog, mocker):
async def test_emc_receive_messages_invalid(default_conf, caplog, mocker):
- default_conf.update({
- "external_message_consumer": {
- "enabled": True,
- "producers": [
- {
- "name": "default",
- "host": _TEST_WS_HOST,
- "port": _TEST_WS_PORT,
- "ws_token": _TEST_WS_TOKEN
- }
- ],
- "wait_timeout": 1,
- "ping_timeout": 60,
- "sleep_time": 60
+ default_conf.update(
+ {
+ "external_message_consumer": {
+ "enabled": True,
+ "producers": [
+ {
+ "name": "default",
+ "host": _TEST_WS_HOST,
+ "port": _TEST_WS_PORT,
+ "ws_token": _TEST_WS_TOKEN,
+ }
+ ],
+ "wait_timeout": 1,
+ "ping_timeout": 60,
+ "sleep_time": 60,
+ }
}
- })
+ )
- mocker.patch('freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start',
- MagicMock())
+ mocker.patch(
+ "freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start", MagicMock()
+ )
lock = asyncio.Lock()
- test_producer = default_conf['external_message_consumer']['producers'][0]
+ test_producer = default_conf["external_message_consumer"]["producers"][0]
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
@@ -363,28 +369,31 @@ async def test_emc_receive_messages_invalid(default_conf, caplog, mocker):
async def test_emc_receive_messages_timeout(default_conf, caplog, mocker):
- default_conf.update({
- "external_message_consumer": {
- "enabled": True,
- "producers": [
- {
- "name": "default",
- "host": _TEST_WS_HOST,
- "port": _TEST_WS_PORT,
- "ws_token": _TEST_WS_TOKEN
- }
- ],
- "wait_timeout": 0.1,
- "ping_timeout": 1,
- "sleep_time": 1
+ default_conf.update(
+ {
+ "external_message_consumer": {
+ "enabled": True,
+ "producers": [
+ {
+ "name": "default",
+ "host": _TEST_WS_HOST,
+ "port": _TEST_WS_PORT,
+ "ws_token": _TEST_WS_TOKEN,
+ }
+ ],
+ "wait_timeout": 0.1,
+ "ping_timeout": 1,
+ "sleep_time": 1,
+ }
}
- })
+ )
- mocker.patch('freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start',
- MagicMock())
+ mocker.patch(
+ "freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start", MagicMock()
+ )
lock = asyncio.Lock()
- test_producer = default_conf['external_message_consumer']['producers'][0]
+ test_producer = default_conf["external_message_consumer"]["producers"][0]
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
@@ -411,28 +420,31 @@ async def test_emc_receive_messages_timeout(default_conf, caplog, mocker):
async def test_emc_receive_messages_handle_error(default_conf, caplog, mocker):
- default_conf.update({
- "external_message_consumer": {
- "enabled": True,
- "producers": [
- {
- "name": "default",
- "host": _TEST_WS_HOST,
- "port": _TEST_WS_PORT,
- "ws_token": _TEST_WS_TOKEN
- }
- ],
- "wait_timeout": 1,
- "ping_timeout": 1,
- "sleep_time": 1
+ default_conf.update(
+ {
+ "external_message_consumer": {
+ "enabled": True,
+ "producers": [
+ {
+ "name": "default",
+ "host": _TEST_WS_HOST,
+ "port": _TEST_WS_PORT,
+ "ws_token": _TEST_WS_TOKEN,
+ }
+ ],
+ "wait_timeout": 1,
+ "ping_timeout": 1,
+ "sleep_time": 1,
+ }
}
- })
+ )
- mocker.patch('freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start',
- MagicMock())
+ mocker.patch(
+ "freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start", MagicMock()
+ )
lock = asyncio.Lock()
- test_producer = default_conf['external_message_consumer']['producers'][0]
+ test_producer = default_conf["external_message_consumer"]["producers"][0]
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
diff --git a/tests/rpc/test_rpc_manager.py b/tests/rpc/test_rpc_manager.py
index f0bb72fc9..2792fd082 100644
--- a/tests/rpc/test_rpc_manager.py
+++ b/tests/rpc/test_rpc_manager.py
@@ -11,7 +11,7 @@ from tests.conftest import get_patched_freqtradebot, log_has
def test__init__(mocker, default_conf) -> None:
- default_conf['telegram']['enabled'] = False
+ default_conf["telegram"]["enabled"] = False
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
assert rpc_manager.registered_modules == []
@@ -19,97 +19,91 @@ def test__init__(mocker, default_conf) -> None:
def test_init_telegram_disabled(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
- default_conf['telegram']['enabled'] = False
+ default_conf["telegram"]["enabled"] = False
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
- assert not log_has('Enabling rpc.telegram ...', caplog)
+ assert not log_has("Enabling rpc.telegram ...", caplog)
assert rpc_manager.registered_modules == []
def test_init_telegram_enabled(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
- default_conf['telegram']['enabled'] = True
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+ default_conf["telegram"]["enabled"] = True
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
- assert log_has('Enabling rpc.telegram ...', caplog)
+ assert log_has("Enabling rpc.telegram ...", caplog)
len_modules = len(rpc_manager.registered_modules)
assert len_modules == 1
- assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules]
+ assert "telegram" in [mod.name for mod in rpc_manager.registered_modules]
def test_cleanup_telegram_disabled(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
- telegram_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.cleanup', MagicMock())
- default_conf['telegram']['enabled'] = False
+ telegram_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.cleanup", MagicMock())
+ default_conf["telegram"]["enabled"] = False
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc_manager = RPCManager(freqtradebot)
rpc_manager.cleanup()
- assert not log_has('Cleaning up rpc.telegram ...', caplog)
+ assert not log_has("Cleaning up rpc.telegram ...", caplog)
assert telegram_mock.call_count == 0
def test_cleanup_telegram_enabled(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
- default_conf['telegram']['enabled'] = True
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
- telegram_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.cleanup', MagicMock())
+ default_conf["telegram"]["enabled"] = True
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
+ telegram_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.cleanup", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc_manager = RPCManager(freqtradebot)
# Check we have Telegram as a registered modules
- assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules]
+ assert "telegram" in [mod.name for mod in rpc_manager.registered_modules]
rpc_manager.cleanup()
- assert log_has('Cleaning up rpc.telegram ...', caplog)
- assert 'telegram' not in [mod.name for mod in rpc_manager.registered_modules]
+ assert log_has("Cleaning up rpc.telegram ...", caplog)
+ assert "telegram" not in [mod.name for mod in rpc_manager.registered_modules]
assert telegram_mock.call_count == 1
def test_send_msg_telegram_disabled(mocker, default_conf, caplog) -> None:
- telegram_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
- default_conf['telegram']['enabled'] = False
+ telegram_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", MagicMock())
+ default_conf["telegram"]["enabled"] = False
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc_manager = RPCManager(freqtradebot)
- rpc_manager.send_msg({
- 'type': RPCMessageType.STATUS,
- 'status': 'test'
- })
+ rpc_manager.send_msg({"type": RPCMessageType.STATUS, "status": "test"})
assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog)
assert telegram_mock.call_count == 0
def test_send_msg_telegram_error(mocker, default_conf, caplog) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
- mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', side_effect=ValueError())
- default_conf['telegram']['enabled'] = True
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", side_effect=ValueError())
+ default_conf["telegram"]["enabled"] = True
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc_manager = RPCManager(freqtradebot)
- rpc_manager.send_msg({
- 'type': RPCMessageType.STATUS,
- 'status': 'test'
- })
+ rpc_manager.send_msg({"type": RPCMessageType.STATUS, "status": "test"})
assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog)
assert log_has("Exception occurred within RPC module telegram", caplog)
def test_process_msg_queue(mocker, default_conf, caplog) -> None:
- telegram_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg')
- default_conf['telegram']['enabled'] = True
- default_conf['telegram']['allow_custom_messages'] = True
- mocker.patch('freqtrade.rpc.telegram.Telegram._init')
+ telegram_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg")
+ default_conf["telegram"]["enabled"] = True
+ default_conf["telegram"]["allow_custom_messages"] = True
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init")
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc_manager = RPCManager(freqtradebot)
queue = deque()
- queue.append('Test message')
- queue.append('Test message 2')
+ queue.append("Test message")
+ queue.append("Test message 2")
rpc_manager.process_msg_queue(queue)
assert log_has("Sending rpc strategy_msg: Test message", caplog)
@@ -118,15 +112,12 @@ def test_process_msg_queue(mocker, default_conf, caplog) -> None:
def test_send_msg_telegram_enabled(mocker, default_conf, caplog) -> None:
- default_conf['telegram']['enabled'] = True
- telegram_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg')
- mocker.patch('freqtrade.rpc.telegram.Telegram._init')
+ default_conf["telegram"]["enabled"] = True
+ telegram_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg")
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init")
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc_manager = RPCManager(freqtradebot)
- rpc_manager.send_msg({
- 'type': RPCMessageType.STATUS,
- 'status': 'test'
- })
+ rpc_manager.send_msg({"type": RPCMessageType.STATUS, "status": "test"})
assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog)
assert telegram_mock.call_count == 1
@@ -134,76 +125,73 @@ def test_send_msg_telegram_enabled(mocker, default_conf, caplog) -> None:
def test_init_webhook_disabled(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
- default_conf['telegram']['enabled'] = False
- default_conf['webhook'] = {'enabled': False}
+ default_conf["telegram"]["enabled"] = False
+ default_conf["webhook"] = {"enabled": False}
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
- assert not log_has('Enabling rpc.webhook ...', caplog)
+ assert not log_has("Enabling rpc.webhook ...", caplog)
assert rpc_manager.registered_modules == []
def test_init_webhook_enabled(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
- default_conf['telegram']['enabled'] = False
- default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"}
+ default_conf["telegram"]["enabled"] = False
+ default_conf["webhook"] = {"enabled": True, "url": "https://DEADBEEF.com"}
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
- assert log_has('Enabling rpc.webhook ...', caplog)
+ assert log_has("Enabling rpc.webhook ...", caplog)
assert len(rpc_manager.registered_modules) == 1
- assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules]
+ assert "webhook" in [mod.name for mod in rpc_manager.registered_modules]
def test_send_msg_webhook_CustomMessagetype(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
- default_conf['telegram']['enabled'] = False
- default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"}
- mocker.patch('freqtrade.rpc.webhook.Webhook.send_msg',
- MagicMock(side_effect=NotImplementedError))
+ default_conf["telegram"]["enabled"] = False
+ default_conf["webhook"] = {"enabled": True, "url": "https://DEADBEEF.com"}
+ mocker.patch(
+ "freqtrade.rpc.webhook.Webhook.send_msg", MagicMock(side_effect=NotImplementedError)
+ )
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
- assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules]
- rpc_manager.send_msg({'type': RPCMessageType.STARTUP,
- 'status': 'TestMessage'})
- assert log_has(
- "Message type 'startup' not implemented by handler webhook.",
- caplog)
+ assert "webhook" in [mod.name for mod in rpc_manager.registered_modules]
+ rpc_manager.send_msg({"type": RPCMessageType.STARTUP, "status": "TestMessage"})
+ assert log_has("Message type 'startup' not implemented by handler webhook.", caplog)
def test_startupmessages_telegram_enabled(mocker, default_conf) -> None:
- default_conf['telegram']['enabled'] = True
- telegram_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+ default_conf["telegram"]["enabled"] = True
+ telegram_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc_manager = RPCManager(freqtradebot)
rpc_manager.startup_messages(default_conf, freqtradebot.pairlists, freqtradebot.protections)
assert telegram_mock.call_count == 3
- assert "*Exchange:* `binance`" in telegram_mock.call_args_list[1][0][0]['status']
+ assert "*Exchange:* `binance`" in telegram_mock.call_args_list[1][0][0]["status"]
telegram_mock.reset_mock()
- default_conf['dry_run'] = True
- default_conf['whitelist'] = {'method': 'VolumePairList',
- 'config': {'number_assets': 20}
- }
- default_conf['protections'] = [{"method": "StoplossGuard",
- "lookback_period": 60, "trade_limit": 2, "stop_duration": 60}]
+ default_conf["dry_run"] = True
+ default_conf["whitelist"] = {"method": "VolumePairList", "config": {"number_assets": 20}}
+ default_conf["protections"] = [
+ {"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2, "stop_duration": 60}
+ ]
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
- rpc_manager.startup_messages(default_conf, freqtradebot.pairlists, freqtradebot.protections)
+ rpc_manager.startup_messages(default_conf, freqtradebot.pairlists, freqtradebot.protections)
assert telegram_mock.call_count == 4
- assert "Dry run is enabled." in telegram_mock.call_args_list[0][0][0]['status']
- assert 'StoplossGuard' in telegram_mock.call_args_list[-1][0][0]['status']
+ assert "Dry run is enabled." in telegram_mock.call_args_list[0][0][0]["status"]
+ assert "StoplossGuard" in telegram_mock.call_args_list[-1][0][0]["status"]
def test_init_apiserver_disabled(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
run_mock = MagicMock()
- mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', run_mock)
- default_conf['telegram']['enabled'] = False
+ mocker.patch("freqtrade.rpc.api_server.ApiServer.start_api", run_mock)
+ default_conf["telegram"]["enabled"] = False
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
- assert not log_has('Enabling rpc.api_server', caplog)
+ assert not log_has("Enabling rpc.api_server", caplog)
assert rpc_manager.registered_modules == []
assert run_mock.call_count == 0
@@ -211,21 +199,22 @@ def test_init_apiserver_disabled(mocker, default_conf, caplog) -> None:
def test_init_apiserver_enabled(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
run_mock = MagicMock()
- mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', run_mock)
+ mocker.patch("freqtrade.rpc.api_server.ApiServer.start_api", run_mock)
default_conf["telegram"]["enabled"] = False
- default_conf["api_server"] = {"enabled": True,
- "listen_ip_address": "127.0.0.1",
- "listen_port": 8080,
- "username": "TestUser",
- "password": "TestPass",
- }
+ default_conf["api_server"] = {
+ "enabled": True,
+ "listen_ip_address": "127.0.0.1",
+ "listen_port": 8080,
+ "username": "TestUser",
+ "password": "TestPass",
+ }
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
# Sleep to allow the thread to start
time.sleep(0.5)
- assert log_has('Enabling rpc.api_server', caplog)
+ assert log_has("Enabling rpc.api_server", caplog)
assert len(rpc_manager.registered_modules) == 1
- assert 'apiserver' in [mod.name for mod in rpc_manager.registered_modules]
+ assert "apiserver" in [mod.name for mod in rpc_manager.registered_modules]
assert run_mock.call_count == 1
ApiServer.shutdown()
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
index 5d339f71c..3063e644b 100644
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -55,13 +55,13 @@ from tests.conftest import (
@pytest.fixture(autouse=True)
def mock_exchange_loop(mocker):
- mocker.patch('freqtrade.exchange.exchange.Exchange._init_async_loop')
+ mocker.patch("freqtrade.exchange.exchange.Exchange._init_async_loop")
@pytest.fixture
def default_conf(default_conf) -> dict:
# Telegram is enabled by default
- default_conf['telegram']['enabled'] = True
+ default_conf["telegram"]["enabled"] = True
return default_conf
@@ -81,6 +81,7 @@ def patch_eventloop_threading(telegrambot):
telegrambot._loop = asyncio.new_event_loop()
is_init = True
telegrambot._loop.run_forever()
+
x = threading.Thread(target=thread_fuck, daemon=True)
x.start()
while not is_init:
@@ -94,7 +95,7 @@ class DummyCls(Telegram):
def __init__(self, rpc: RPC, config) -> None:
super().__init__(rpc, config)
- self.state = {'called': False}
+ self.state = {"called": False}
def _init(self):
pass
@@ -104,21 +105,21 @@ class DummyCls(Telegram):
"""
Fake method that only change the state of the object
"""
- self.state['called'] = True
+ self.state["called"] = True
@authorized_only
async def dummy_exception(self, *args, **kwargs) -> None:
"""
Fake method that throw an exception
"""
- raise Exception('test')
+ raise Exception("test")
def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None):
msg_mock = AsyncMock()
if mock:
mocker.patch.multiple(
- 'freqtrade.rpc.telegram.Telegram',
+ "freqtrade.rpc.telegram.Telegram",
_init=MagicMock(),
_send_msg=msg_mock,
_start_thread=MagicMock(),
@@ -134,7 +135,7 @@ def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None):
def test_telegram__init__(default_conf, mocker) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
telegram, _, _ = get_telegram_testobject(mocker, default_conf)
assert telegram._config == default_conf
@@ -142,9 +143,9 @@ def test_telegram__init__(default_conf, mocker) -> None:
def test_telegram_init(default_conf, mocker, caplog) -> None:
app_mock = MagicMock()
- mocker.patch('freqtrade.rpc.telegram.Telegram._start_thread', MagicMock())
- mocker.patch('freqtrade.rpc.telegram.Telegram._init_telegram_app', return_value=app_mock)
- mocker.patch('freqtrade.rpc.telegram.Telegram._startup_telegram', AsyncMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._start_thread", MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init_telegram_app", return_value=app_mock)
+ mocker.patch("freqtrade.rpc.telegram.Telegram._startup_telegram", AsyncMock())
telegram, _, _ = get_telegram_testobject(mocker, default_conf, mock=False)
telegram._init()
@@ -154,18 +155,20 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
assert app_mock.add_handler.call_count > 0
# assert start_polling.start_polling.call_count == 1
- message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], "
- "['balance'], ['start'], ['stop'], "
- "['forceexit', 'forcesell', 'fx'], ['forcebuy', 'forcelong'], ['forceshort'], "
- "['reload_trade'], ['trades'], ['delete'], ['cancel_open_order', 'coo'], "
- "['performance'], ['buys', 'entries'], ['exits', 'sells'], ['mix_tags'], "
- "['stats'], ['daily'], ['weekly'], ['monthly'], "
- "['count'], ['locks'], ['delete_locks', 'unlock'], "
- "['reload_conf', 'reload_config'], ['show_conf', 'show_config'], "
- "['stopbuy', 'stopentry'], ['whitelist'], ['blacklist'], "
- "['bl_delete', 'blacklist_delete'], "
- "['logs'], ['edge'], ['health'], ['help'], ['version'], ['marketdir'], "
- "['order'], ['list_custom_data']]")
+ message_str = (
+ "rpc.telegram is listening for following commands: [['status'], ['profit'], "
+ "['balance'], ['start'], ['stop'], "
+ "['forceexit', 'forcesell', 'fx'], ['forcebuy', 'forcelong'], ['forceshort'], "
+ "['reload_trade'], ['trades'], ['delete'], ['cancel_open_order', 'coo'], "
+ "['performance'], ['buys', 'entries'], ['exits', 'sells'], ['mix_tags'], "
+ "['stats'], ['daily'], ['weekly'], ['monthly'], "
+ "['count'], ['locks'], ['delete_locks', 'unlock'], "
+ "['reload_conf', 'reload_config'], ['show_conf', 'show_config'], "
+ "['stopbuy', 'stopentry'], ['whitelist'], ['blacklist'], "
+ "['bl_delete', 'blacklist_delete'], "
+ "['logs'], ['edge'], ['health'], ['help'], ['version'], ['marketdir'], "
+ "['order'], ['list_custom_data']]"
+ )
assert log_has(message_str, caplog)
@@ -176,7 +179,7 @@ async def test_telegram_startup(default_conf, mocker) -> None:
app_mock.start = AsyncMock()
app_mock.updater.start_polling = AsyncMock()
app_mock.updater.running = False
- sleep_mock = mocker.patch('freqtrade.rpc.telegram.asyncio.sleep', AsyncMock())
+ sleep_mock = mocker.patch("freqtrade.rpc.telegram.asyncio.sleep", AsyncMock())
telegram, _, _ = get_telegram_testobject(mocker, default_conf)
telegram._app = app_mock
@@ -187,7 +190,10 @@ async def test_telegram_startup(default_conf, mocker) -> None:
assert sleep_mock.call_count == 1
-async def test_telegram_cleanup(default_conf, mocker, ) -> None:
+async def test_telegram_cleanup(
+ default_conf,
+ mocker,
+) -> None:
app_mock = MagicMock()
app_mock.stop = AsyncMock()
app_mock.initialize = AsyncMock()
@@ -210,43 +216,43 @@ async def test_telegram_cleanup(default_conf, mocker, ) -> None:
async def test_authorized_only(default_conf, mocker, caplog, update) -> None:
patch_exchange(mocker)
caplog.set_level(logging.DEBUG)
- default_conf['telegram']['enabled'] = False
+ default_conf["telegram"]["enabled"] = False
bot = FreqtradeBot(default_conf)
rpc = RPC(bot)
dummy = DummyCls(rpc, default_conf)
patch_get_signal(bot)
await dummy.dummy_handler(update=update, context=MagicMock())
- assert dummy.state['called'] is True
- assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog)
- assert not log_has('Rejected unauthorized message from: 0', caplog)
- assert not log_has('Exception occurred within Telegram module', caplog)
+ assert dummy.state["called"] is True
+ assert log_has("Executing handler: dummy_handler for chat_id: 0", caplog)
+ assert not log_has("Rejected unauthorized message from: 0", caplog)
+ assert not log_has("Exception occurred within Telegram module", caplog)
async def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
patch_exchange(mocker)
caplog.set_level(logging.DEBUG)
- chat = Chat(0xdeadbeef, 0)
+ chat = Chat(0xDEADBEEF, 0)
message = Message(randint(1, 100), datetime.now(timezone.utc), chat)
update = Update(randint(1, 100), message=message)
- default_conf['telegram']['enabled'] = False
+ default_conf["telegram"]["enabled"] = False
bot = FreqtradeBot(default_conf)
rpc = RPC(bot)
dummy = DummyCls(rpc, default_conf)
patch_get_signal(bot)
await dummy.dummy_handler(update=update, context=MagicMock())
- assert dummy.state['called'] is False
- assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog)
- assert log_has('Rejected unauthorized message from: 3735928559', caplog)
- assert not log_has('Exception occurred within Telegram module', caplog)
+ assert dummy.state["called"] is False
+ assert not log_has("Executing handler: dummy_handler for chat_id: 3735928559", caplog)
+ assert log_has("Rejected unauthorized message from: 3735928559", caplog)
+ assert not log_has("Exception occurred within Telegram module", caplog)
async def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None:
patch_exchange(mocker)
- default_conf['telegram']['enabled'] = False
+ default_conf["telegram"]["enabled"] = False
bot = FreqtradeBot(default_conf)
rpc = RPC(bot)
@@ -254,54 +260,58 @@ async def test_authorized_only_exception(default_conf, mocker, caplog, update) -
patch_get_signal(bot)
await dummy.dummy_exception(update=update, context=MagicMock())
- assert dummy.state['called'] is False
- assert not log_has('Executing handler: dummy_handler for chat_id: 0', caplog)
- assert not log_has('Rejected unauthorized message from: 0', caplog)
- assert log_has('Exception occurred within Telegram module', caplog)
+ assert dummy.state["called"] is False
+ assert not log_has("Executing handler: dummy_handler for chat_id: 0", caplog)
+ assert not log_has("Rejected unauthorized message from: 0", caplog)
+ assert log_has("Exception occurred within Telegram module", caplog)
async def test_telegram_status(default_conf, update, mocker) -> None:
- default_conf['telegram']['enabled'] = False
+ default_conf["telegram"]["enabled"] = False
status_table = MagicMock()
- mocker.patch('freqtrade.rpc.telegram.Telegram._status_table', status_table)
+ mocker.patch("freqtrade.rpc.telegram.Telegram._status_table", status_table)
mocker.patch.multiple(
- 'freqtrade.rpc.rpc.RPC',
- _rpc_trade_status=MagicMock(return_value=[{
- 'trade_id': 1,
- 'pair': 'ETH/BTC',
- 'base_currency': 'ETH',
- 'quote_currency': 'BTC',
- 'open_date': dt_now(),
- 'close_date': None,
- 'open_rate': 1.099e-05,
- 'close_rate': None,
- 'current_rate': 1.098e-05,
- 'amount': 90.99181074,
- 'stake_amount': 90.99181074,
- 'max_stake_amount': 90.99181074,
- 'buy_tag': None,
- 'enter_tag': None,
- 'close_profit_ratio': None,
- 'profit': -0.0059,
- 'profit_ratio': -0.0059,
- 'profit_abs': -0.225,
- 'realized_profit': 0.0,
- 'total_profit_abs': -0.225,
- 'initial_stop_loss_abs': 1.098e-05,
- 'stop_loss_abs': 1.099e-05,
- 'exit_order_status': None,
- 'initial_stop_loss_ratio': -0.0005,
- 'stoploss_current_dist': 1e-08,
- 'stoploss_current_dist_ratio': -0.0002,
- 'stop_loss_ratio': -0.0001,
- 'open_order': '(limit buy rem=0.00000000)',
- 'is_open': True,
- 'is_short': False,
- 'filled_entry_orders': [],
- 'orders': []
- }]),
+ "freqtrade.rpc.rpc.RPC",
+ _rpc_trade_status=MagicMock(
+ return_value=[
+ {
+ "trade_id": 1,
+ "pair": "ETH/BTC",
+ "base_currency": "ETH",
+ "quote_currency": "BTC",
+ "open_date": dt_now(),
+ "close_date": None,
+ "open_rate": 1.099e-05,
+ "close_rate": None,
+ "current_rate": 1.098e-05,
+ "amount": 90.99181074,
+ "stake_amount": 90.99181074,
+ "max_stake_amount": 90.99181074,
+ "buy_tag": None,
+ "enter_tag": None,
+ "close_profit_ratio": None,
+ "profit": -0.0059,
+ "profit_ratio": -0.0059,
+ "profit_abs": -0.225,
+ "realized_profit": 0.0,
+ "total_profit_abs": -0.225,
+ "initial_stop_loss_abs": 1.098e-05,
+ "stop_loss_abs": 1.099e-05,
+ "exit_order_status": None,
+ "initial_stop_loss_ratio": -0.0005,
+ "stoploss_current_dist": 1e-08,
+ "stoploss_current_dist_ratio": -0.0002,
+ "stop_loss_ratio": -0.0001,
+ "open_order": "(limit buy rem=0.00000000)",
+ "is_open": True,
+ "is_short": False,
+ "filled_entry_orders": [],
+ "orders": [],
+ }
+ ]
+ ),
)
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
@@ -318,8 +328,8 @@ async def test_telegram_status(default_conf, update, mocker) -> None:
@pytest.mark.usefixtures("init_persistence")
async def test_telegram_status_multi_entry(default_conf, update, mocker, fee) -> None:
- default_conf['telegram']['enabled'] = False
- default_conf['position_adjustment_enable'] = True
+ default_conf["telegram"]["enabled"] = False
+ default_conf["position_adjustment_enable"] = True
mocker.patch.multiple(
EXMS,
fetch_order=MagicMock(return_value=None),
@@ -333,25 +343,26 @@ async def test_telegram_status_multi_entry(default_conf, update, mocker, fee) ->
trade = trades[3]
# Average may be empty on some exchanges
trade.orders[0].average = 0
- trade.orders.append(Order(
- order_id='5412vbb',
- ft_order_side='buy',
- ft_pair=trade.pair,
- ft_is_open=False,
- ft_amount=trade.amount,
- ft_price=trade.open_rate,
- status="closed",
- symbol=trade.pair,
- order_type="market",
- side="buy",
- price=trade.open_rate * 0.95,
- average=0,
- filled=trade.amount,
- remaining=0,
- cost=trade.amount,
- order_date=trade.open_date,
- order_filled_date=trade.open_date,
- )
+ trade.orders.append(
+ Order(
+ order_id="5412vbb",
+ ft_order_side="buy",
+ ft_pair=trade.pair,
+ ft_is_open=False,
+ ft_amount=trade.amount,
+ ft_price=trade.open_rate,
+ status="closed",
+ symbol=trade.pair,
+ order_type="market",
+ side="buy",
+ price=trade.open_rate * 0.95,
+ average=0,
+ filled=trade.amount,
+ remaining=0,
+ cost=trade.amount,
+ order_date=trade.open_date,
+ order_filled_date=trade.open_date,
+ )
)
trade.recalc_trade_from_orders()
Trade.commit()
@@ -359,15 +370,15 @@ async def test_telegram_status_multi_entry(default_conf, update, mocker, fee) ->
await telegram._status(update=update, context=MagicMock())
assert msg_mock.call_count == 4
msg = msg_mock.call_args_list[3][0][0]
- assert re.search(r'Number of Entries.*2', msg)
- assert re.search(r'Number of Exits.*1', msg)
- assert re.search(r'Close Date:', msg) is None
- assert re.search(r'Close Profit:', msg) is None
+ assert re.search(r"Number of Entries.*2", msg)
+ assert re.search(r"Number of Exits.*1", msg)
+ assert re.search(r"Close Date:", msg) is None
+ assert re.search(r"Close Profit:", msg) is None
@pytest.mark.usefixtures("init_persistence")
async def test_telegram_status_closed_trade(default_conf, update, mocker, fee) -> None:
- default_conf['position_adjustment_enable'] = True
+ default_conf["position_adjustment_enable"] = True
mocker.patch.multiple(
EXMS,
fetch_order=MagicMock(return_value=None),
@@ -383,12 +394,12 @@ async def test_telegram_status_closed_trade(default_conf, update, mocker, fee) -
await telegram._status(update=update, context=context)
assert msg_mock.call_count == 1
msg = msg_mock.call_args_list[0][0][0]
- assert re.search(r'Close Date:', msg)
- assert re.search(r'Close Profit:', msg)
+ assert re.search(r"Close Date:", msg)
+ assert re.search(r"Close Profit:", msg)
async def test_order_handle(default_conf, update, ticker, fee, mocker) -> None:
- default_conf['max_open_trades'] = 3
+ default_conf["max_open_trades"] = 3
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -397,7 +408,7 @@ async def test_order_handle(default_conf, update, ticker, fee, mocker) -> None:
)
status_table = MagicMock()
mocker.patch.multiple(
- 'freqtrade.rpc.telegram.Telegram',
+ "freqtrade.rpc.telegram.Telegram",
_status_table=status_table,
)
@@ -411,7 +422,7 @@ async def test_order_handle(default_conf, update, ticker, fee, mocker) -> None:
# Create some test data
freqtradebot.enter_positions()
- mocker.patch('freqtrade.rpc.telegram.MAX_MESSAGE_LENGTH', 500)
+ mocker.patch("freqtrade.rpc.telegram.MAX_MESSAGE_LENGTH", 500)
msg_mock.reset_mock()
context = MagicMock()
@@ -422,10 +433,10 @@ async def test_order_handle(default_conf, update, ticker, fee, mocker) -> None:
msg1 = msg_mock.call_args_list[0][0][0]
- assert 'Order List for Trade #*`2`' in msg1
+ assert "Order List for Trade #*`2`" in msg1
msg_mock.reset_mock()
- mocker.patch('freqtrade.rpc.telegram.MAX_MESSAGE_LENGTH', 50)
+ mocker.patch("freqtrade.rpc.telegram.MAX_MESSAGE_LENGTH", 50)
context = MagicMock()
context.args = ["2"]
await telegram._order(update=update, context=context)
@@ -435,14 +446,14 @@ async def test_order_handle(default_conf, update, ticker, fee, mocker) -> None:
msg1 = msg_mock.call_args_list[0][0][0]
msg2 = msg_mock.call_args_list[1][0][0]
- assert 'Order List for Trade #*`2`' in msg1
- assert '*Order List for Trade #*`2` - continued' in msg2
+ assert "Order List for Trade #*`2`" in msg1
+ assert "*Order List for Trade #*`2` - continued" in msg2
@pytest.mark.usefixtures("init_persistence")
async def test_telegram_order_multi_entry(default_conf, update, mocker, fee) -> None:
- default_conf['telegram']['enabled'] = False
- default_conf['position_adjustment_enable'] = True
+ default_conf["telegram"]["enabled"] = False
+ default_conf["position_adjustment_enable"] = True
mocker.patch.multiple(
EXMS,
fetch_order=MagicMock(return_value=None),
@@ -456,25 +467,26 @@ async def test_telegram_order_multi_entry(default_conf, update, mocker, fee) ->
trade = trades[3]
# Average may be empty on some exchanges
trade.orders[0].average = 0
- trade.orders.append(Order(
- order_id='5412vbb',
- ft_order_side='buy',
- ft_pair=trade.pair,
- ft_is_open=False,
- ft_amount=trade.amount,
- ft_price=trade.open_rate,
- status="closed",
- symbol=trade.pair,
- order_type="market",
- side="buy",
- price=trade.open_rate * 0.95,
- average=0,
- filled=trade.amount,
- remaining=0,
- cost=trade.amount,
- order_date=trade.open_date,
- order_filled_date=trade.open_date,
- )
+ trade.orders.append(
+ Order(
+ order_id="5412vbb",
+ ft_order_side="buy",
+ ft_pair=trade.pair,
+ ft_is_open=False,
+ ft_amount=trade.amount,
+ ft_price=trade.open_rate,
+ status="closed",
+ symbol=trade.pair,
+ order_type="market",
+ side="buy",
+ price=trade.open_rate * 0.95,
+ average=0,
+ filled=trade.amount,
+ remaining=0,
+ cost=trade.amount,
+ order_date=trade.open_date,
+ order_filled_date=trade.open_date,
+ )
)
trade.recalc_trade_from_orders()
Trade.commit()
@@ -482,12 +494,12 @@ async def test_telegram_order_multi_entry(default_conf, update, mocker, fee) ->
await telegram._order(update=update, context=MagicMock())
assert msg_mock.call_count == 4
msg = msg_mock.call_args_list[3][0][0]
- assert re.search(r'from 1st entry rate', msg)
- assert re.search(r'Order Filled', msg)
+ assert re.search(r"from 1st entry rate", msg)
+ assert re.search(r"Order Filled", msg)
async def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
- default_conf['max_open_trades'] = 3
+ default_conf["max_open_trades"] = 3
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -496,7 +508,7 @@ async def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
)
status_table = MagicMock()
mocker.patch.multiple(
- 'freqtrade.rpc.telegram.Telegram',
+ "freqtrade.rpc.telegram.Telegram",
_status_table=status_table,
)
@@ -508,13 +520,13 @@ async def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
# Status is also enabled when stopped
await telegram._status(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'no active trade' in msg_mock.call_args_list[0][0][0]
+ assert "no active trade" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
freqtradebot.state = State.RUNNING
await telegram._status(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'no active trade' in msg_mock.call_args_list[0][0][0]
+ assert "no active trade" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
# Create some test data
@@ -524,14 +536,14 @@ async def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
# close_rate should not be included in the message as the trade is not closed
# and no line should be empty
- lines = msg_mock.call_args_list[0][0][0].split('\n')
- assert '' not in lines[:-1]
- assert 'Close Rate' not in ''.join(lines)
- assert 'Close Profit' not in ''.join(lines)
+ lines = msg_mock.call_args_list[0][0][0].split("\n")
+ assert "" not in lines[:-1]
+ assert "Close Rate" not in "".join(lines)
+ assert "Close Profit" not in "".join(lines)
assert msg_mock.call_count == 3
- assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0]
- assert 'LTC/BTC' in msg_mock.call_args_list[1][0][0]
+ assert "ETH/BTC" in msg_mock.call_args_list[0][0][0]
+ assert "LTC/BTC" in msg_mock.call_args_list[1][0][0]
msg_mock.reset_mock()
context = MagicMock()
@@ -539,15 +551,15 @@ async def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
await telegram._status(update=update, context=context)
- lines = msg_mock.call_args_list[0][0][0].split('\n')
- assert '' not in lines[:-1]
- assert 'Close Rate' not in ''.join(lines)
- assert 'Close Profit' not in ''.join(lines)
+ lines = msg_mock.call_args_list[0][0][0].split("\n")
+ assert "" not in lines[:-1]
+ assert "Close Rate" not in "".join(lines)
+ assert "Close Profit" not in "".join(lines)
assert msg_mock.call_count == 2
- assert 'LTC/BTC' in msg_mock.call_args_list[0][0][0]
+ assert "LTC/BTC" in msg_mock.call_args_list[0][0][0]
- mocker.patch('freqtrade.rpc.telegram.MAX_MESSAGE_LENGTH', 500)
+ mocker.patch("freqtrade.rpc.telegram.MAX_MESSAGE_LENGTH", 500)
msg_mock.reset_mock()
context = MagicMock()
@@ -558,8 +570,8 @@ async def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
msg1 = msg_mock.call_args_list[0][0][0]
- assert 'Close Rate' not in msg1
- assert 'Trade ID:* `2`' in msg1
+ assert "Close Rate" not in msg1
+ assert "Trade ID:* `2`" in msg1
async def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
@@ -569,7 +581,7 @@ async def test_status_table_handle(default_conf, update, ticker, fee, mocker) ->
get_fee=fee,
)
- default_conf['stake_amount'] = 15.0
+ default_conf["stake_amount"] = 15.0
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
@@ -579,13 +591,13 @@ async def test_status_table_handle(default_conf, update, ticker, fee, mocker) ->
# Status table is also enabled when stopped
await telegram._status_table(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'no active trade' in msg_mock.call_args_list[0][0][0]
+ assert "no active trade" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
freqtradebot.state = State.RUNNING
await telegram._status_table(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'no active trade' in msg_mock.call_args_list[0][0][0]
+ assert "no active trade" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
# Create some test data
@@ -593,21 +605,18 @@ async def test_status_table_handle(default_conf, update, ticker, fee, mocker) ->
await telegram._status_table(update=update, context=MagicMock())
- text = re.sub('?pre>', '', msg_mock.call_args_list[-1][0][0])
+ text = re.sub("?pre>", "", msg_mock.call_args_list[-1][0][0])
line = text.split("\n")
- fields = re.sub('[ ]+', ' ', line[2].strip()).split(' ')
+ fields = re.sub("[ ]+", " ", line[2].strip()).split(" ")
assert int(fields[0]) == 1
# assert 'L' in fields[1]
- assert 'ETH/BTC' in fields[1]
+ assert "ETH/BTC" in fields[1]
assert msg_mock.call_count == 1
async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time_machine) -> None:
- mocker.patch(
- 'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price',
- return_value=1.1
- )
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1)
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -617,7 +626,7 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
telegram, _freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt)
# Move date to within day
- time_machine.move_to('2022-06-11 08:00:00+00:00')
+ time_machine.move_to("2022-06-11 08:00:00+00:00")
# Create some test data
create_mock_trades_usdt(fee)
@@ -628,13 +637,13 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
await telegram._daily(update=update, context=context)
assert msg_mock.call_count == 1
assert "Daily Profit over the last 2 days:" in msg_mock.call_args_list[0][0][0]
- assert 'Day ' in msg_mock.call_args_list[0][0][0]
+ assert "Day " in msg_mock.call_args_list[0][0][0]
assert str(datetime.now(timezone.utc).date()) in msg_mock.call_args_list[0][0][0]
- assert ' 6.83 USDT' in msg_mock.call_args_list[0][0][0]
- assert ' 7.51 USD' in msg_mock.call_args_list[0][0][0]
- assert '(2)' in msg_mock.call_args_list[0][0][0]
- assert '(2) 6.83 USDT 7.51 USD 0.64%' in msg_mock.call_args_list[0][0][0]
- assert '(0)' in msg_mock.call_args_list[0][0][0]
+ assert " 6.83 USDT" in msg_mock.call_args_list[0][0][0]
+ assert " 7.51 USD" in msg_mock.call_args_list[0][0][0]
+ assert "(2)" in msg_mock.call_args_list[0][0][0]
+ assert "(2) 6.83 USDT 7.51 USD 0.64%" in msg_mock.call_args_list[0][0][0]
+ assert "(0)" in msg_mock.call_args_list[0][0][0]
# Reset msg_mock
msg_mock.reset_mock()
@@ -643,13 +652,15 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
assert msg_mock.call_count == 1
assert "Daily Profit over the last 7 days:" in msg_mock.call_args_list[0][0][0]
assert str(datetime.now(timezone.utc).date()) in msg_mock.call_args_list[0][0][0]
- assert str((datetime.now(timezone.utc) - timedelta(days=5)).date()
- ) in msg_mock.call_args_list[0][0][0]
- assert ' 6.83 USDT' in msg_mock.call_args_list[0][0][0]
- assert ' 7.51 USD' in msg_mock.call_args_list[0][0][0]
- assert '(2)' in msg_mock.call_args_list[0][0][0]
- assert '(1)' in msg_mock.call_args_list[0][0][0]
- assert '(0)' in msg_mock.call_args_list[0][0][0]
+ assert (
+ str((datetime.now(timezone.utc) - timedelta(days=5)).date())
+ in msg_mock.call_args_list[0][0][0]
+ )
+ assert " 6.83 USDT" in msg_mock.call_args_list[0][0][0]
+ assert " 7.51 USD" in msg_mock.call_args_list[0][0][0]
+ assert "(2)" in msg_mock.call_args_list[0][0][0]
+ assert "(1)" in msg_mock.call_args_list[0][0][0]
+ assert "(0)" in msg_mock.call_args_list[0][0][0]
# Reset msg_mock
msg_mock.reset_mock()
@@ -658,16 +669,13 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
context = MagicMock()
context.args = ["1"]
await telegram._daily(update=update, context=context)
- assert ' 6.83 USDT' in msg_mock.call_args_list[0][0][0]
- assert ' 7.51 USD' in msg_mock.call_args_list[0][0][0]
- assert '(2)' in msg_mock.call_args_list[0][0][0]
+ assert " 6.83 USDT" in msg_mock.call_args_list[0][0][0]
+ assert " 7.51 USD" in msg_mock.call_args_list[0][0][0]
+ assert "(2)" in msg_mock.call_args_list[0][0][0]
async def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
- mocker.patch.multiple(
- EXMS,
- fetch_ticker=ticker
- )
+ mocker.patch.multiple(EXMS, fetch_ticker=ticker)
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -680,7 +688,7 @@ async def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
context.args = ["-2"]
await telegram._daily(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]
+ assert "must be an integer greater than 0" in msg_mock.call_args_list[0][0][0]
# Try invalid data
msg_mock.reset_mock()
@@ -689,15 +697,12 @@ async def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
context = MagicMock()
context.args = ["today"]
await telegram._daily(update=update, context=context)
- assert 'Daily Profit over the last 7 days:' in msg_mock.call_args_list[0][0][0]
+ assert "Daily Profit over the last 7 days:" in msg_mock.call_args_list[0][0][0]
async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, time_machine) -> None:
- default_conf_usdt['max_open_trades'] = 1
- mocker.patch(
- 'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price',
- return_value=1.1
- )
+ default_conf_usdt["max_open_trades"] = 1
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1)
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -706,7 +711,7 @@ async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, tim
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt)
# Move to saturday - so all trades are within that week
- time_machine.move_to('2022-06-11')
+ time_machine.move_to("2022-06-11")
create_mock_trades_usdt(fee)
# Try valid data
@@ -715,29 +720,33 @@ async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, tim
context.args = ["2"]
await telegram._weekly(update=update, context=context)
assert msg_mock.call_count == 1
- assert "Weekly Profit over the last 2 weeks (starting from Monday):" \
- in msg_mock.call_args_list[0][0][0]
- assert 'Monday ' in msg_mock.call_args_list[0][0][0]
+ assert (
+ "Weekly Profit over the last 2 weeks (starting from Monday):"
+ in msg_mock.call_args_list[0][0][0]
+ )
+ assert "Monday " in msg_mock.call_args_list[0][0][0]
today = datetime.now(timezone.utc).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 ' 2.74 USDT' in msg_mock.call_args_list[0][0][0]
- assert ' 3.01 USD' in msg_mock.call_args_list[0][0][0]
- assert '(3)' in msg_mock.call_args_list[0][0][0]
- assert '(0)' in msg_mock.call_args_list[0][0][0]
+ assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
+ assert " 3.01 USD" in msg_mock.call_args_list[0][0][0]
+ assert "(3)" in msg_mock.call_args_list[0][0][0]
+ assert "(0)" in msg_mock.call_args_list[0][0][0]
# Reset msg_mock
msg_mock.reset_mock()
context.args = []
await telegram._weekly(update=update, context=context)
assert msg_mock.call_count == 1
- assert "Weekly Profit over the last 8 weeks (starting from Monday):" \
- in msg_mock.call_args_list[0][0][0]
- assert 'Weekly' in msg_mock.call_args_list[0][0][0]
- assert ' 2.74 USDT' in msg_mock.call_args_list[0][0][0]
- assert ' 3.01 USD' in msg_mock.call_args_list[0][0][0]
- assert '(3)' in msg_mock.call_args_list[0][0][0]
- assert '(0)' in msg_mock.call_args_list[0][0][0]
+ assert (
+ "Weekly Profit over the last 8 weeks (starting from Monday):"
+ in msg_mock.call_args_list[0][0][0]
+ )
+ assert "Weekly" in msg_mock.call_args_list[0][0][0]
+ assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
+ assert " 3.01 USD" in msg_mock.call_args_list[0][0][0]
+ assert "(3)" in msg_mock.call_args_list[0][0][0]
+ assert "(0)" in msg_mock.call_args_list[0][0][0]
# Try invalid data
msg_mock.reset_mock()
@@ -747,7 +756,7 @@ async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, tim
context.args = ["-3"]
await 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]
+ assert "must be an integer greater than 0" in msg_mock.call_args_list[0][0][0]
# Try invalid data
msg_mock.reset_mock()
@@ -757,17 +766,14 @@ async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, tim
context.args = ["this week"]
await telegram._weekly(update=update, context=context)
assert (
- 'Weekly Profit over the last 8 weeks (starting from Monday):'
+ "Weekly Profit over the last 8 weeks (starting from Monday):"
in msg_mock.call_args_list[0][0][0]
)
async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, time_machine) -> None:
- default_conf_usdt['max_open_trades'] = 1
- mocker.patch(
- 'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price',
- return_value=1.1
- )
+ default_conf_usdt["max_open_trades"] = 1
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1)
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -776,7 +782,7 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt)
# Move to day within the month so all mock trades fall into this week.
- time_machine.move_to('2022-06-11')
+ time_machine.move_to("2022-06-11")
create_mock_trades_usdt(fee)
# Try valid data
@@ -785,15 +791,15 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti
context.args = ["2"]
await telegram._monthly(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Monthly Profit over the last 2 months:' in msg_mock.call_args_list[0][0][0]
- assert 'Month ' in msg_mock.call_args_list[0][0][0]
+ assert "Monthly Profit over the last 2 months:" in msg_mock.call_args_list[0][0][0]
+ assert "Month " in msg_mock.call_args_list[0][0][0]
today = datetime.now(timezone.utc).date()
current_month = f"{today.year}-{today.month:02} "
assert current_month in msg_mock.call_args_list[0][0][0]
- assert ' 2.74 USDT' in msg_mock.call_args_list[0][0][0]
- assert ' 3.01 USD' in msg_mock.call_args_list[0][0][0]
- assert '(3)' in msg_mock.call_args_list[0][0][0]
- assert '(0)' in msg_mock.call_args_list[0][0][0]
+ assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
+ assert " 3.01 USD" in msg_mock.call_args_list[0][0][0]
+ assert "(3)" in msg_mock.call_args_list[0][0][0]
+ assert "(0)" in msg_mock.call_args_list[0][0][0]
# Reset msg_mock
msg_mock.reset_mock()
@@ -801,13 +807,13 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti
await telegram._monthly(update=update, context=context)
assert msg_mock.call_count == 1
# Default to 6 months
- assert 'Monthly Profit over the last 6 months:' in msg_mock.call_args_list[0][0][0]
- assert 'Month ' in msg_mock.call_args_list[0][0][0]
+ assert "Monthly Profit over the last 6 months:" 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 ' 2.74 USDT' in msg_mock.call_args_list[0][0][0]
- assert ' 3.01 USD' in msg_mock.call_args_list[0][0][0]
- assert '(3)' in msg_mock.call_args_list[0][0][0]
- assert '(0)' in msg_mock.call_args_list[0][0][0]
+ assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
+ assert " 3.01 USD" in msg_mock.call_args_list[0][0][0]
+ assert "(3)" in msg_mock.call_args_list[0][0][0]
+ assert "(0)" in msg_mock.call_args_list[0][0][0]
# Reset msg_mock
msg_mock.reset_mock()
@@ -817,14 +823,14 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti
context.args = ["12"]
await telegram._monthly(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Monthly Profit over the last 12 months:' in msg_mock.call_args_list[0][0][0]
- assert ' 2.74 USDT' in msg_mock.call_args_list[0][0][0]
- assert ' 3.01 USD' in msg_mock.call_args_list[0][0][0]
- assert '(3)' in msg_mock.call_args_list[0][0][0]
+ assert "Monthly Profit over the last 12 months:" in msg_mock.call_args_list[0][0][0]
+ assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
+ assert " 3.01 USD" in msg_mock.call_args_list[0][0][0]
+ assert "(3)" 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 '-09' in msg_mock.call_args_list[0][0][0]
+ assert "-09" in msg_mock.call_args_list[0][0][0]
# Try invalid data
msg_mock.reset_mock()
@@ -834,7 +840,7 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti
context.args = ["-3"]
await 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]
+ assert "must be an integer greater than 0" in msg_mock.call_args_list[0][0][0]
# Try invalid data
msg_mock.reset_mock()
@@ -843,13 +849,13 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti
context = MagicMock()
context.args = ["february"]
await telegram._monthly(update=update, context=context)
- assert 'Monthly Profit over the last 6 months:' in msg_mock.call_args_list[0][0][0]
+ assert "Monthly Profit over the last 6 months:" in msg_mock.call_args_list[0][0][0]
async def test_telegram_profit_handle(
- default_conf_usdt, update, ticker_usdt, ticker_sell_up, fee,
- limit_sell_order_usdt, mocker) -> None:
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=1.1)
+ default_conf_usdt, update, ticker_usdt, ticker_sell_up, fee, limit_sell_order_usdt, mocker
+) -> None:
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1)
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker_usdt,
@@ -861,7 +867,7 @@ async def test_telegram_profit_handle(
await telegram._profit(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
@@ -873,19 +879,22 @@ async def test_telegram_profit_handle(
context.args = ["aaa"]
await telegram._profit(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'No closed trade' in msg_mock.call_args_list[-1][0][0]
- assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
- mocker.patch('freqtrade.wallets.Wallets.get_starting_balance', return_value=1000)
- assert ('∙ `0.298 USDT (0.50%) (0.03 \N{GREEK CAPITAL LETTER SIGMA}%)`'
- in msg_mock.call_args_list[-1][0][0])
+ assert "No closed trade" in msg_mock.call_args_list[-1][0][0]
+ assert "*ROI:* All trades" in msg_mock.call_args_list[-1][0][0]
+ mocker.patch("freqtrade.wallets.Wallets.get_starting_balance", return_value=1000)
+ assert (
+ "∙ `0.298 USDT (0.50%) (0.03 \N{GREEK CAPITAL LETTER SIGMA}%)`"
+ in msg_mock.call_args_list[-1][0][0]
+ )
msg_mock.reset_mock()
# Update the ticker with a market going up
- mocker.patch(f'{EXMS}.fetch_ticker', ticker_sell_up)
+ mocker.patch(f"{EXMS}.fetch_ticker", ticker_sell_up)
# Simulate fulfilled LIMIT_SELL order for trade
trade = Trade.session.scalars(select(Trade)).first()
oobj = Order.parse_from_ccxt_object(
- limit_sell_order_usdt, limit_sell_order_usdt['symbol'], 'sell')
+ limit_sell_order_usdt, limit_sell_order_usdt["symbol"], "sell"
+ )
trade.orders.append(oobj)
trade.update_trade(oobj)
@@ -896,26 +905,30 @@ async def test_telegram_profit_handle(
context.args = [3]
await telegram._profit(update=update, context=context)
assert msg_mock.call_count == 1
- assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0]
- assert ('∙ `5.685 USDT (9.45%) (0.57 \N{GREEK CAPITAL LETTER SIGMA}%)`'
- in msg_mock.call_args_list[-1][0][0])
- assert '∙ `6.253 USD`' in msg_mock.call_args_list[-1][0][0]
- assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
- assert ('∙ `5.685 USDT (9.45%) (0.57 \N{GREEK CAPITAL LETTER SIGMA}%)`'
- in msg_mock.call_args_list[-1][0][0])
- assert '∙ `6.253 USD`' in msg_mock.call_args_list[-1][0][0]
+ assert "*ROI:* Closed trades" in msg_mock.call_args_list[-1][0][0]
+ assert (
+ "∙ `5.685 USDT (9.45%) (0.57 \N{GREEK CAPITAL LETTER SIGMA}%)`"
+ in msg_mock.call_args_list[-1][0][0]
+ )
+ assert "∙ `6.253 USD`" in msg_mock.call_args_list[-1][0][0]
+ assert "*ROI:* All trades" in msg_mock.call_args_list[-1][0][0]
+ assert (
+ "∙ `5.685 USDT (9.45%) (0.57 \N{GREEK CAPITAL LETTER SIGMA}%)`"
+ in msg_mock.call_args_list[-1][0][0]
+ )
+ assert "∙ `6.253 USD`" in msg_mock.call_args_list[-1][0][0]
- assert '*Best Performing:* `ETH/USDT: 9.45%`' in msg_mock.call_args_list[-1][0][0]
- assert '*Max Drawdown:*' in msg_mock.call_args_list[-1][0][0]
- assert '*Profit factor:*' in msg_mock.call_args_list[-1][0][0]
- assert '*Winrate:*' in msg_mock.call_args_list[-1][0][0]
- assert '*Expectancy (Ratio):*' in msg_mock.call_args_list[-1][0][0]
- assert '*Trading volume:* `126 USDT`' in msg_mock.call_args_list[-1][0][0]
+ assert "*Best Performing:* `ETH/USDT: 9.45%`" in msg_mock.call_args_list[-1][0][0]
+ assert "*Max Drawdown:*" in msg_mock.call_args_list[-1][0][0]
+ assert "*Profit factor:*" in msg_mock.call_args_list[-1][0][0]
+ assert "*Winrate:*" in msg_mock.call_args_list[-1][0][0]
+ assert "*Expectancy (Ratio):*" in msg_mock.call_args_list[-1][0][0]
+ assert "*Trading volume:* `126 USDT`" in msg_mock.call_args_list[-1][0][0]
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
async def test_telegram_stats(default_conf, update, ticker, fee, mocker, is_short) -> None:
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0)
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -926,7 +939,7 @@ async def test_telegram_stats(default_conf, update, ticker, fee, mocker, is_shor
await 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
@@ -934,20 +947,20 @@ async def test_telegram_stats(default_conf, update, ticker, fee, mocker, is_shor
await telegram._stats(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'Exit Reason' in msg_mock.call_args_list[-1][0][0]
- assert 'ROI' in msg_mock.call_args_list[-1][0][0]
- assert 'Avg. Duration' in msg_mock.call_args_list[-1][0][0]
+ assert "Exit Reason" in msg_mock.call_args_list[-1][0][0]
+ assert "ROI" in msg_mock.call_args_list[-1][0][0]
+ assert "Avg. Duration" in msg_mock.call_args_list[-1][0][0]
# Duration is not only N/A
- assert '0:19:00' in msg_mock.call_args_list[-1][0][0]
- assert 'N/A' in msg_mock.call_args_list[-1][0][0]
+ assert "0:19:00" in msg_mock.call_args_list[-1][0][0]
+ assert "N/A" in msg_mock.call_args_list[-1][0][0]
msg_mock.reset_mock()
async def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tickers) -> None:
- default_conf['dry_run'] = False
- mocker.patch(f'{EXMS}.get_balances', return_value=rpc_balance)
- mocker.patch(f'{EXMS}.get_tickers', tickers)
- mocker.patch(f'{EXMS}.get_valid_pair_combination', side_effect=lambda a, b: f"{a}/{b}")
+ default_conf["dry_run"] = False
+ mocker.patch(f"{EXMS}.get_balances", return_value=rpc_balance)
+ mocker.patch(f"{EXMS}.get_tickers", tickers)
+ mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: f"{a}/{b}")
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -959,40 +972,40 @@ async def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance
result = msg_mock.call_args_list[0][0][0]
result_full = msg_mock.call_args_list[1][0][0]
assert msg_mock.call_count == 2
- assert '*BTC:*' in result
- assert '*ETH:*' not in result
- assert '*USDT:*' not in result
- assert '*EUR:*' not in result
- assert '*LTC:*' not in result
+ assert "*BTC:*" in result
+ assert "*ETH:*" not in result
+ assert "*USDT:*" not in result
+ assert "*EUR:*" not in result
+ assert "*LTC:*" not in result
- assert '*LTC:*' in result_full
- assert '*XRP:*' not in result
- assert 'Balance:' in result
- assert 'Est. BTC:' in result
- assert 'BTC: 11' in result
- assert 'BTC: 12' in result_full
+ assert "*LTC:*" in result_full
+ assert "*XRP:*" not in result
+ assert "Balance:" in result
+ assert "Est. BTC:" in result
+ assert "BTC: 11" in result
+ assert "BTC: 12" in result_full
assert "*3 Other Currencies (< 0.0001 BTC):*" in result
- assert 'BTC: 0.00000309' in result
- assert '*Estimated Value*:' in result_full
- assert '*Estimated Value (Bot managed assets only)*:' in result
+ assert "BTC: 0.00000309" in result
+ assert "*Estimated Value*:" in result_full
+ assert "*Estimated Value (Bot managed assets only)*:" in result
async def test_balance_handle_empty_response(default_conf, update, mocker) -> None:
- default_conf['dry_run'] = False
- mocker.patch(f'{EXMS}.get_balances', return_value={})
+ default_conf["dry_run"] = False
+ mocker.patch(f"{EXMS}.get_balances", return_value={})
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
- freqtradebot.config['dry_run'] = False
+ freqtradebot.config["dry_run"] = False
await telegram._balance(update=update, context=MagicMock())
result = msg_mock.call_args_list[0][0][0]
assert msg_mock.call_count == 1
- assert 'Starting capital: `0 BTC' in result
+ assert "Starting capital: `0 BTC" in result
async def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None:
- mocker.patch(f'{EXMS}.get_balances', return_value={})
+ mocker.patch(f"{EXMS}.get_balances", return_value={})
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -1008,31 +1021,36 @@ async def test_balance_handle_too_large_response(default_conf, update, mocker) -
balances = []
for i in range(100):
curr = choice(ascii_uppercase) + choice(ascii_uppercase) + choice(ascii_uppercase)
- balances.append({
- 'currency': curr,
- 'free': 1.0,
- 'used': 0.5,
- 'balance': i,
- 'bot_owned': 0.5,
- 'est_stake': 1,
- 'est_stake_bot': 1,
- 'stake': 'BTC',
- 'is_position': False,
- 'leverage': 1.0,
- 'position': 0.0,
- 'side': 'long',
- 'is_bot_managed': True,
- })
- mocker.patch('freqtrade.rpc.rpc.RPC._rpc_balance', return_value={
- 'currencies': balances,
- 'total': 100.0,
- 'total_bot': 100.0,
- 'symbol': 100.0,
- 'value': 1000.0,
- 'value_bot': 1000.0,
- 'starting_capital': 1000,
- 'starting_capital_fiat': 1000,
- })
+ balances.append(
+ {
+ "currency": curr,
+ "free": 1.0,
+ "used": 0.5,
+ "balance": i,
+ "bot_owned": 0.5,
+ "est_stake": 1,
+ "est_stake_bot": 1,
+ "stake": "BTC",
+ "is_position": False,
+ "leverage": 1.0,
+ "position": 0.0,
+ "side": "long",
+ "is_bot_managed": True,
+ }
+ )
+ mocker.patch(
+ "freqtrade.rpc.rpc.RPC._rpc_balance",
+ return_value={
+ "currencies": balances,
+ "total": 100.0,
+ "total_bot": 100.0,
+ "symbol": 100.0,
+ "value": 1000.0,
+ "value_bot": 1000.0,
+ "starting_capital": 1000,
+ "starting_capital_fiat": 1000,
+ },
+ )
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -1047,7 +1065,6 @@ async def test_balance_handle_too_large_response(default_conf, update, mocker) -
async def test_start_handle(default_conf, update, mocker) -> None:
-
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
freqtradebot.state = State.STOPPED
@@ -1058,7 +1075,6 @@ async def test_start_handle(default_conf, update, mocker) -> None:
async def test_start_handle_already_running(default_conf, update, mocker) -> None:
-
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
freqtradebot.state = State.RUNNING
@@ -1066,11 +1082,10 @@ async def test_start_handle_already_running(default_conf, update, mocker) -> Non
await telegram._start(update=update, context=MagicMock())
assert freqtradebot.state == State.RUNNING
assert msg_mock.call_count == 1
- assert 'already running' in msg_mock.call_args_list[0][0][0]
+ assert "already running" in msg_mock.call_args_list[0][0][0]
async def test_stop_handle(default_conf, update, mocker) -> None:
-
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
freqtradebot.state = State.RUNNING
@@ -1078,11 +1093,10 @@ async def test_stop_handle(default_conf, update, mocker) -> None:
await telegram._stop(update=update, context=MagicMock())
assert freqtradebot.state == State.STOPPED
assert msg_mock.call_count == 1
- assert 'stopping trader' in msg_mock.call_args_list[0][0][0]
+ assert "stopping trader" in msg_mock.call_args_list[0][0][0]
async def test_stop_handle_already_stopped(default_conf, update, mocker) -> None:
-
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
freqtradebot.state = State.STOPPED
@@ -1090,23 +1104,23 @@ async def test_stop_handle_already_stopped(default_conf, update, mocker) -> None
await telegram._stop(update=update, context=MagicMock())
assert freqtradebot.state == State.STOPPED
assert msg_mock.call_count == 1
- assert 'already stopped' in msg_mock.call_args_list[0][0][0]
+ assert "already stopped" in msg_mock.call_args_list[0][0][0]
async def test_stopbuy_handle(default_conf, update, mocker) -> None:
-
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
- assert freqtradebot.config['max_open_trades'] != 0
+ assert freqtradebot.config["max_open_trades"] != 0
await telegram._stopentry(update=update, context=MagicMock())
- assert freqtradebot.config['max_open_trades'] == 0
+ assert freqtradebot.config["max_open_trades"] == 0
assert msg_mock.call_count == 1
- assert 'No more entries will occur from now. Run /reload_config to reset.' \
+ assert (
+ "No more entries will occur from now. Run /reload_config to reset."
in msg_mock.call_args_list[0][0][0]
+ )
async def test_reload_config_handle(default_conf, update, mocker) -> None:
-
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
freqtradebot.state = State.RUNNING
@@ -1114,14 +1128,15 @@ async def test_reload_config_handle(default_conf, update, mocker) -> None:
await telegram._reload_config(update=update, context=MagicMock())
assert freqtradebot.state == State.RELOAD_CONFIG
assert msg_mock.call_count == 1
- assert 'Reloading config' in msg_mock.call_args_list[0][0][0]
+ assert "Reloading config" in msg_mock.call_args_list[0][0][0]
-async def test_telegram_forceexit_handle(default_conf, update, ticker, fee,
- ticker_sell_up, mocker) -> None:
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
- msg_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+async def test_telegram_forceexit_handle(
+ default_conf, update, ticker, fee, ticker_sell_up, mocker
+) -> None:
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0)
+ msg_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
patch_exchange(mocker)
patch_whitelist(mocker, default_conf)
mocker.patch.multiple(
@@ -1143,7 +1158,7 @@ async def test_telegram_forceexit_handle(default_conf, update, ticker, fee,
assert trade
# Increase the price and sell it
- mocker.patch(f'{EXMS}.fetch_ticker', ticker_sell_up)
+ mocker.patch(f"{EXMS}.fetch_ticker", ticker_sell_up)
# /forceexit 1
context = MagicMock()
@@ -1153,45 +1168,47 @@ async def test_telegram_forceexit_handle(default_conf, update, ticker, fee,
assert msg_mock.call_count == 4
last_msg = msg_mock.call_args_list[-2][0][0]
assert {
- 'type': RPCMessageType.EXIT,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'gain': 'profit',
- 'leverage': 1.0,
- 'limit': 1.173e-05,
- 'order_rate': 1.173e-05,
- 'amount': 91.07468123,
- 'order_type': 'limit',
- 'open_rate': 1.098e-05,
- 'current_rate': 1.173e-05,
- 'direction': 'Long',
- 'profit_amount': 6.314e-05,
- 'profit_ratio': 0.0629778,
- 'stake_currency': 'BTC',
- 'quote_currency': 'BTC',
- 'base_currency': 'ETH',
- 'fiat_currency': 'USD',
- 'buy_tag': ANY,
- 'enter_tag': ANY,
- 'exit_reason': ExitType.FORCE_EXIT.value,
- 'open_date': ANY,
- 'close_date': ANY,
- 'close_rate': ANY,
- 'stake_amount': 0.0009999999999054,
- 'sub_trade': False,
- 'cumulative_profit': 0.0,
- 'is_final_exit': False,
- 'final_profit_ratio': None,
+ "type": RPCMessageType.EXIT,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "gain": "profit",
+ "leverage": 1.0,
+ "limit": 1.173e-05,
+ "order_rate": 1.173e-05,
+ "amount": 91.07468123,
+ "order_type": "limit",
+ "open_rate": 1.098e-05,
+ "current_rate": 1.173e-05,
+ "direction": "Long",
+ "profit_amount": 6.314e-05,
+ "profit_ratio": 0.0629778,
+ "stake_currency": "BTC",
+ "quote_currency": "BTC",
+ "base_currency": "ETH",
+ "fiat_currency": "USD",
+ "buy_tag": ANY,
+ "enter_tag": ANY,
+ "exit_reason": ExitType.FORCE_EXIT.value,
+ "open_date": ANY,
+ "close_date": ANY,
+ "close_rate": ANY,
+ "stake_amount": 0.0009999999999054,
+ "sub_trade": False,
+ "cumulative_profit": 0.0,
+ "is_final_exit": False,
+ "final_profit_ratio": None,
} == last_msg
-async def test_telegram_force_exit_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)
- msg_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+async def test_telegram_force_exit_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
+ )
+ msg_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
patch_exchange(mocker)
patch_whitelist(mocker, default_conf)
@@ -1211,10 +1228,7 @@ async def test_telegram_force_exit_down_handle(default_conf, update, ticker, fee
freqtradebot.enter_positions()
# Decrease the price and sell it
- mocker.patch.multiple(
- EXMS,
- fetch_ticker=ticker_sell_down
- )
+ mocker.patch.multiple(EXMS, fetch_ticker=ticker_sell_down)
trade = Trade.session.scalars(select(Trade)).first()
assert trade
@@ -1228,45 +1242,46 @@ async def test_telegram_force_exit_down_handle(default_conf, update, ticker, fee
last_msg = msg_mock.call_args_list[-2][0][0]
assert {
- 'type': RPCMessageType.EXIT,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'gain': 'loss',
- 'leverage': 1.0,
- 'limit': 1.043e-05,
- 'order_rate': 1.043e-05,
- 'amount': 91.07468123,
- 'order_type': 'limit',
- 'open_rate': 1.098e-05,
- 'current_rate': 1.043e-05,
- 'direction': 'Long',
- 'profit_amount': -5.497e-05,
- 'profit_ratio': -0.05482878,
- 'stake_currency': 'BTC',
- 'quote_currency': 'BTC',
- 'base_currency': 'ETH',
- 'fiat_currency': 'USD',
- 'buy_tag': ANY,
- 'enter_tag': ANY,
- 'exit_reason': ExitType.FORCE_EXIT.value,
- 'open_date': ANY,
- 'close_date': ANY,
- 'close_rate': ANY,
- 'stake_amount': 0.0009999999999054,
- 'sub_trade': False,
- 'cumulative_profit': 0.0,
- 'is_final_exit': False,
- 'final_profit_ratio': None,
+ "type": RPCMessageType.EXIT,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "gain": "loss",
+ "leverage": 1.0,
+ "limit": 1.043e-05,
+ "order_rate": 1.043e-05,
+ "amount": 91.07468123,
+ "order_type": "limit",
+ "open_rate": 1.098e-05,
+ "current_rate": 1.043e-05,
+ "direction": "Long",
+ "profit_amount": -5.497e-05,
+ "profit_ratio": -0.05482878,
+ "stake_currency": "BTC",
+ "quote_currency": "BTC",
+ "base_currency": "ETH",
+ "fiat_currency": "USD",
+ "buy_tag": ANY,
+ "enter_tag": ANY,
+ "exit_reason": ExitType.FORCE_EXIT.value,
+ "open_date": ANY,
+ "close_date": ANY,
+ "close_rate": ANY,
+ "stake_amount": 0.0009999999999054,
+ "sub_trade": False,
+ "cumulative_profit": 0.0,
+ "is_final_exit": False,
+ "final_profit_ratio": None,
} == last_msg
async def test_forceexit_all_handle(default_conf, update, ticker, fee, mocker) -> None:
patch_exchange(mocker)
- mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
- return_value=15000.0)
- msg_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+ mocker.patch(
+ "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price", return_value=15000.0
+ )
+ msg_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
patch_whitelist(mocker, default_conf)
mocker.patch.multiple(
EXMS,
@@ -1274,7 +1289,7 @@ async def test_forceexit_all_handle(default_conf, update, ticker, fee, mocker) -
get_fee=fee,
_dry_is_price_crossed=MagicMock(return_value=True),
)
- default_conf['max_open_trades'] = 4
+ default_conf["max_open_trades"] = 4
freqtradebot = FreqtradeBot(default_conf)
rpc = RPC(freqtradebot)
telegram = Telegram(rpc, default_conf)
@@ -1293,42 +1308,43 @@ async def test_forceexit_all_handle(default_conf, update, ticker, fee, mocker) -
assert msg_mock.call_count == 8
msg = msg_mock.call_args_list[0][0][0]
assert {
- 'type': RPCMessageType.EXIT,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'gain': 'loss',
- 'leverage': 1.0,
- 'order_rate': 1.099e-05,
- 'limit': 1.099e-05,
- 'amount': 91.07468123,
- 'order_type': 'limit',
- 'open_rate': 1.098e-05,
- 'current_rate': 1.099e-05,
- 'direction': 'Long',
- 'profit_amount': -4.09e-06,
- 'profit_ratio': -0.00408133,
- 'stake_currency': 'BTC',
- 'quote_currency': 'BTC',
- 'base_currency': 'ETH',
- 'fiat_currency': 'USD',
- 'buy_tag': ANY,
- 'enter_tag': ANY,
- 'exit_reason': ExitType.FORCE_EXIT.value,
- 'open_date': ANY,
- 'close_date': ANY,
- 'close_rate': ANY,
- 'stake_amount': 0.0009999999999054,
- 'sub_trade': False,
- 'cumulative_profit': 0.0,
- 'is_final_exit': False,
- 'final_profit_ratio': None,
+ "type": RPCMessageType.EXIT,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "gain": "loss",
+ "leverage": 1.0,
+ "order_rate": 1.099e-05,
+ "limit": 1.099e-05,
+ "amount": 91.07468123,
+ "order_type": "limit",
+ "open_rate": 1.098e-05,
+ "current_rate": 1.099e-05,
+ "direction": "Long",
+ "profit_amount": -4.09e-06,
+ "profit_ratio": -0.00408133,
+ "stake_currency": "BTC",
+ "quote_currency": "BTC",
+ "base_currency": "ETH",
+ "fiat_currency": "USD",
+ "buy_tag": ANY,
+ "enter_tag": ANY,
+ "exit_reason": ExitType.FORCE_EXIT.value,
+ "open_date": ANY,
+ "close_date": ANY,
+ "close_rate": ANY,
+ "stake_amount": 0.0009999999999054,
+ "sub_trade": False,
+ "cumulative_profit": 0.0,
+ "is_final_exit": False,
+ "final_profit_ratio": None,
} == msg
async def test_forceexit_handle_invalid(default_conf, update, mocker) -> None:
- mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
- return_value=15000.0)
+ mocker.patch(
+ "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price", return_value=15000.0
+ )
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -1340,7 +1356,7 @@ async def test_forceexit_handle_invalid(default_conf, update, mocker) -> None:
context.args = ["1"]
await telegram._force_exit(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'not running' in msg_mock.call_args_list[0][0][0]
+ assert "not running" in msg_mock.call_args_list[0][0][0]
# Invalid argument
msg_mock.reset_mock()
@@ -1350,18 +1366,18 @@ async def test_forceexit_handle_invalid(default_conf, update, mocker) -> None:
context.args = ["123456"]
await telegram._force_exit(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'invalid argument' in msg_mock.call_args_list[0][0][0]
+ assert "invalid argument" in msg_mock.call_args_list[0][0][0]
async def test_force_exit_no_pair(default_conf, update, ticker, fee, mocker) -> None:
- default_conf['max_open_trades'] = 4
+ default_conf["max_open_trades"] = 4
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
get_fee=fee,
_dry_is_price_crossed=MagicMock(return_value=True),
)
- femock = mocker.patch('freqtrade.rpc.rpc.RPC._rpc_force_exit')
+ femock = mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_exit")
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -1371,7 +1387,7 @@ async def test_force_exit_no_pair(default_conf, update, ticker, fee, mocker) ->
context.args = []
await telegram._force_exit(update=update, context=context)
# No pair
- assert msg_mock.call_args_list[0][1]['msg'] == 'No open trade found.'
+ assert msg_mock.call_args_list[0][1]["msg"] == "No open trade found."
# Create some test data
freqtradebot.enter_positions()
@@ -1379,12 +1395,12 @@ async def test_force_exit_no_pair(default_conf, update, ticker, fee, mocker) ->
# /forceexit
await telegram._force_exit(update=update, context=context)
- keyboard = msg_mock.call_args_list[0][1]['keyboard']
+ keyboard = msg_mock.call_args_list[0][1]["keyboard"]
# 4 pairs + cancel
assert reduce(lambda acc, x: acc + len(x), keyboard, 0) == 5
assert keyboard[-1][0].text == "Cancel"
- assert keyboard[1][0].callback_data == 'force_exit__2 '
+ assert keyboard[1][0].callback_data == "force_exit__2 "
update = MagicMock()
update.callback_query = AsyncMock()
update.callback_query.data = keyboard[1][0].callback_data
@@ -1392,7 +1408,7 @@ async def test_force_exit_no_pair(default_conf, update, ticker, fee, mocker) ->
assert update.callback_query.answer.call_count == 1
assert update.callback_query.edit_message_text.call_count == 1
assert femock.call_count == 1
- assert femock.call_args_list[0][0][0] == '2'
+ assert femock.call_args_list[0][0][0] == "2"
# Retry exiting - but cancel instead
update.callback_query.reset_mock()
@@ -1403,14 +1419,14 @@ async def test_force_exit_no_pair(default_conf, update, ticker, fee, mocker) ->
query = update.callback_query
assert query.answer.call_count == 1
assert query.edit_message_text.call_count == 1
- assert query.edit_message_text.call_args_list[-1][1]['text'] == "Force exit canceled."
+ assert query.edit_message_text.call_args_list[-1][1]["text"] == "Force exit canceled."
async def test_force_enter_handle(default_conf, update, mocker) -> None:
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0)
fbuy_mock = MagicMock(return_value=None)
- mocker.patch('freqtrade.rpc.rpc.RPC._rpc_force_entry', fbuy_mock)
+ mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_entry", fbuy_mock)
telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -1421,26 +1437,26 @@ async def test_force_enter_handle(default_conf, update, mocker) -> None:
await telegram._force_enter(update=update, context=context, order_side=SignalDirection.LONG)
assert fbuy_mock.call_count == 1
- assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC'
+ assert fbuy_mock.call_args_list[0][0][0] == "ETH/BTC"
assert fbuy_mock.call_args_list[0][0][1] is None
- assert fbuy_mock.call_args_list[0][1]['order_side'] == SignalDirection.LONG
+ assert fbuy_mock.call_args_list[0][1]["order_side"] == SignalDirection.LONG
# Reset and retry with specified price
fbuy_mock = MagicMock(return_value=None)
- mocker.patch('freqtrade.rpc.rpc.RPC._rpc_force_entry', fbuy_mock)
+ mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_entry", fbuy_mock)
# /forcelong ETH/BTC 0.055
context = MagicMock()
context.args = ["ETH/BTC", "0.055"]
await telegram._force_enter(update=update, context=context, order_side=SignalDirection.LONG)
assert fbuy_mock.call_count == 1
- assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC'
+ assert fbuy_mock.call_args_list[0][0][0] == "ETH/BTC"
assert isinstance(fbuy_mock.call_args_list[0][0][1], float)
assert fbuy_mock.call_args_list[0][0][1] == 0.055
async def test_force_enter_handle_exception(default_conf, update, mocker) -> None:
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0)
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
patch_get_signal(freqtradebot)
@@ -1448,14 +1464,14 @@ async def test_force_enter_handle_exception(default_conf, update, mocker) -> Non
await telegram._force_enter(update=update, context=MagicMock(), order_side=SignalDirection.LONG)
assert msg_mock.call_count == 1
- assert msg_mock.call_args_list[0][0][0] == 'Force_entry not enabled.'
+ assert msg_mock.call_args_list[0][0][0] == "Force_entry not enabled."
async def test_force_enter_no_pair(default_conf, update, mocker) -> None:
- mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
+ mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0)
fbuy_mock = MagicMock(return_value=None)
- mocker.patch('freqtrade.rpc.rpc.RPC._rpc_force_entry', fbuy_mock)
+ mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_entry", fbuy_mock)
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
@@ -1467,29 +1483,28 @@ async def test_force_enter_no_pair(default_conf, update, mocker) -> None:
assert fbuy_mock.call_count == 0
assert msg_mock.call_count == 1
- assert msg_mock.call_args_list[0][1]['msg'] == 'Which pair?'
+ assert msg_mock.call_args_list[0][1]["msg"] == "Which pair?"
# assert msg_mock.call_args_list[0][1]['callback_query_handler'] == 'forcebuy'
- keyboard = msg_mock.call_args_list[0][1]['keyboard']
+ keyboard = msg_mock.call_args_list[0][1]["keyboard"]
# One additional button - cancel
assert reduce(lambda acc, x: acc + len(x), keyboard, 0) == 5
update = MagicMock()
update.callback_query = AsyncMock()
- update.callback_query.data = 'force_enter__XRP/USDT_||_long'
+ update.callback_query.data = "force_enter__XRP/USDT_||_long"
await telegram._force_enter_inline(update, None)
assert fbuy_mock.call_count == 1
fbuy_mock.reset_mock()
update.callback_query = AsyncMock()
- update.callback_query.data = 'force_enter__cancel'
+ update.callback_query.data = "force_enter__cancel"
await telegram._force_enter_inline(update, None)
assert fbuy_mock.call_count == 0
query = update.callback_query
assert query.edit_message_text.call_count == 1
- assert query.edit_message_text.call_args_list[-1][1]['text'] == "Force enter canceled."
+ assert query.edit_message_text.call_args_list[-1][1]["text"] == "Force enter canceled."
async def test_telegram_performance_handle(default_conf_usdt, update, ticker, fee, mocker) -> None:
-
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -1502,12 +1517,13 @@ async def test_telegram_performance_handle(default_conf_usdt, update, ticker, fe
await telegram._performance(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'Performance' in msg_mock.call_args_list[0][0][0]
- assert 'XRP/USDT\t2.842 USDT (10.00%) (1)' in msg_mock.call_args_list[0][0][0]
+ assert "Performance" in msg_mock.call_args_list[0][0][0]
+ assert "XRP/USDT\t2.842 USDT (10.00%) (1)" in msg_mock.call_args_list[0][0][0]
async def test_telegram_entry_tag_performance_handle(
- default_conf_usdt, update, ticker, fee, mocker) -> None:
+ default_conf_usdt, update, ticker, fee, mocker
+) -> None:
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -1521,16 +1537,17 @@ async def test_telegram_entry_tag_performance_handle(
context = MagicMock()
await telegram._enter_tag_performance(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Entry Tag Performance' in msg_mock.call_args_list[0][0][0]
- assert '`TEST1\t3.987 USDT (5.00%) (1)`' in msg_mock.call_args_list[0][0][0]
+ assert "Entry Tag Performance" in msg_mock.call_args_list[0][0][0]
+ assert "`TEST1\t3.987 USDT (5.00%) (1)`" in msg_mock.call_args_list[0][0][0]
- context.args = ['XRP/USDT']
+ context.args = ["XRP/USDT"]
await telegram._enter_tag_performance(update=update, context=context)
assert msg_mock.call_count == 2
msg_mock.reset_mock()
- mocker.patch('freqtrade.rpc.rpc.RPC._rpc_enter_tag_performance',
- side_effect=RPCException('Error'))
+ mocker.patch(
+ "freqtrade.rpc.rpc.RPC._rpc_enter_tag_performance", side_effect=RPCException("Error")
+ )
await telegram._enter_tag_performance(update=update, context=MagicMock())
assert msg_mock.call_count == 1
@@ -1538,7 +1555,8 @@ async def test_telegram_entry_tag_performance_handle(
async def test_telegram_exit_reason_performance_handle(
- default_conf_usdt, update, ticker, fee, mocker) -> None:
+ default_conf_usdt, update, ticker, fee, mocker
+) -> None:
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -1552,24 +1570,26 @@ async def test_telegram_exit_reason_performance_handle(
context = MagicMock()
await telegram._exit_reason_performance(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Exit Reason Performance' in msg_mock.call_args_list[0][0][0]
- assert '`roi\t2.842 USDT (10.00%) (1)`' in msg_mock.call_args_list[0][0][0]
- context.args = ['XRP/USDT']
+ assert "Exit Reason Performance" in msg_mock.call_args_list[0][0][0]
+ assert "`roi\t2.842 USDT (10.00%) (1)`" in msg_mock.call_args_list[0][0][0]
+ context.args = ["XRP/USDT"]
await telegram._exit_reason_performance(update=update, context=context)
assert msg_mock.call_count == 2
msg_mock.reset_mock()
- mocker.patch('freqtrade.rpc.rpc.RPC._rpc_exit_reason_performance',
- side_effect=RPCException('Error'))
+ mocker.patch(
+ "freqtrade.rpc.rpc.RPC._rpc_exit_reason_performance", side_effect=RPCException("Error")
+ )
await telegram._exit_reason_performance(update=update, context=MagicMock())
assert msg_mock.call_count == 1
assert "Error" in msg_mock.call_args_list[0][0][0]
-async def test_telegram_mix_tag_performance_handle(default_conf_usdt, update, ticker, fee,
- mocker) -> None:
+async def test_telegram_mix_tag_performance_handle(
+ default_conf_usdt, update, ticker, fee, mocker
+) -> None:
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -1584,17 +1604,17 @@ async def test_telegram_mix_tag_performance_handle(default_conf_usdt, update, ti
context = MagicMock()
await telegram._mix_tag_performance(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Mix Tag Performance' in msg_mock.call_args_list[0][0][0]
- assert ('`TEST3 roi\t2.842 USDT (10.00%) (1)`'
- in msg_mock.call_args_list[0][0][0])
+ assert "Mix Tag Performance" in msg_mock.call_args_list[0][0][0]
+ assert "`TEST3 roi\t2.842 USDT (10.00%) (1)`" in msg_mock.call_args_list[0][0][0]
- context.args = ['XRP/USDT']
+ context.args = ["XRP/USDT"]
await telegram._mix_tag_performance(update=update, context=context)
assert msg_mock.call_count == 2
msg_mock.reset_mock()
- mocker.patch('freqtrade.rpc.rpc.RPC._rpc_mix_tag_performance',
- side_effect=RPCException('Error'))
+ mocker.patch(
+ "freqtrade.rpc.rpc.RPC._rpc_mix_tag_performance", side_effect=RPCException("Error")
+ )
await telegram._mix_tag_performance(update=update, context=MagicMock())
assert msg_mock.call_count == 1
@@ -1613,7 +1633,7 @@ async def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
freqtradebot.state = State.STOPPED
await telegram._count(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'not running' in msg_mock.call_args_list[0][0][0]
+ assert "not running" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
freqtradebot.state = State.RUNNING
@@ -1622,11 +1642,10 @@ async def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
msg_mock.reset_mock()
await telegram._count(update=update, context=MagicMock())
- msg = ('
current max total stake\n--------- ----- -------------\n'
- ' 1 {} {}').format(
- default_conf['max_open_trades'],
- default_conf['stake_amount']
- )
+ msg = (
+ " current max total stake\n--------- ----- -------------\n"
+ " 1 {} {}"
+ ).format(default_conf["max_open_trades"], default_conf["stake_amount"])
assert msg in msg_mock.call_args_list[0][0][0]
@@ -1640,107 +1659,118 @@ async def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -
patch_get_signal(freqtradebot)
await telegram._locks(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'No active locks.' in msg_mock.call_args_list[0][0][0]
+ assert "No active locks." in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
- PairLocks.lock_pair('ETH/BTC', dt_now() + timedelta(minutes=4), 'randreason')
- PairLocks.lock_pair('XRP/BTC', dt_now() + timedelta(minutes=20), 'deadbeef')
+ PairLocks.lock_pair("ETH/BTC", dt_now() + timedelta(minutes=4), "randreason")
+ PairLocks.lock_pair("XRP/BTC", dt_now() + timedelta(minutes=20), "deadbeef")
await telegram._locks(update=update, context=MagicMock())
- assert 'Pair' in msg_mock.call_args_list[0][0][0]
- assert 'Until' in msg_mock.call_args_list[0][0][0]
- assert 'Reason\n' in msg_mock.call_args_list[0][0][0]
- assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0]
- assert 'XRP/BTC' in msg_mock.call_args_list[0][0][0]
- assert 'deadbeef' in msg_mock.call_args_list[0][0][0]
- assert 'randreason' in msg_mock.call_args_list[0][0][0]
+ assert "Pair" in msg_mock.call_args_list[0][0][0]
+ assert "Until" in msg_mock.call_args_list[0][0][0]
+ assert "Reason\n" in msg_mock.call_args_list[0][0][0]
+ assert "ETH/BTC" in msg_mock.call_args_list[0][0][0]
+ assert "XRP/BTC" in msg_mock.call_args_list[0][0][0]
+ assert "deadbeef" in msg_mock.call_args_list[0][0][0]
+ assert "randreason" in msg_mock.call_args_list[0][0][0]
context = MagicMock()
- context.args = ['XRP/BTC']
+ context.args = ["XRP/BTC"]
msg_mock.reset_mock()
await telegram._delete_locks(update=update, context=context)
- assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0]
- assert 'randreason' in msg_mock.call_args_list[0][0][0]
- assert 'XRP/BTC' not in msg_mock.call_args_list[0][0][0]
- assert 'deadbeef' not in msg_mock.call_args_list[0][0][0]
+ assert "ETH/BTC" in msg_mock.call_args_list[0][0][0]
+ assert "randreason" in msg_mock.call_args_list[0][0][0]
+ assert "XRP/BTC" not in msg_mock.call_args_list[0][0][0]
+ assert "deadbeef" not in msg_mock.call_args_list[0][0][0]
async def test_whitelist_static(default_conf, update, mocker) -> None:
-
telegram, _freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
await telegram._whitelist(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert ("Using whitelist `['StaticPairList']` with 4 pairs\n"
- "`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`" in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Using whitelist `['StaticPairList']` with 4 pairs\n"
+ "`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`" in msg_mock.call_args_list[0][0][0]
+ )
context = MagicMock()
- context.args = ['sorted']
+ context.args = ["sorted"]
msg_mock.reset_mock()
await telegram._whitelist(update=update, context=context)
- assert ("Using whitelist `['StaticPairList']` with 4 pairs\n"
- "`ETH/BTC, LTC/BTC, NEO/BTC, XRP/BTC`" in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Using whitelist `['StaticPairList']` with 4 pairs\n"
+ "`ETH/BTC, LTC/BTC, NEO/BTC, XRP/BTC`" in msg_mock.call_args_list[0][0][0]
+ )
context = MagicMock()
- context.args = ['baseonly']
+ context.args = ["baseonly"]
msg_mock.reset_mock()
await telegram._whitelist(update=update, context=context)
- assert ("Using whitelist `['StaticPairList']` with 4 pairs\n"
- "`ETH, LTC, XRP, NEO`" in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Using whitelist `['StaticPairList']` with 4 pairs\n"
+ "`ETH, LTC, XRP, NEO`" in msg_mock.call_args_list[0][0][0]
+ )
context = MagicMock()
- context.args = ['baseonly', 'sorted']
+ context.args = ["baseonly", "sorted"]
msg_mock.reset_mock()
await telegram._whitelist(update=update, context=context)
- assert ("Using whitelist `['StaticPairList']` with 4 pairs\n"
- "`ETH, LTC, NEO, XRP`" in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Using whitelist `['StaticPairList']` with 4 pairs\n"
+ "`ETH, LTC, NEO, XRP`" in msg_mock.call_args_list[0][0][0]
+ )
async def test_whitelist_dynamic(default_conf, update, mocker) -> None:
- mocker.patch(f'{EXMS}.exchange_has', return_value=True)
- default_conf['pairlists'] = [{'method': 'VolumePairList',
- 'number_assets': 4
- }]
+ mocker.patch(f"{EXMS}.exchange_has", return_value=True)
+ default_conf["pairlists"] = [{"method": "VolumePairList", "number_assets": 4}]
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
await telegram._whitelist(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert ("Using whitelist `['VolumePairList']` with 4 pairs\n"
- "`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`" in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Using whitelist `['VolumePairList']` with 4 pairs\n"
+ "`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`" in msg_mock.call_args_list[0][0][0]
+ )
context = MagicMock()
- context.args = ['sorted']
+ context.args = ["sorted"]
msg_mock.reset_mock()
await telegram._whitelist(update=update, context=context)
- assert ("Using whitelist `['VolumePairList']` with 4 pairs\n"
- "`ETH/BTC, LTC/BTC, NEO/BTC, XRP/BTC`" in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Using whitelist `['VolumePairList']` with 4 pairs\n"
+ "`ETH/BTC, LTC/BTC, NEO/BTC, XRP/BTC`" in msg_mock.call_args_list[0][0][0]
+ )
context = MagicMock()
- context.args = ['baseonly']
+ context.args = ["baseonly"]
msg_mock.reset_mock()
await telegram._whitelist(update=update, context=context)
- assert ("Using whitelist `['VolumePairList']` with 4 pairs\n"
- "`ETH, LTC, XRP, NEO`" in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Using whitelist `['VolumePairList']` with 4 pairs\n"
+ "`ETH, LTC, XRP, NEO`" in msg_mock.call_args_list[0][0][0]
+ )
context = MagicMock()
- context.args = ['baseonly', 'sorted']
+ context.args = ["baseonly", "sorted"]
msg_mock.reset_mock()
await telegram._whitelist(update=update, context=context)
- assert ("Using whitelist `['VolumePairList']` with 4 pairs\n"
- "`ETH, LTC, NEO, XRP`" in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Using whitelist `['VolumePairList']` with 4 pairs\n"
+ "`ETH, LTC, NEO, XRP`" in msg_mock.call_args_list[0][0][0]
+ )
async def test_blacklist_static(default_conf, update, mocker) -> None:
-
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
await telegram._blacklist(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert ("Blacklist contains 2 pairs\n`DOGE/BTC, HOT/BTC`"
- in msg_mock.call_args_list[0][0][0])
+ assert "Blacklist contains 2 pairs\n`DOGE/BTC, HOT/BTC`" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
@@ -1749,8 +1779,10 @@ async def test_blacklist_static(default_conf, update, mocker) -> None:
context.args = ["ETH/BTC"]
await telegram._blacklist(update=update, context=context)
assert msg_mock.call_count == 1
- assert ("Blacklist contains 3 pairs\n`DOGE/BTC, HOT/BTC, ETH/BTC`"
- in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Blacklist contains 3 pairs\n`DOGE/BTC, HOT/BTC, ETH/BTC`"
+ in msg_mock.call_args_list[0][0][0]
+ )
assert freqtradebot.pairlists.blacklist == ["DOGE/BTC", "HOT/BTC", "ETH/BTC"]
msg_mock.reset_mock()
@@ -1759,21 +1791,24 @@ async def test_blacklist_static(default_conf, update, mocker) -> None:
await telegram._blacklist(update=update, context=context)
assert msg_mock.call_count == 1
- assert ("Blacklist contains 4 pairs\n`DOGE/BTC, HOT/BTC, ETH/BTC, XRP/.*`"
- in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Blacklist contains 4 pairs\n`DOGE/BTC, HOT/BTC, ETH/BTC, XRP/.*`"
+ in msg_mock.call_args_list[0][0][0]
+ )
assert freqtradebot.pairlists.blacklist == ["DOGE/BTC", "HOT/BTC", "ETH/BTC", "XRP/.*"]
msg_mock.reset_mock()
context.args = ["DOGE/BTC"]
await telegram._blacklist_delete(update=update, context=context)
assert msg_mock.call_count == 1
- assert ("Blacklist contains 3 pairs\n`HOT/BTC, ETH/BTC, XRP/.*`"
- in msg_mock.call_args_list[0][0][0])
+ assert (
+ "Blacklist contains 3 pairs\n`HOT/BTC, ETH/BTC, XRP/.*`" in msg_mock.call_args_list[0][0][0]
+ )
async def test_telegram_logs(default_conf, update, mocker) -> None:
mocker.patch.multiple(
- 'freqtrade.rpc.telegram.Telegram',
+ "freqtrade.rpc.telegram.Telegram",
_init=MagicMock(),
)
setup_logging(default_conf)
@@ -1793,7 +1828,7 @@ async def test_telegram_logs(default_conf, update, mocker) -> None:
msg_mock.reset_mock()
# Test with changed MaxMessageLength
- mocker.patch('freqtrade.rpc.telegram.MAX_MESSAGE_LENGTH', 200)
+ mocker.patch("freqtrade.rpc.telegram.MAX_MESSAGE_LENGTH", 200)
context = MagicMock()
context.args = []
await telegram._logs(update=update, context=context)
@@ -1803,7 +1838,6 @@ async def test_telegram_logs(default_conf, update, mocker) -> None:
async def test_edge_disabled(default_conf, update, mocker) -> None:
-
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
await telegram._edge(update=update, context=MagicMock())
@@ -1812,34 +1846,36 @@ async def test_edge_disabled(default_conf, update, mocker) -> None:
async def test_edge_enabled(edge_conf, update, mocker) -> None:
- mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
- return_value={
- 'E/F': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
- }
- ))
+ mocker.patch(
+ "freqtrade.edge.Edge._cached_pairs",
+ mocker.PropertyMock(
+ return_value={
+ "E/F": PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
+ }
+ ),
+ )
telegram, _, msg_mock = get_telegram_testobject(mocker, edge_conf)
await telegram._edge(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'Edge only validated following pairs:\n' in msg_mock.call_args_list[0][0][0]
- assert 'Pair Winrate Expectancy Stoploss' in msg_mock.call_args_list[0][0][0]
+ assert "Edge only validated following pairs:\n" in msg_mock.call_args_list[0][0][0]
+ assert "Pair Winrate Expectancy Stoploss" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
- mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
- return_value={}))
+ mocker.patch("freqtrade.edge.Edge._cached_pairs", mocker.PropertyMock(return_value={}))
await telegram._edge(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert 'Edge only validated following pairs:' in msg_mock.call_args_list[0][0][0]
- assert 'Winrate' not in msg_mock.call_args_list[0][0][0]
+ assert "Edge only validated following pairs:" in msg_mock.call_args_list[0][0][0]
+ assert "Winrate" not in msg_mock.call_args_list[0][0][0]
-@pytest.mark.parametrize('is_short,regex_pattern',
- [(True, r"now[ ]*XRP\/BTC \(#3\) -1.00% \("),
- (False, r"now[ ]*XRP\/BTC \(#3\) 1.00% \(")])
+@pytest.mark.parametrize(
+ "is_short,regex_pattern",
+ [(True, r"now[ ]*XRP\/BTC \(#3\) -1.00% \("), (False, r"now[ ]*XRP\/BTC \(#3\) 1.00% \(")],
+)
async def test_telegram_trades(mocker, update, default_conf, fee, is_short, regex_pattern):
-
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
context = MagicMock()
@@ -1850,7 +1886,7 @@ async def test_telegram_trades(mocker, update, default_conf, fee, is_short, rege
assert "" not in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
- context.args = ['hello']
+ context.args = ["hello"]
await telegram._trades(update=update, context=context)
assert "0 recent trades:" in msg_mock.call_args_list[0][0][0]
assert "" not in msg_mock.call_args_list[0][0][0]
@@ -1869,9 +1905,8 @@ async def test_telegram_trades(mocker, update, default_conf, fee, is_short, rege
assert bool(re.search(regex_pattern, msg_mock.call_args_list[0][0][0]))
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
async def test_telegram_delete_trade(mocker, update, default_conf, fee, is_short):
-
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
context = MagicMock()
context.args = []
@@ -1890,9 +1925,8 @@ async def test_telegram_delete_trade(mocker, update, default_conf, fee, is_short
assert "Please make sure to take care of this asset" in msg_mock.call_args_list[0][0][0]
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
async def test_telegram_reload_trade_from_exchange(mocker, update, default_conf, fee, is_short):
-
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
context = MagicMock()
context.args = []
@@ -1909,9 +1943,8 @@ async def test_telegram_reload_trade_from_exchange(mocker, update, default_conf,
assert "Status: `Reloaded from orders from exchange`" in msg_mock.call_args_list[0][0][0]
-@pytest.mark.parametrize('is_short', [True, False])
+@pytest.mark.parametrize("is_short", [True, False])
async def test_telegram_delete_open_order(mocker, update, default_conf, fee, is_short, ticker):
-
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -1934,7 +1967,7 @@ async def test_telegram_delete_open_order(mocker, update, default_conf, fee, is_
msg_mock.reset_mock()
trade = Trade.get_trades([Trade.id == 6]).first()
- mocker.patch(f'{EXMS}.fetch_order', return_value=trade.orders[-1].to_ccxt_object())
+ mocker.patch(f"{EXMS}.fetch_order", return_value=trade.orders[-1].to_ccxt_object())
context = MagicMock()
context.args = [6]
await telegram._cancel_open_order(update=update, context=context)
@@ -1947,611 +1980,655 @@ async def test_help_handle(default_conf, update, mocker) -> None:
await telegram._help(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert '*/help:* `This help message`' in msg_mock.call_args_list[0][0][0]
+ assert "*/help:* `This help message`" in msg_mock.call_args_list[0][0][0]
async def test_version_handle(default_conf, update, mocker) -> None:
-
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
await telegram._version(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert f'*Version:* `{__version__}`' in msg_mock.call_args_list[0][0][0]
+ assert f"*Version:* `{__version__}`" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
- freqtradebot.strategy.version = lambda: '1.1.1'
+ freqtradebot.strategy.version = lambda: "1.1.1"
await telegram._version(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert f'*Version:* `{__version__}`' in msg_mock.call_args_list[0][0][0]
- assert '*Strategy version: * `1.1.1`' in msg_mock.call_args_list[0][0][0]
+ assert f"*Version:* `{__version__}`" in msg_mock.call_args_list[0][0][0]
+ assert "*Strategy version: * `1.1.1`" in msg_mock.call_args_list[0][0][0]
async def test_show_config_handle(default_conf, update, mocker) -> None:
-
- default_conf['runmode'] = RunMode.DRY_RUN
+ default_conf["runmode"] = RunMode.DRY_RUN
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
await telegram._show_config(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0]
- assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0]
- assert f'*Strategy:* `{CURRENT_TEST_STRATEGY}`' in msg_mock.call_args_list[0][0][0]
- assert '*Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]
+ assert "*Mode:* `{}`".format("Dry-run") in msg_mock.call_args_list[0][0][0]
+ assert "*Exchange:* `binance`" in msg_mock.call_args_list[0][0][0]
+ assert f"*Strategy:* `{CURRENT_TEST_STRATEGY}`" in msg_mock.call_args_list[0][0][0]
+ assert "*Stoploss:* `-0.1`" in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
- freqtradebot.config['trailing_stop'] = True
+ freqtradebot.config["trailing_stop"] = True
await telegram._show_config(update=update, context=MagicMock())
assert msg_mock.call_count == 1
- assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0]
- assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0]
- assert f'*Strategy:* `{CURRENT_TEST_STRATEGY}`' in msg_mock.call_args_list[0][0][0]
- assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]
+ assert "*Mode:* `{}`".format("Dry-run") in msg_mock.call_args_list[0][0][0]
+ assert "*Exchange:* `binance`" in msg_mock.call_args_list[0][0][0]
+ assert f"*Strategy:* `{CURRENT_TEST_STRATEGY}`" in msg_mock.call_args_list[0][0][0]
+ assert "*Initial Stoploss:* `-0.1`" in msg_mock.call_args_list[0][0][0]
-@pytest.mark.parametrize('message_type,enter,enter_signal,leverage', [
- (RPCMessageType.ENTRY, 'Long', 'long_signal_01', None),
- (RPCMessageType.ENTRY, 'Long', 'long_signal_01', 1.0),
- (RPCMessageType.ENTRY, 'Long', 'long_signal_01', 5.0),
- (RPCMessageType.ENTRY, 'Short', 'short_signal_01', 2.0)])
-def test_send_msg_enter_notification(default_conf, mocker, caplog, message_type,
- enter, enter_signal, leverage) -> None:
- default_conf['telegram']['notification_settings']['show_candle'] = 'ohlc'
- df = DataFrame({
- 'open': [1.1],
- 'high': [2.2],
- 'low': [1.0],
- 'close': [1.5],
- })
- mocker.patch('freqtrade.data.dataprovider.DataProvider.get_analyzed_dataframe',
- return_value=(df, 1))
+@pytest.mark.parametrize(
+ "message_type,enter,enter_signal,leverage",
+ [
+ (RPCMessageType.ENTRY, "Long", "long_signal_01", None),
+ (RPCMessageType.ENTRY, "Long", "long_signal_01", 1.0),
+ (RPCMessageType.ENTRY, "Long", "long_signal_01", 5.0),
+ (RPCMessageType.ENTRY, "Short", "short_signal_01", 2.0),
+ ],
+)
+def test_send_msg_enter_notification(
+ default_conf, mocker, caplog, message_type, enter, enter_signal, leverage
+) -> None:
+ default_conf["telegram"]["notification_settings"]["show_candle"] = "ohlc"
+ df = DataFrame(
+ {
+ "open": [1.1],
+ "high": [2.2],
+ "low": [1.0],
+ "close": [1.5],
+ }
+ )
+ mocker.patch(
+ "freqtrade.data.dataprovider.DataProvider.get_analyzed_dataframe", return_value=(df, 1)
+ )
msg = {
- 'type': message_type,
- 'trade_id': 1,
- 'enter_tag': enter_signal,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': leverage,
- 'open_rate': 1.099e-05,
- 'order_type': 'limit',
- 'direction': enter,
- 'stake_amount': 0.01465333,
- 'stake_amount_fiat': 0.0,
- 'stake_currency': 'BTC',
- 'quote_currency': 'BTC',
- 'base_currency': 'ETH',
- 'fiat_currency': 'USD',
- 'sub_trade': False,
- 'current_rate': 1.099e-05,
- 'amount': 1333.3333333333335,
- 'analyzed_candle': {'open': 1.1, 'high': 2.2, 'low': 1.0, 'close': 1.5},
- 'open_date': dt_now() + timedelta(hours=-1)
+ "type": message_type,
+ "trade_id": 1,
+ "enter_tag": enter_signal,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": leverage,
+ "open_rate": 1.099e-05,
+ "order_type": "limit",
+ "direction": enter,
+ "stake_amount": 0.01465333,
+ "stake_amount_fiat": 0.0,
+ "stake_currency": "BTC",
+ "quote_currency": "BTC",
+ "base_currency": "ETH",
+ "fiat_currency": "USD",
+ "sub_trade": False,
+ "current_rate": 1.099e-05,
+ "amount": 1333.3333333333335,
+ "analyzed_candle": {"open": 1.1, "high": 2.2, "low": 1.0, "close": 1.5},
+ "open_date": dt_now() + timedelta(hours=-1),
}
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram.send_msg(msg)
- leverage_text = f' ({leverage:.3g}x)' if leverage and leverage != 1.0 else ''
+ leverage_text = f" ({leverage:.3g}x)" if leverage and leverage != 1.0 else ""
assert msg_mock.call_args[0][0] == (
- f'\N{LARGE BLUE CIRCLE} *Binance (dry):* New Trade (#1)\n'
- f'*Pair:* `ETH/BTC`\n'
- '*Candle OHLC*: `1.1, 2.2, 1.0, 1.5`\n'
- f'*Enter Tag:* `{enter_signal}`\n'
- '*Amount:* `1333.33333333`\n'
- f'*Direction:* `{enter}'
- f'{leverage_text}`\n'
- '*Open Rate:* `0.00001099 BTC`\n'
- '*Current Rate:* `0.00001099 BTC`\n'
- '*Total:* `0.01465333 BTC / 180.895 USD`'
+ f"\N{LARGE BLUE CIRCLE} *Binance (dry):* New Trade (#1)\n"
+ f"*Pair:* `ETH/BTC`\n"
+ "*Candle OHLC*: `1.1, 2.2, 1.0, 1.5`\n"
+ f"*Enter Tag:* `{enter_signal}`\n"
+ "*Amount:* `1333.33333333`\n"
+ f"*Direction:* `{enter}"
+ f"{leverage_text}`\n"
+ "*Open Rate:* `0.00001099 BTC`\n"
+ "*Current Rate:* `0.00001099 BTC`\n"
+ "*Total:* `0.01465333 BTC / 180.895 USD`"
)
- freqtradebot.config['telegram']['notification_settings'] = {'entry': 'off'}
+ freqtradebot.config["telegram"]["notification_settings"] = {"entry": "off"}
caplog.clear()
msg_mock.reset_mock()
telegram.send_msg(msg)
assert msg_mock.call_count == 0
assert log_has("Notification 'entry' not sent.", caplog)
- freqtradebot.config['telegram']['notification_settings'] = {'entry': 'silent'}
+ freqtradebot.config["telegram"]["notification_settings"] = {"entry": "silent"}
caplog.clear()
msg_mock.reset_mock()
telegram.send_msg(msg)
assert msg_mock.call_count == 1
- assert msg_mock.call_args_list[0][1]['disable_notification'] is True
+ assert msg_mock.call_args_list[0][1]["disable_notification"] is True
-@pytest.mark.parametrize('message_type,enter_signal', [
- (RPCMessageType.ENTRY_CANCEL, 'long_signal_01'),
- (RPCMessageType.ENTRY_CANCEL, 'short_signal_01')])
+@pytest.mark.parametrize(
+ "message_type,enter_signal",
+ [
+ (RPCMessageType.ENTRY_CANCEL, "long_signal_01"),
+ (RPCMessageType.ENTRY_CANCEL, "short_signal_01"),
+ ],
+)
def test_send_msg_enter_cancel_notification(
- default_conf, mocker, message_type, enter_signal) -> None:
-
+ default_conf, mocker, message_type, enter_signal
+) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': message_type,
- 'enter_tag': enter_signal,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'reason': CANCEL_REASON['TIMEOUT']
- })
- assert (msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Binance (dry):* '
- 'Cancelling enter Order for ETH/BTC (#1). '
- 'Reason: cancelled due to timeout.')
+ telegram.send_msg(
+ {
+ "type": message_type,
+ "enter_tag": enter_signal,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "reason": CANCEL_REASON["TIMEOUT"],
+ }
+ )
+ assert (
+ msg_mock.call_args[0][0] == "\N{WARNING SIGN} *Binance (dry):* "
+ "Cancelling enter Order for ETH/BTC (#1). "
+ "Reason: cancelled due to timeout."
+ )
def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> None:
-
- default_conf['telegram']['notification_settings']['protection_trigger'] = 'on'
+ default_conf["telegram"]["notification_settings"]["protection_trigger"] = "on"
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
time_machine.move_to("2021-09-01 05:00:00 +00:00")
- lock = PairLocks.lock_pair('ETH/BTC', dt_now() + timedelta(minutes=6), 'randreason')
+ lock = PairLocks.lock_pair("ETH/BTC", dt_now() + timedelta(minutes=6), "randreason")
msg = {
- 'type': RPCMessageType.PROTECTION_TRIGGER,
+ "type": RPCMessageType.PROTECTION_TRIGGER,
}
msg.update(lock.to_json())
telegram.send_msg(msg)
- assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. "
- "`ETH/BTC` will be locked until `2021-09-01 05:10:00`.")
+ assert (
+ msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. "
+ "`ETH/BTC` will be locked until `2021-09-01 05:10:00`."
+ )
msg_mock.reset_mock()
# Test global protection
msg = {
- 'type': RPCMessageType.PROTECTION_TRIGGER_GLOBAL,
+ "type": RPCMessageType.PROTECTION_TRIGGER_GLOBAL,
}
- lock = PairLocks.lock_pair('*', dt_now() + timedelta(minutes=100), 'randreason')
+ lock = PairLocks.lock_pair("*", dt_now() + timedelta(minutes=100), "randreason")
msg.update(lock.to_json())
telegram.send_msg(msg)
- assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. "
- "*All pairs* will be locked until `2021-09-01 06:45:00`.")
+ assert (
+ msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. "
+ "*All pairs* will be locked until `2021-09-01 06:45:00`."
+ )
-@pytest.mark.parametrize('message_type,entered,enter_signal,leverage', [
- (RPCMessageType.ENTRY_FILL, 'Long', 'long_signal_01', 1.0),
- (RPCMessageType.ENTRY_FILL, 'Long', 'long_signal_02', 2.0),
- (RPCMessageType.ENTRY_FILL, 'Short', 'short_signal_01', 2.0),
-])
-def test_send_msg_entry_fill_notification(default_conf, mocker, message_type, entered,
- enter_signal, leverage) -> None:
-
- default_conf['telegram']['notification_settings']['entry_fill'] = 'on'
+@pytest.mark.parametrize(
+ "message_type,entered,enter_signal,leverage",
+ [
+ (RPCMessageType.ENTRY_FILL, "Long", "long_signal_01", 1.0),
+ (RPCMessageType.ENTRY_FILL, "Long", "long_signal_02", 2.0),
+ (RPCMessageType.ENTRY_FILL, "Short", "short_signal_01", 2.0),
+ ],
+)
+def test_send_msg_entry_fill_notification(
+ default_conf, mocker, message_type, entered, enter_signal, leverage
+) -> None:
+ default_conf["telegram"]["notification_settings"]["entry_fill"] = "on"
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': message_type,
- 'trade_id': 1,
- 'enter_tag': enter_signal,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': leverage,
- 'stake_amount': 0.01465333,
- 'direction': entered,
- 'sub_trade': False,
- 'stake_currency': 'BTC',
- 'quote_currency': 'BTC',
- 'base_currency': 'ETH',
- 'fiat_currency': 'USD',
- 'open_rate': 1.099e-05,
- 'amount': 1333.3333333333335,
- 'open_date': dt_now() - timedelta(hours=1)
- })
- leverage_text = f' ({leverage:.3g}x)' if leverage != 1.0 else ''
+ telegram.send_msg(
+ {
+ "type": message_type,
+ "trade_id": 1,
+ "enter_tag": enter_signal,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": leverage,
+ "stake_amount": 0.01465333,
+ "direction": entered,
+ "sub_trade": False,
+ "stake_currency": "BTC",
+ "quote_currency": "BTC",
+ "base_currency": "ETH",
+ "fiat_currency": "USD",
+ "open_rate": 1.099e-05,
+ "amount": 1333.3333333333335,
+ "open_date": dt_now() - timedelta(hours=1),
+ }
+ )
+ leverage_text = f" ({leverage:.3g}x)" if leverage != 1.0 else ""
assert msg_mock.call_args[0][0] == (
- f'\N{CHECK MARK} *Binance (dry):* New Trade filled (#1)\n'
- f'*Pair:* `ETH/BTC`\n'
- f'*Enter Tag:* `{enter_signal}`\n'
- '*Amount:* `1333.33333333`\n'
- f'*Direction:* `{entered}'
+ f"\N{CHECK MARK} *Binance (dry):* New Trade filled (#1)\n"
+ f"*Pair:* `ETH/BTC`\n"
+ f"*Enter Tag:* `{enter_signal}`\n"
+ "*Amount:* `1333.33333333`\n"
+ f"*Direction:* `{entered}"
f"{leverage_text}`\n"
- '*Open Rate:* `0.00001099 BTC`\n'
- '*Total:* `0.01465333 BTC / 180.895 USD`'
+ "*Open Rate:* `0.00001099 BTC`\n"
+ "*Total:* `0.01465333 BTC / 180.895 USD`"
)
msg_mock.reset_mock()
- telegram.send_msg({
- 'type': message_type,
- 'trade_id': 1,
- 'enter_tag': enter_signal,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': leverage,
- 'stake_amount': 0.01465333,
- 'sub_trade': True,
- 'direction': entered,
- 'stake_currency': 'BTC',
- 'quote_currency': 'BTC',
- 'base_currency': 'ETH',
- 'fiat_currency': 'USD',
- 'open_rate': 1.099e-05,
- 'amount': 1333.3333333333335,
- 'open_date': dt_now() - timedelta(hours=1)
- })
+ telegram.send_msg(
+ {
+ "type": message_type,
+ "trade_id": 1,
+ "enter_tag": enter_signal,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": leverage,
+ "stake_amount": 0.01465333,
+ "sub_trade": True,
+ "direction": entered,
+ "stake_currency": "BTC",
+ "quote_currency": "BTC",
+ "base_currency": "ETH",
+ "fiat_currency": "USD",
+ "open_rate": 1.099e-05,
+ "amount": 1333.3333333333335,
+ "open_date": dt_now() - timedelta(hours=1),
+ }
+ )
assert msg_mock.call_args[0][0] == (
- f'\N{CHECK MARK} *Binance (dry):* Position increase filled (#1)\n'
- f'*Pair:* `ETH/BTC`\n'
- f'*Enter Tag:* `{enter_signal}`\n'
- '*Amount:* `1333.33333333`\n'
- f'*Direction:* `{entered}'
+ f"\N{CHECK MARK} *Binance (dry):* Position increase filled (#1)\n"
+ f"*Pair:* `ETH/BTC`\n"
+ f"*Enter Tag:* `{enter_signal}`\n"
+ "*Amount:* `1333.33333333`\n"
+ f"*Direction:* `{entered}"
f"{leverage_text}`\n"
- '*Open Rate:* `0.00001099 BTC`\n'
- '*New Total:* `0.01465333 BTC / 180.895 USD`'
+ "*Open Rate:* `0.00001099 BTC`\n"
+ "*New Total:* `0.01465333 BTC / 180.895 USD`"
)
def test_send_msg_exit_notification(default_conf, mocker) -> None:
-
with time_machine.travel("2022-09-01 05:00:00 +00:00", tick=False):
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
old_convamount = telegram._rpc._fiat_converter.convert_amount
telegram._rpc._fiat_converter.convert_amount = lambda a, b, c: -24.812
- telegram.send_msg({
- 'type': RPCMessageType.EXIT,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'KEY/ETH',
- 'leverage': 1.0,
- 'direction': 'Long',
- 'gain': 'loss',
- 'order_rate': 3.201e-04,
- 'amount': 1333.3333333333335,
- 'order_type': 'market',
- 'open_rate': 7.5e-04,
- 'current_rate': 3.201e-04,
- 'profit_amount': -0.05746268,
- 'profit_ratio': -0.57405275,
- 'stake_currency': 'ETH',
- 'quote_currency': 'ETH',
- 'base_currency': 'KEY',
- 'fiat_currency': 'USD',
- 'enter_tag': 'buy_signal1',
- 'exit_reason': ExitType.STOP_LOSS.value,
- 'open_date': dt_now() - timedelta(hours=1),
- 'close_date': dt_now(),
- })
+ telegram.send_msg(
+ {
+ "type": RPCMessageType.EXIT,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "KEY/ETH",
+ "leverage": 1.0,
+ "direction": "Long",
+ "gain": "loss",
+ "order_rate": 3.201e-04,
+ "amount": 1333.3333333333335,
+ "order_type": "market",
+ "open_rate": 7.5e-04,
+ "current_rate": 3.201e-04,
+ "profit_amount": -0.05746268,
+ "profit_ratio": -0.57405275,
+ "stake_currency": "ETH",
+ "quote_currency": "ETH",
+ "base_currency": "KEY",
+ "fiat_currency": "USD",
+ "enter_tag": "buy_signal1",
+ "exit_reason": ExitType.STOP_LOSS.value,
+ "open_date": dt_now() - timedelta(hours=1),
+ "close_date": dt_now(),
+ }
+ )
assert msg_mock.call_args[0][0] == (
- '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n'
- '*Unrealized Profit:* `-57.41% (loss: -0.05746 ETH / -24.812 USD)`\n'
- '*Enter Tag:* `buy_signal1`\n'
- '*Exit Reason:* `stop_loss`\n'
- '*Direction:* `Long`\n'
- '*Amount:* `1333.33333333`\n'
- '*Open Rate:* `0.00075 ETH`\n'
- '*Current Rate:* `0.00032 ETH`\n'
- '*Exit Rate:* `0.00032 ETH`\n'
- '*Duration:* `1:00:00 (60.0 min)`'
+ "\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n"
+ "*Unrealized Profit:* `-57.41% (loss: -0.05746 ETH / -24.812 USD)`\n"
+ "*Enter Tag:* `buy_signal1`\n"
+ "*Exit Reason:* `stop_loss`\n"
+ "*Direction:* `Long`\n"
+ "*Amount:* `1333.33333333`\n"
+ "*Open Rate:* `0.00075 ETH`\n"
+ "*Current Rate:* `0.00032 ETH`\n"
+ "*Exit Rate:* `0.00032 ETH`\n"
+ "*Duration:* `1:00:00 (60.0 min)`"
)
msg_mock.reset_mock()
- telegram.send_msg({
- 'type': RPCMessageType.EXIT,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'KEY/ETH',
- 'direction': 'Long',
- 'gain': 'loss',
- 'order_rate': 3.201e-04,
- 'amount': 1333.3333333333335,
- 'order_type': 'market',
- 'open_rate': 7.5e-04,
- 'current_rate': 3.201e-04,
- 'cumulative_profit': -0.15746268,
- 'profit_amount': -0.05746268,
- 'profit_ratio': -0.57405275,
- 'stake_currency': 'ETH',
- 'quote_currency': 'ETH',
- 'base_currency': 'KEY',
- 'fiat_currency': 'USD',
- 'enter_tag': 'buy_signal1',
- 'exit_reason': ExitType.STOP_LOSS.value,
- 'open_date': dt_now() - timedelta(days=1, hours=2, minutes=30),
- 'close_date': dt_now(),
- 'stake_amount': 0.01,
- 'sub_trade': True,
- })
+ telegram.send_msg(
+ {
+ "type": RPCMessageType.EXIT,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "KEY/ETH",
+ "direction": "Long",
+ "gain": "loss",
+ "order_rate": 3.201e-04,
+ "amount": 1333.3333333333335,
+ "order_type": "market",
+ "open_rate": 7.5e-04,
+ "current_rate": 3.201e-04,
+ "cumulative_profit": -0.15746268,
+ "profit_amount": -0.05746268,
+ "profit_ratio": -0.57405275,
+ "stake_currency": "ETH",
+ "quote_currency": "ETH",
+ "base_currency": "KEY",
+ "fiat_currency": "USD",
+ "enter_tag": "buy_signal1",
+ "exit_reason": ExitType.STOP_LOSS.value,
+ "open_date": dt_now() - timedelta(days=1, hours=2, minutes=30),
+ "close_date": dt_now(),
+ "stake_amount": 0.01,
+ "sub_trade": True,
+ }
+ )
assert msg_mock.call_args[0][0] == (
- '\N{WARNING SIGN} *Binance (dry):* Partially exiting KEY/ETH (#1)\n'
- '*Unrealized Sub Profit:* `-57.41% (loss: -0.05746 ETH / -24.812 USD)`\n'
- '*Cumulative Profit:* `-0.15746 ETH / -24.812 USD`\n'
- '*Enter Tag:* `buy_signal1`\n'
- '*Exit Reason:* `stop_loss`\n'
- '*Direction:* `Long`\n'
- '*Amount:* `1333.33333333`\n'
- '*Open Rate:* `0.00075 ETH`\n'
- '*Current Rate:* `0.00032 ETH`\n'
- '*Exit Rate:* `0.00032 ETH`\n'
- '*Remaining:* `0.01 ETH / -24.812 USD`'
- )
+ "\N{WARNING SIGN} *Binance (dry):* Partially exiting KEY/ETH (#1)\n"
+ "*Unrealized Sub Profit:* `-57.41% (loss: -0.05746 ETH / -24.812 USD)`\n"
+ "*Cumulative Profit:* `-0.15746 ETH / -24.812 USD`\n"
+ "*Enter Tag:* `buy_signal1`\n"
+ "*Exit Reason:* `stop_loss`\n"
+ "*Direction:* `Long`\n"
+ "*Amount:* `1333.33333333`\n"
+ "*Open Rate:* `0.00075 ETH`\n"
+ "*Current Rate:* `0.00032 ETH`\n"
+ "*Exit Rate:* `0.00032 ETH`\n"
+ "*Remaining:* `0.01 ETH / -24.812 USD`"
+ )
msg_mock.reset_mock()
- telegram.send_msg({
- 'type': RPCMessageType.EXIT,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'KEY/ETH',
- 'direction': 'Long',
- 'gain': 'loss',
- 'order_rate': 3.201e-04,
- 'amount': 1333.3333333333335,
- 'order_type': 'market',
- 'open_rate': 7.5e-04,
- 'current_rate': 3.201e-04,
- 'profit_amount': -0.05746268,
- 'profit_ratio': -0.57405275,
- 'stake_currency': 'ETH',
- 'quote_currency': 'ETH',
- 'base_currency': 'KEY',
- 'fiat_currency': None,
- 'enter_tag': 'buy_signal1',
- 'exit_reason': ExitType.STOP_LOSS.value,
- 'open_date': dt_now() - timedelta(days=1, hours=2, minutes=30),
- 'close_date': dt_now(),
- })
+ telegram.send_msg(
+ {
+ "type": RPCMessageType.EXIT,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "KEY/ETH",
+ "direction": "Long",
+ "gain": "loss",
+ "order_rate": 3.201e-04,
+ "amount": 1333.3333333333335,
+ "order_type": "market",
+ "open_rate": 7.5e-04,
+ "current_rate": 3.201e-04,
+ "profit_amount": -0.05746268,
+ "profit_ratio": -0.57405275,
+ "stake_currency": "ETH",
+ "quote_currency": "ETH",
+ "base_currency": "KEY",
+ "fiat_currency": None,
+ "enter_tag": "buy_signal1",
+ "exit_reason": ExitType.STOP_LOSS.value,
+ "open_date": dt_now() - timedelta(days=1, hours=2, minutes=30),
+ "close_date": dt_now(),
+ }
+ )
assert msg_mock.call_args[0][0] == (
- '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n'
- '*Unrealized Profit:* `-57.41% (loss: -0.05746 ETH)`\n'
- '*Enter Tag:* `buy_signal1`\n'
- '*Exit Reason:* `stop_loss`\n'
- '*Direction:* `Long`\n'
- '*Amount:* `1333.33333333`\n'
- '*Open Rate:* `0.00075 ETH`\n'
- '*Current Rate:* `0.00032 ETH`\n'
- '*Exit Rate:* `0.00032 ETH`\n'
- '*Duration:* `1 day, 2:30:00 (1590.0 min)`'
+ "\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n"
+ "*Unrealized Profit:* `-57.41% (loss: -0.05746 ETH)`\n"
+ "*Enter Tag:* `buy_signal1`\n"
+ "*Exit Reason:* `stop_loss`\n"
+ "*Direction:* `Long`\n"
+ "*Amount:* `1333.33333333`\n"
+ "*Open Rate:* `0.00075 ETH`\n"
+ "*Current Rate:* `0.00032 ETH`\n"
+ "*Exit Rate:* `0.00032 ETH`\n"
+ "*Duration:* `1 day, 2:30:00 (1590.0 min)`"
)
# Reset singleton function to avoid random breaks
telegram._rpc._fiat_converter.convert_amount = old_convamount
async def test_send_msg_exit_cancel_notification(default_conf, mocker) -> None:
-
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
old_convamount = telegram._rpc._fiat_converter.convert_amount
telegram._rpc._fiat_converter.convert_amount = lambda a, b, c: -24.812
- telegram.send_msg({
- 'type': RPCMessageType.EXIT_CANCEL,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'KEY/ETH',
- 'reason': 'Cancelled on exchange'
- })
+ telegram.send_msg(
+ {
+ "type": RPCMessageType.EXIT_CANCEL,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "KEY/ETH",
+ "reason": "Cancelled on exchange",
+ }
+ )
assert msg_mock.call_args[0][0] == (
- '\N{WARNING SIGN} *Binance (dry):* Cancelling exit Order for KEY/ETH (#1).'
- ' Reason: Cancelled on exchange.')
+ "\N{WARNING SIGN} *Binance (dry):* Cancelling exit Order for KEY/ETH (#1)."
+ " Reason: Cancelled on exchange."
+ )
msg_mock.reset_mock()
# Test with live mode (no dry appendix)
- telegram._config['dry_run'] = False
- telegram.send_msg({
- 'type': RPCMessageType.EXIT_CANCEL,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'KEY/ETH',
- 'reason': 'timeout'
- })
+ telegram._config["dry_run"] = False
+ telegram.send_msg(
+ {
+ "type": RPCMessageType.EXIT_CANCEL,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "KEY/ETH",
+ "reason": "timeout",
+ }
+ )
assert msg_mock.call_args[0][0] == (
- '\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1). Reason: timeout.')
+ "\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1). Reason: timeout."
+ )
# Reset singleton function to avoid random breaks
telegram._rpc._fiat_converter.convert_amount = old_convamount
-@pytest.mark.parametrize('direction,enter_signal,leverage', [
- ('Long', 'long_signal_01', None),
- ('Long', 'long_signal_01', 1.0),
- ('Long', 'long_signal_01', 5.0),
- ('Short', 'short_signal_01', 2.0)])
-def test_send_msg_exit_fill_notification(default_conf, mocker, direction,
- enter_signal, leverage) -> None:
-
- default_conf['telegram']['notification_settings']['exit_fill'] = 'on'
+@pytest.mark.parametrize(
+ "direction,enter_signal,leverage",
+ [
+ ("Long", "long_signal_01", None),
+ ("Long", "long_signal_01", 1.0),
+ ("Long", "long_signal_01", 5.0),
+ ("Short", "short_signal_01", 2.0),
+ ],
+)
+def test_send_msg_exit_fill_notification(
+ default_conf, mocker, direction, enter_signal, leverage
+) -> None:
+ default_conf["telegram"]["notification_settings"]["exit_fill"] = "on"
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
with time_machine.travel("2022-09-01 05:00:00 +00:00", tick=False):
- telegram.send_msg({
- 'type': RPCMessageType.EXIT_FILL,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'KEY/ETH',
- 'leverage': leverage,
- 'direction': direction,
- 'gain': 'loss',
- 'limit': 3.201e-04,
- 'amount': 1333.3333333333335,
- 'order_type': 'market',
- 'open_rate': 7.5e-04,
- 'close_rate': 3.201e-04,
- 'profit_amount': -0.05746268,
- 'profit_ratio': -0.57405275,
- 'stake_currency': 'ETH',
- 'quote_currency': 'ETH',
- 'base_currency': 'KEY',
- 'fiat_currency': None,
- 'enter_tag': enter_signal,
- 'exit_reason': ExitType.STOP_LOSS.value,
- 'open_date': dt_now() - timedelta(days=1, hours=2, minutes=30),
- 'close_date': dt_now(),
- })
+ telegram.send_msg(
+ {
+ "type": RPCMessageType.EXIT_FILL,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "KEY/ETH",
+ "leverage": leverage,
+ "direction": direction,
+ "gain": "loss",
+ "limit": 3.201e-04,
+ "amount": 1333.3333333333335,
+ "order_type": "market",
+ "open_rate": 7.5e-04,
+ "close_rate": 3.201e-04,
+ "profit_amount": -0.05746268,
+ "profit_ratio": -0.57405275,
+ "stake_currency": "ETH",
+ "quote_currency": "ETH",
+ "base_currency": "KEY",
+ "fiat_currency": None,
+ "enter_tag": enter_signal,
+ "exit_reason": ExitType.STOP_LOSS.value,
+ "open_date": dt_now() - timedelta(days=1, hours=2, minutes=30),
+ "close_date": dt_now(),
+ }
+ )
- leverage_text = f' ({leverage:.3g}x)`\n' if leverage and leverage != 1.0 else '`\n'
+ leverage_text = f" ({leverage:.3g}x)`\n" if leverage and leverage != 1.0 else "`\n"
assert msg_mock.call_args[0][0] == (
- '\N{WARNING SIGN} *Binance (dry):* Exited KEY/ETH (#1)\n'
- '*Profit:* `-57.41% (loss: -0.05746 ETH)`\n'
- f'*Enter Tag:* `{enter_signal}`\n'
- '*Exit Reason:* `stop_loss`\n'
+ "\N{WARNING SIGN} *Binance (dry):* Exited KEY/ETH (#1)\n"
+ "*Profit:* `-57.41% (loss: -0.05746 ETH)`\n"
+ f"*Enter Tag:* `{enter_signal}`\n"
+ "*Exit Reason:* `stop_loss`\n"
f"*Direction:* `{direction}"
f"{leverage_text}"
- '*Amount:* `1333.33333333`\n'
- '*Open Rate:* `0.00075 ETH`\n'
- '*Exit Rate:* `0.00032 ETH`\n'
- '*Duration:* `1 day, 2:30:00 (1590.0 min)`'
+ "*Amount:* `1333.33333333`\n"
+ "*Open Rate:* `0.00075 ETH`\n"
+ "*Exit Rate:* `0.00032 ETH`\n"
+ "*Duration:* `1 day, 2:30:00 (1590.0 min)`"
)
def test_send_msg_status_notification(default_conf, mocker) -> None:
-
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': RPCMessageType.STATUS,
- 'status': 'running'
- })
- assert msg_mock.call_args[0][0] == '*Status:* `running`'
+ telegram.send_msg({"type": RPCMessageType.STATUS, "status": "running"})
+ assert msg_mock.call_args[0][0] == "*Status:* `running`"
async def test_warning_notification(default_conf, mocker) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': RPCMessageType.WARNING,
- 'status': 'message'
- })
- assert msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Warning:* `message`'
+ telegram.send_msg({"type": RPCMessageType.WARNING, "status": "message"})
+ assert msg_mock.call_args[0][0] == "\N{WARNING SIGN} *Warning:* `message`"
def test_startup_notification(default_conf, mocker) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': RPCMessageType.STARTUP,
- 'status': '*Custom:* `Hello World`'
- })
- assert msg_mock.call_args[0][0] == '*Custom:* `Hello World`'
+ telegram.send_msg({"type": RPCMessageType.STARTUP, "status": "*Custom:* `Hello World`"})
+ assert msg_mock.call_args[0][0] == "*Custom:* `Hello World`"
def test_send_msg_strategy_msg_notification(default_conf, mocker) -> None:
-
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': RPCMessageType.STRATEGY_MSG,
- 'msg': 'hello world, Test msg'
- })
- assert msg_mock.call_args[0][0] == 'hello world, Test msg'
+ telegram.send_msg({"type": RPCMessageType.STRATEGY_MSG, "msg": "hello world, Test msg"})
+ assert msg_mock.call_args[0][0] == "hello world, Test msg"
def test_send_msg_unknown_type(default_conf, mocker) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': None,
- })
+ telegram.send_msg(
+ {
+ "type": None,
+ }
+ )
assert msg_mock.call_count == 0
-@pytest.mark.parametrize('message_type,enter,enter_signal,leverage', [
- (RPCMessageType.ENTRY, 'Long', 'long_signal_01', None),
- (RPCMessageType.ENTRY, 'Long', 'long_signal_01', 2.0),
- (RPCMessageType.ENTRY, 'Short', 'short_signal_01', 2.0)])
+@pytest.mark.parametrize(
+ "message_type,enter,enter_signal,leverage",
+ [
+ (RPCMessageType.ENTRY, "Long", "long_signal_01", None),
+ (RPCMessageType.ENTRY, "Long", "long_signal_01", 2.0),
+ (RPCMessageType.ENTRY, "Short", "short_signal_01", 2.0),
+ ],
+)
def test_send_msg_buy_notification_no_fiat(
- default_conf, mocker, message_type, enter, enter_signal, leverage) -> None:
- del default_conf['fiat_display_currency']
- default_conf['dry_run'] = False
+ default_conf, mocker, message_type, enter, enter_signal, leverage
+) -> None:
+ del default_conf["fiat_display_currency"]
+ default_conf["dry_run"] = False
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': message_type,
- 'enter_tag': enter_signal,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': leverage,
- 'open_rate': 1.099e-05,
- 'order_type': 'limit',
- 'direction': enter,
- 'sub_trade': False,
- 'stake_amount': 0.01465333,
- 'stake_amount_fiat': 0.0,
- 'stake_currency': 'BTC',
- 'quote_currency': 'BTC',
- 'base_currency': 'ETH',
- 'fiat_currency': None,
- 'current_rate': 1.099e-05,
- 'amount': 1333.3333333333335,
- 'open_date': dt_now() - timedelta(hours=1)
- })
+ telegram.send_msg(
+ {
+ "type": message_type,
+ "enter_tag": enter_signal,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": leverage,
+ "open_rate": 1.099e-05,
+ "order_type": "limit",
+ "direction": enter,
+ "sub_trade": False,
+ "stake_amount": 0.01465333,
+ "stake_amount_fiat": 0.0,
+ "stake_currency": "BTC",
+ "quote_currency": "BTC",
+ "base_currency": "ETH",
+ "fiat_currency": None,
+ "current_rate": 1.099e-05,
+ "amount": 1333.3333333333335,
+ "open_date": dt_now() - timedelta(hours=1),
+ }
+ )
- leverage_text = f' ({leverage:.3g}x)' if leverage and leverage != 1.0 else ''
+ leverage_text = f" ({leverage:.3g}x)" if leverage and leverage != 1.0 else ""
assert msg_mock.call_args[0][0] == (
- f'\N{LARGE BLUE CIRCLE} *Binance:* New Trade (#1)\n'
- '*Pair:* `ETH/BTC`\n'
- f'*Enter Tag:* `{enter_signal}`\n'
- '*Amount:* `1333.33333333`\n'
- f'*Direction:* `{enter}'
- f'{leverage_text}`\n'
- '*Open Rate:* `0.00001099 BTC`\n'
- '*Current Rate:* `0.00001099 BTC`\n'
- '*Total:* `0.01465333 BTC`'
+ f"\N{LARGE BLUE CIRCLE} *Binance:* New Trade (#1)\n"
+ "*Pair:* `ETH/BTC`\n"
+ f"*Enter Tag:* `{enter_signal}`\n"
+ "*Amount:* `1333.33333333`\n"
+ f"*Direction:* `{enter}"
+ f"{leverage_text}`\n"
+ "*Open Rate:* `0.00001099 BTC`\n"
+ "*Current Rate:* `0.00001099 BTC`\n"
+ "*Total:* `0.01465333 BTC`"
)
-@pytest.mark.parametrize('direction,enter_signal,leverage', [
- ('Long', 'long_signal_01', None),
- ('Long', 'long_signal_01', 1.0),
- ('Long', 'long_signal_01', 5.0),
- ('Short', 'short_signal_01', 2.0),
-])
+@pytest.mark.parametrize(
+ "direction,enter_signal,leverage",
+ [
+ ("Long", "long_signal_01", None),
+ ("Long", "long_signal_01", 1.0),
+ ("Long", "long_signal_01", 5.0),
+ ("Short", "short_signal_01", 2.0),
+ ],
+)
def test_send_msg_exit_notification_no_fiat(
- default_conf, mocker, direction, enter_signal, leverage, time_machine) -> None:
- del default_conf['fiat_display_currency']
- time_machine.move_to('2022-05-02 00:00:00 +00:00', tick=False)
+ default_conf, mocker, direction, enter_signal, leverage, time_machine
+) -> None:
+ del default_conf["fiat_display_currency"]
+ time_machine.move_to("2022-05-02 00:00:00 +00:00", tick=False)
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- telegram.send_msg({
- 'type': RPCMessageType.EXIT,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'KEY/ETH',
- 'gain': 'loss',
- 'leverage': leverage,
- 'direction': direction,
- 'sub_trade': False,
- 'order_rate': 3.201e-04,
- 'amount': 1333.3333333333335,
- 'order_type': 'limit',
- 'open_rate': 7.5e-04,
- 'current_rate': 3.201e-04,
- 'profit_amount': -0.05746268,
- 'profit_ratio': -0.57405275,
- 'stake_currency': 'ETH',
- 'quote_currency': 'ETH',
- 'base_currency': 'KEY',
- 'fiat_currency': 'USD',
- 'enter_tag': enter_signal,
- 'exit_reason': ExitType.STOP_LOSS.value,
- 'open_date': dt_now() - timedelta(hours=2, minutes=35, seconds=3),
- 'close_date': dt_now(),
- })
+ telegram.send_msg(
+ {
+ "type": RPCMessageType.EXIT,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "KEY/ETH",
+ "gain": "loss",
+ "leverage": leverage,
+ "direction": direction,
+ "sub_trade": False,
+ "order_rate": 3.201e-04,
+ "amount": 1333.3333333333335,
+ "order_type": "limit",
+ "open_rate": 7.5e-04,
+ "current_rate": 3.201e-04,
+ "profit_amount": -0.05746268,
+ "profit_ratio": -0.57405275,
+ "stake_currency": "ETH",
+ "quote_currency": "ETH",
+ "base_currency": "KEY",
+ "fiat_currency": "USD",
+ "enter_tag": enter_signal,
+ "exit_reason": ExitType.STOP_LOSS.value,
+ "open_date": dt_now() - timedelta(hours=2, minutes=35, seconds=3),
+ "close_date": dt_now(),
+ }
+ )
- leverage_text = f' ({leverage:.3g}x)' if leverage and leverage != 1.0 else ''
+ leverage_text = f" ({leverage:.3g}x)" if leverage and leverage != 1.0 else ""
assert msg_mock.call_args[0][0] == (
- '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n'
- '*Unrealized Profit:* `-57.41% (loss: -0.05746 ETH)`\n'
- f'*Enter Tag:* `{enter_signal}`\n'
- '*Exit Reason:* `stop_loss`\n'
- f'*Direction:* `{direction}'
- f'{leverage_text}`\n'
- '*Amount:* `1333.33333333`\n'
- '*Open Rate:* `0.00075 ETH`\n'
- '*Current Rate:* `0.00032 ETH`\n'
- '*Exit Rate:* `0.00032 ETH`\n'
- '*Duration:* `2:35:03 (155.1 min)`'
+ "\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n"
+ "*Unrealized Profit:* `-57.41% (loss: -0.05746 ETH)`\n"
+ f"*Enter Tag:* `{enter_signal}`\n"
+ "*Exit Reason:* `stop_loss`\n"
+ f"*Direction:* `{direction}"
+ f"{leverage_text}`\n"
+ "*Amount:* `1333.33333333`\n"
+ "*Open Rate:* `0.00075 ETH`\n"
+ "*Current Rate:* `0.00032 ETH`\n"
+ "*Exit Rate:* `0.00032 ETH`\n"
+ "*Duration:* `2:35:03 (155.1 min)`"
)
-@pytest.mark.parametrize('msg,expected', [
- ({'profit_ratio': 0.201, 'exit_reason': 'roi'}, "\N{ROCKET}"),
- ({'profit_ratio': 0.051, 'exit_reason': 'roi'}, "\N{ROCKET}"),
- ({'profit_ratio': 0.0256, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
- ({'profit_ratio': 0.01, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
- ({'profit_ratio': 0.0, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
- ({'profit_ratio': -0.05, 'exit_reason': 'stop_loss'}, "\N{WARNING SIGN}"),
- ({'profit_ratio': -0.02, 'exit_reason': 'sell_signal'}, "\N{CROSS MARK}"),
-])
+@pytest.mark.parametrize(
+ "msg,expected",
+ [
+ ({"profit_ratio": 0.201, "exit_reason": "roi"}, "\N{ROCKET}"),
+ ({"profit_ratio": 0.051, "exit_reason": "roi"}, "\N{ROCKET}"),
+ ({"profit_ratio": 0.0256, "exit_reason": "roi"}, "\N{EIGHT SPOKED ASTERISK}"),
+ ({"profit_ratio": 0.01, "exit_reason": "roi"}, "\N{EIGHT SPOKED ASTERISK}"),
+ ({"profit_ratio": 0.0, "exit_reason": "roi"}, "\N{EIGHT SPOKED ASTERISK}"),
+ ({"profit_ratio": -0.05, "exit_reason": "stop_loss"}, "\N{WARNING SIGN}"),
+ ({"profit_ratio": -0.02, "exit_reason": "sell_signal"}, "\N{CROSS MARK}"),
+ ],
+)
def test__exit_emoji(default_conf, mocker, msg, expected):
- del default_conf['fiat_display_currency']
+ del default_conf["fiat_display_currency"]
telegram, _, _ = get_telegram_testobject(mocker, default_conf)
@@ -2559,7 +2636,7 @@ def test__exit_emoji(default_conf, mocker, msg, expected):
async def test_telegram__send_msg(default_conf, mocker, caplog) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
bot = MagicMock()
bot.send_message = AsyncMock()
bot.edit_message_text = AsyncMock()
@@ -2567,64 +2644,68 @@ async def test_telegram__send_msg(default_conf, mocker, caplog) -> None:
telegram._app = MagicMock()
telegram._app.bot = bot
- await telegram._send_msg('test')
+ await telegram._send_msg("test")
assert len(bot.method_calls) == 1
# Test update
query = MagicMock()
query.edit_message_text = AsyncMock()
- await telegram._send_msg('test', callback_path="DeadBeef", query=query, reload_able=True)
+ await telegram._send_msg("test", callback_path="DeadBeef", query=query, reload_able=True)
assert query.edit_message_text.call_count == 1
- assert "Updated: " in query.edit_message_text.call_args_list[0][1]['text']
+ assert "Updated: " in query.edit_message_text.call_args_list[0][1]["text"]
query.edit_message_text = AsyncMock(side_effect=BadRequest("not modified"))
- await telegram._send_msg('test', callback_path="DeadBeef", query=query)
+ await telegram._send_msg("test", callback_path="DeadBeef", query=query)
assert query.edit_message_text.call_count == 1
assert not log_has_re(r"TelegramError: .*", caplog)
query.edit_message_text = AsyncMock(side_effect=BadRequest(""))
- await telegram._send_msg('test2', callback_path="DeadBeef", query=query)
+ await telegram._send_msg("test2", callback_path="DeadBeef", query=query)
assert query.edit_message_text.call_count == 1
assert log_has_re(r"TelegramError: .*", caplog)
query.edit_message_text = AsyncMock(side_effect=TelegramError("DeadBEEF"))
- await telegram._send_msg('test3', callback_path="DeadBeef", query=query)
+ await telegram._send_msg("test3", callback_path="DeadBeef", query=query)
assert log_has_re(r"TelegramError: DeadBEEF! Giving up.*", caplog)
async def test__send_msg_network_error(default_conf, mocker, caplog) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
bot = MagicMock()
- bot.send_message = MagicMock(side_effect=NetworkError('Oh snap'))
+ bot.send_message = MagicMock(side_effect=NetworkError("Oh snap"))
telegram, _, _ = get_telegram_testobject(mocker, default_conf, mock=False)
telegram._app = MagicMock()
telegram._app.bot = bot
- telegram._config['telegram']['enabled'] = True
- await telegram._send_msg('test')
+ telegram._config["telegram"]["enabled"] = True
+ await telegram._send_msg("test")
# Bot should've tried to send it twice
assert len(bot.method_calls) == 2
- assert log_has('Telegram NetworkError: Oh snap! Trying one more time.', caplog)
+ assert log_has("Telegram NetworkError: Oh snap! Trying one more time.", caplog)
@pytest.mark.filterwarnings("ignore:.*ChatPermissions")
async def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
- mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+ mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock())
bot = MagicMock()
bot.send_message = AsyncMock()
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
- invalid_keys_list = [['/not_valid', '/profit'], ['/daily'], ['/alsoinvalid']]
- default_keys_list = [['/daily', '/profit', '/balance'],
- ['/status', '/status table', '/performance'],
- ['/count', '/start', '/stop', '/help']]
+ invalid_keys_list = [["/not_valid", "/profit"], ["/daily"], ["/alsoinvalid"]]
+ default_keys_list = [
+ ["/daily", "/profit", "/balance"],
+ ["/status", "/status table", "/performance"],
+ ["/count", "/start", "/stop", "/help"],
+ ]
default_keyboard = ReplyKeyboardMarkup(default_keys_list)
- custom_keys_list = [['/daily', '/stats', '/balance', '/profit', '/profit 5'],
- ['/count', '/start', '/reload_config', '/help']]
+ custom_keys_list = [
+ ["/daily", "/stats", "/balance", "/profit", "/profit 5"],
+ ["/count", "/start", "/reload_config", "/help"],
+ ]
custom_keyboard = ReplyKeyboardMarkup(custom_keys_list)
def init_telegram(freqtradebot):
@@ -2634,31 +2715,39 @@ async def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
return telegram
# no keyboard in config -> default keyboard
- freqtradebot.config['telegram']['enabled'] = True
+ freqtradebot.config["telegram"]["enabled"] = True
telegram = init_telegram(freqtradebot)
- await telegram._send_msg('test')
- used_keyboard = bot.send_message.call_args[1]['reply_markup']
+ await telegram._send_msg("test")
+ used_keyboard = bot.send_message.call_args[1]["reply_markup"]
assert used_keyboard == default_keyboard
# invalid keyboard in config -> default keyboard
- freqtradebot.config['telegram']['enabled'] = True
- freqtradebot.config['telegram']['keyboard'] = invalid_keys_list
- err_msg = re.escape("config.telegram.keyboard: Invalid commands for custom "
- "Telegram keyboard: ['/not_valid', '/alsoinvalid']"
- "\nvalid commands are: ") + r"*"
+ freqtradebot.config["telegram"]["enabled"] = True
+ freqtradebot.config["telegram"]["keyboard"] = invalid_keys_list
+ err_msg = (
+ re.escape(
+ "config.telegram.keyboard: Invalid commands for custom "
+ "Telegram keyboard: ['/not_valid', '/alsoinvalid']"
+ "\nvalid commands are: "
+ )
+ + r"*"
+ )
with pytest.raises(OperationalException, match=err_msg):
telegram = init_telegram(freqtradebot)
# valid keyboard in config -> custom keyboard
- freqtradebot.config['telegram']['enabled'] = True
- freqtradebot.config['telegram']['keyboard'] = custom_keys_list
+ freqtradebot.config["telegram"]["enabled"] = True
+ freqtradebot.config["telegram"]["keyboard"] = custom_keys_list
telegram = init_telegram(freqtradebot)
- await telegram._send_msg('test')
- used_keyboard = bot.send_message.call_args[1]['reply_markup']
+ await telegram._send_msg("test")
+ used_keyboard = bot.send_message.call_args[1]["reply_markup"]
assert used_keyboard == custom_keyboard
- assert log_has("using custom keyboard from config.json: "
- "[['/daily', '/stats', '/balance', '/profit', '/profit 5'], ['/count', "
- "'/start', '/reload_config', '/help']]", caplog)
+ assert log_has(
+ "using custom keyboard from config.json: "
+ "[['/daily', '/stats', '/balance', '/profit', '/profit 5'], ['/count', "
+ "'/start', '/reload_config', '/help']]",
+ caplog,
+ )
async def test_change_market_direction(default_conf, mocker, update) -> None:
@@ -2675,7 +2764,6 @@ async def test_change_market_direction(default_conf, mocker, update) -> None:
async def test_telegram_list_custom_data(default_conf_usdt, update, ticker, fee, mocker) -> None:
-
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker,
@@ -2689,11 +2777,11 @@ async def test_telegram_list_custom_data(default_conf_usdt, update, ticker, fee,
context = MagicMock()
await telegram._list_custom_data(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Trade-id not set.' in msg_mock.call_args_list[0][0][0]
+ assert "Trade-id not set." in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
#
- context.args = ['1']
+ context.args = ["1"]
await telegram._list_custom_data(update=update, context=context)
assert msg_mock.call_count == 1
assert (
@@ -2703,8 +2791,8 @@ async def test_telegram_list_custom_data(default_conf_usdt, update, ticker, fee,
# Add some custom data
trade1 = Trade.get_trades_proxy()[0]
- trade1.set_custom_data('test_int', 1)
- trade1.set_custom_data('test_dict', {'test': 'dict'})
+ trade1.set_custom_data("test_int", 1)
+ trade1.set_custom_data("test_dict", {"test": "dict"})
Trade.commit()
context.args = [f"{trade1.id}"]
await telegram._list_custom_data(update=update, context=context)
@@ -2712,9 +2800,11 @@ async def test_telegram_list_custom_data(default_conf_usdt, update, ticker, fee,
assert "Found custom-data entries: " in msg_mock.call_args_list[0][0][0]
assert (
"*Key:* `test_int`\n*ID:* `1`\n*Trade ID:* `1`\n*Type:* `int`\n"
- "*Value:* `1`\n*Create Date:*") in msg_mock.call_args_list[1][0][0]
+ "*Value:* `1`\n*Create Date:*"
+ ) in msg_mock.call_args_list[1][0][0]
assert (
- '*Key:* `test_dict`\n*ID:* `2`\n*Trade ID:* `1`\n*Type:* `dict`\n'
- '*Value:* `{"test": "dict"}`\n*Create Date:* `') in msg_mock.call_args_list[2][0][0]
+ "*Key:* `test_dict`\n*ID:* `2`\n*Trade ID:* `1`\n*Type:* `dict`\n"
+ '*Value:* `{"test": "dict"}`\n*Create Date:* `'
+ ) in msg_mock.call_args_list[2][0][0]
msg_mock.reset_mock()
diff --git a/tests/rpc/test_rpc_webhook.py b/tests/rpc/test_rpc_webhook.py
index 7d88056e4..dc33f965f 100644
--- a/tests/rpc/test_rpc_webhook.py
+++ b/tests/rpc/test_rpc_webhook.py
@@ -26,47 +26,43 @@ def get_webhook_dict() -> dict:
"value2": "limit {limit:8f}",
"value3": "{stake_amount:8f} {stake_currency}",
"value4": "leverage {leverage:.1f}",
- "value5": "direction {direction}"
+ "value5": "direction {direction}",
},
"webhookentrycancel": {
"value1": "Cancelling Open Buy Order for {pair}",
"value2": "limit {limit:8f}",
"value3": "{stake_amount:8f} {stake_currency}",
"value4": "leverage {leverage:.1f}",
- "value5": "direction {direction}"
+ "value5": "direction {direction}",
},
"webhookentryfill": {
"value1": "Buy Order for {pair} filled",
"value2": "at {open_rate:8f}",
"value3": "{stake_amount:8f} {stake_currency}",
"value4": "leverage {leverage:.1f}",
- "value5": "direction {direction}"
+ "value5": "direction {direction}",
},
"webhookexit": {
"value1": "Selling {pair}",
"value2": "limit {limit:8f}",
- "value3": "profit: {profit_amount:8f} {stake_currency} ({profit_ratio})"
+ "value3": "profit: {profit_amount:8f} {stake_currency} ({profit_ratio})",
},
"webhookexitcancel": {
"value1": "Cancelling Open Sell Order for {pair}",
"value2": "limit {limit:8f}",
- "value3": "profit: {profit_amount:8f} {stake_currency} ({profit_ratio})"
+ "value3": "profit: {profit_amount:8f} {stake_currency} ({profit_ratio})",
},
"webhookexitfill": {
"value1": "Sell Order for {pair} filled",
"value2": "at {close_rate:8f}",
- "value3": ""
+ "value3": "",
},
- "webhookstatus": {
- "value1": "Status: {status}",
- "value2": "",
- "value3": ""
- }
+ "webhookstatus": {"value1": "Status: {status}", "value2": "", "value3": ""},
}
def test__init__(mocker, default_conf):
- default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"}
+ default_conf["webhook"] = {"enabled": True, "url": "https://DEADBEEF.com"}
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
assert webhook._config == default_conf
@@ -80,255 +76,290 @@ def test_send_msg_webhook(default_conf, mocker):
msg_mock = MagicMock()
mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
msg = {
- 'type': RPCMessageType.ENTRY,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': 1.0,
- 'direction': 'Long',
- 'limit': 0.005,
- 'stake_amount': 0.8,
- 'stake_amount_fiat': 500,
- 'stake_currency': 'BTC',
- 'fiat_currency': 'EUR'
+ "type": RPCMessageType.ENTRY,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": 1.0,
+ "direction": "Long",
+ "limit": 0.005,
+ "stake_amount": 0.8,
+ "stake_amount_fiat": 500,
+ "stake_currency": "BTC",
+ "fiat_currency": "EUR",
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["entry"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["entry"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["entry"]["value3"].format(**msg))
- assert (msg_mock.call_args[0][0]["value4"] ==
- default_conf["webhook"]["entry"]["value4"].format(**msg))
- assert (msg_mock.call_args[0][0]["value5"] ==
- default_conf["webhook"]["entry"]["value5"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["entry"]["value1"].format(
+ **msg
+ )
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["entry"]["value2"].format(
+ **msg
+ )
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["entry"]["value3"].format(
+ **msg
+ )
+ assert msg_mock.call_args[0][0]["value4"] == default_conf["webhook"]["entry"]["value4"].format(
+ **msg
+ )
+ assert msg_mock.call_args[0][0]["value5"] == default_conf["webhook"]["entry"]["value5"].format(
+ **msg
+ )
# Test short
msg_mock.reset_mock()
msg = {
- 'type': RPCMessageType.ENTRY,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': 2.0,
- 'direction': 'Short',
- 'limit': 0.005,
- 'stake_amount': 0.8,
- 'stake_amount_fiat': 500,
- 'stake_currency': 'BTC',
- 'fiat_currency': 'EUR'
+ "type": RPCMessageType.ENTRY,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": 2.0,
+ "direction": "Short",
+ "limit": 0.005,
+ "stake_amount": 0.8,
+ "stake_amount_fiat": 500,
+ "stake_currency": "BTC",
+ "fiat_currency": "EUR",
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["entry"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["entry"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["entry"]["value3"].format(**msg))
- assert (msg_mock.call_args[0][0]["value4"] ==
- default_conf["webhook"]["entry"]["value4"].format(**msg))
- assert (msg_mock.call_args[0][0]["value5"] ==
- default_conf["webhook"]["entry"]["value5"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["entry"]["value1"].format(
+ **msg
+ )
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["entry"]["value2"].format(
+ **msg
+ )
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["entry"]["value3"].format(
+ **msg
+ )
+ assert msg_mock.call_args[0][0]["value4"] == default_conf["webhook"]["entry"]["value4"].format(
+ **msg
+ )
+ assert msg_mock.call_args[0][0]["value5"] == default_conf["webhook"]["entry"]["value5"].format(
+ **msg
+ )
# Test buy cancel
msg_mock.reset_mock()
msg = {
- 'type': RPCMessageType.ENTRY_CANCEL,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': 1.0,
- 'direction': 'Long',
- 'limit': 0.005,
- 'stake_amount': 0.8,
- 'stake_amount_fiat': 500,
- 'stake_currency': 'BTC',
- 'fiat_currency': 'EUR'
+ "type": RPCMessageType.ENTRY_CANCEL,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": 1.0,
+ "direction": "Long",
+ "limit": 0.005,
+ "stake_amount": 0.8,
+ "stake_amount_fiat": 500,
+ "stake_currency": "BTC",
+ "fiat_currency": "EUR",
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["webhookentrycancel"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["webhookentrycancel"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["webhookentrycancel"]["value3"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["webhookentrycancel"][
+ "value1"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookentrycancel"][
+ "value2"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookentrycancel"][
+ "value3"
+ ].format(**msg)
# Test short cancel
msg_mock.reset_mock()
msg = {
- 'type': RPCMessageType.ENTRY_CANCEL,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': 2.0,
- 'direction': 'Short',
- 'limit': 0.005,
- 'stake_amount': 0.8,
- 'stake_amount_fiat': 500,
- 'stake_currency': 'BTC',
- 'fiat_currency': 'EUR'
+ "type": RPCMessageType.ENTRY_CANCEL,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": 2.0,
+ "direction": "Short",
+ "limit": 0.005,
+ "stake_amount": 0.8,
+ "stake_amount_fiat": 500,
+ "stake_currency": "BTC",
+ "fiat_currency": "EUR",
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["webhookentrycancel"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["webhookentrycancel"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["webhookentrycancel"]["value3"].format(**msg))
- assert (msg_mock.call_args[0][0]["value4"] ==
- default_conf["webhook"]["webhookentrycancel"]["value4"].format(**msg))
- assert (msg_mock.call_args[0][0]["value5"] ==
- default_conf["webhook"]["webhookentrycancel"]["value5"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["webhookentrycancel"][
+ "value1"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookentrycancel"][
+ "value2"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookentrycancel"][
+ "value3"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value4"] == default_conf["webhook"]["webhookentrycancel"][
+ "value4"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value5"] == default_conf["webhook"]["webhookentrycancel"][
+ "value5"
+ ].format(**msg)
# Test buy fill
msg_mock.reset_mock()
msg = {
- 'type': RPCMessageType.ENTRY_FILL,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': 1.0,
- 'direction': 'Long',
- 'open_rate': 0.005,
- 'stake_amount': 0.8,
- 'stake_amount_fiat': 500,
- 'stake_currency': 'BTC',
- 'fiat_currency': 'EUR'
+ "type": RPCMessageType.ENTRY_FILL,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": 1.0,
+ "direction": "Long",
+ "open_rate": 0.005,
+ "stake_amount": 0.8,
+ "stake_amount_fiat": 500,
+ "stake_currency": "BTC",
+ "fiat_currency": "EUR",
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["webhookentryfill"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["webhookentryfill"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["webhookentryfill"]["value3"].format(**msg))
- assert (msg_mock.call_args[0][0]["value4"] ==
- default_conf["webhook"]["webhookentrycancel"]["value4"].format(**msg))
- assert (msg_mock.call_args[0][0]["value5"] ==
- default_conf["webhook"]["webhookentrycancel"]["value5"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["webhookentryfill"][
+ "value1"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookentryfill"][
+ "value2"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookentryfill"][
+ "value3"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value4"] == default_conf["webhook"]["webhookentrycancel"][
+ "value4"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value5"] == default_conf["webhook"]["webhookentrycancel"][
+ "value5"
+ ].format(**msg)
# Test short fill
msg_mock.reset_mock()
msg = {
- 'type': RPCMessageType.ENTRY_FILL,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'leverage': 2.0,
- 'direction': 'Short',
- 'open_rate': 0.005,
- 'stake_amount': 0.8,
- 'stake_amount_fiat': 500,
- 'stake_currency': 'BTC',
- 'fiat_currency': 'EUR'
+ "type": RPCMessageType.ENTRY_FILL,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "leverage": 2.0,
+ "direction": "Short",
+ "open_rate": 0.005,
+ "stake_amount": 0.8,
+ "stake_amount_fiat": 500,
+ "stake_currency": "BTC",
+ "fiat_currency": "EUR",
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["webhookentryfill"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["webhookentryfill"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["webhookentryfill"]["value3"].format(**msg))
- assert (msg_mock.call_args[0][0]["value4"] ==
- default_conf["webhook"]["webhookentrycancel"]["value4"].format(**msg))
- assert (msg_mock.call_args[0][0]["value5"] ==
- default_conf["webhook"]["webhookentrycancel"]["value5"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["webhookentryfill"][
+ "value1"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookentryfill"][
+ "value2"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookentryfill"][
+ "value3"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value4"] == default_conf["webhook"]["webhookentrycancel"][
+ "value4"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value5"] == default_conf["webhook"]["webhookentrycancel"][
+ "value5"
+ ].format(**msg)
# Test sell
msg_mock.reset_mock()
msg = {
- 'type': RPCMessageType.EXIT,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'gain': "profit",
- 'limit': 0.005,
- 'amount': 0.8,
- 'order_type': 'limit',
- 'open_rate': 0.004,
- 'current_rate': 0.005,
- 'profit_amount': 0.001,
- 'profit_ratio': 0.20,
- 'stake_currency': 'BTC',
- 'sell_reason': ExitType.STOP_LOSS.value
+ "type": RPCMessageType.EXIT,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "gain": "profit",
+ "limit": 0.005,
+ "amount": 0.8,
+ "order_type": "limit",
+ "open_rate": 0.004,
+ "current_rate": 0.005,
+ "profit_amount": 0.001,
+ "profit_ratio": 0.20,
+ "stake_currency": "BTC",
+ "sell_reason": ExitType.STOP_LOSS.value,
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["webhookexit"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["webhookexit"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["webhookexit"]["value3"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["webhookexit"][
+ "value1"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookexit"][
+ "value2"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookexit"][
+ "value3"
+ ].format(**msg)
# Test sell cancel
msg_mock.reset_mock()
msg = {
- 'type': RPCMessageType.EXIT_CANCEL,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'gain': "profit",
- 'limit': 0.005,
- 'amount': 0.8,
- 'order_type': 'limit',
- 'open_rate': 0.004,
- 'current_rate': 0.005,
- 'profit_amount': 0.001,
- 'profit_ratio': 0.20,
- 'stake_currency': 'BTC',
- 'sell_reason': ExitType.STOP_LOSS.value
+ "type": RPCMessageType.EXIT_CANCEL,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "gain": "profit",
+ "limit": 0.005,
+ "amount": 0.8,
+ "order_type": "limit",
+ "open_rate": 0.004,
+ "current_rate": 0.005,
+ "profit_amount": 0.001,
+ "profit_ratio": 0.20,
+ "stake_currency": "BTC",
+ "sell_reason": ExitType.STOP_LOSS.value,
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["webhookexitcancel"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["webhookexitcancel"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["webhookexitcancel"]["value3"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["webhookexitcancel"][
+ "value1"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookexitcancel"][
+ "value2"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookexitcancel"][
+ "value3"
+ ].format(**msg)
# Test Sell fill
msg_mock.reset_mock()
msg = {
- 'type': RPCMessageType.EXIT_FILL,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'gain': "profit",
- 'close_rate': 0.005,
- 'amount': 0.8,
- 'order_type': 'limit',
- 'open_rate': 0.004,
- 'current_rate': 0.005,
- 'profit_amount': 0.001,
- 'profit_ratio': 0.20,
- 'stake_currency': 'BTC',
- 'sell_reason': ExitType.STOP_LOSS.value
+ "type": RPCMessageType.EXIT_FILL,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "gain": "profit",
+ "close_rate": 0.005,
+ "amount": 0.8,
+ "order_type": "limit",
+ "open_rate": 0.004,
+ "current_rate": 0.005,
+ "profit_amount": 0.001,
+ "profit_ratio": 0.20,
+ "stake_currency": "BTC",
+ "sell_reason": ExitType.STOP_LOSS.value,
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["webhookexitfill"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["webhookexitfill"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["webhookexitfill"]["value3"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["webhookexitfill"][
+ "value1"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookexitfill"][
+ "value2"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookexitfill"][
+ "value3"
+ ].format(**msg)
- for msgtype in [RPCMessageType.STATUS,
- RPCMessageType.WARNING,
- RPCMessageType.STARTUP]:
+ for msgtype in [RPCMessageType.STATUS, RPCMessageType.WARNING, RPCMessageType.STARTUP]:
# Test notification
- msg = {
- 'type': msgtype,
- 'status': 'Unfilled sell order for BTC cancelled due to timeout'
- }
+ msg = {"type": msgtype, "status": "Unfilled sell order for BTC cancelled due to timeout"}
msg_mock = MagicMock()
mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
webhook.send_msg(msg)
assert msg_mock.call_count == 1
- assert (msg_mock.call_args[0][0]["value1"] ==
- default_conf["webhook"]["webhookstatus"]["value1"].format(**msg))
- assert (msg_mock.call_args[0][0]["value2"] ==
- default_conf["webhook"]["webhookstatus"]["value2"].format(**msg))
- assert (msg_mock.call_args[0][0]["value3"] ==
- default_conf["webhook"]["webhookstatus"]["value3"].format(**msg))
+ assert msg_mock.call_args[0][0]["value1"] == default_conf["webhook"]["webhookstatus"][
+ "value1"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookstatus"][
+ "value2"
+ ].format(**msg)
+ assert msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookstatus"][
+ "value3"
+ ].format(**msg)
def test_exception_send_msg(default_conf, mocker, caplog):
@@ -338,9 +369,8 @@ def test_exception_send_msg(default_conf, mocker, caplog):
del default_conf["webhook"]["webhookentry"]
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
- webhook.send_msg({'type': RPCMessageType.ENTRY})
- assert log_has(f"Message type '{RPCMessageType.ENTRY}' not configured for webhooks",
- caplog)
+ webhook.send_msg({"type": RPCMessageType.ENTRY})
+ assert log_has(f"Message type '{RPCMessageType.ENTRY}' not configured for webhooks", caplog)
default_conf["webhook"] = get_webhook_dict()
default_conf["webhook"]["strategy_msg"] = {"value1": "{DEADBEEF:8f}"}
@@ -348,112 +378,103 @@ def test_exception_send_msg(default_conf, mocker, caplog):
mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
msg = {
- 'type': RPCMessageType.STRATEGY_MSG,
- 'msg': 'hello world',
+ "type": RPCMessageType.STRATEGY_MSG,
+ "msg": "hello world",
}
webhook.send_msg(msg)
- assert log_has("Problem calling Webhook. Please check your webhook configuration. "
- "Exception: 'DEADBEEF'", caplog)
+ assert log_has(
+ "Problem calling Webhook. Please check your webhook configuration. "
+ "Exception: 'DEADBEEF'",
+ caplog,
+ )
# Test no failure for not implemented but known messagetypes
for e in RPCMessageType:
- msg = {
- 'type': e,
- 'status': 'whatever'
- }
+ msg = {"type": e, "status": "whatever"}
webhook.send_msg(msg)
# Test no failure for not implemented but known messagetypes
for e in RPCMessageType:
- msg = {
- 'type': e,
- 'status': 'whatever'
- }
+ msg = {"type": e, "status": "whatever"}
webhook.send_msg(msg)
def test__send_msg(default_conf, mocker, caplog):
default_conf["webhook"] = get_webhook_dict()
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
- msg = {'value1': 'DEADBEEF',
- 'value2': 'ALIVEBEEF',
- 'value3': 'FREQTRADE'}
+ msg = {"value1": "DEADBEEF", "value2": "ALIVEBEEF", "value3": "FREQTRADE"}
post = MagicMock()
mocker.patch("freqtrade.rpc.webhook.post", post)
webhook._send_msg(msg)
assert post.call_count == 1
- assert post.call_args[1] == {'data': msg, 'timeout': 10}
- assert post.call_args[0] == (default_conf['webhook']['url'], )
+ assert post.call_args[1] == {"data": msg, "timeout": 10}
+ assert post.call_args[0] == (default_conf["webhook"]["url"],)
post = MagicMock(side_effect=RequestException)
mocker.patch("freqtrade.rpc.webhook.post", post)
webhook._send_msg(msg)
- assert log_has('Could not call webhook url. Exception: ', caplog)
+ assert log_has("Could not call webhook url. Exception: ", caplog)
def test__send_msg_with_json_format(default_conf, mocker, caplog):
default_conf["webhook"] = get_webhook_dict()
default_conf["webhook"]["format"] = "json"
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
- msg = {'text': 'Hello'}
+ msg = {"text": "Hello"}
post = MagicMock()
mocker.patch("freqtrade.rpc.webhook.post", post)
webhook._send_msg(msg)
- assert post.call_args[1] == {'json': msg, 'timeout': 10}
+ assert post.call_args[1] == {"json": msg, "timeout": 10}
def test__send_msg_with_raw_format(default_conf, mocker, caplog):
default_conf["webhook"] = get_webhook_dict()
default_conf["webhook"]["format"] = "raw"
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
- msg = {'data': 'Hello'}
+ msg = {"data": "Hello"}
post = MagicMock()
mocker.patch("freqtrade.rpc.webhook.post", post)
webhook._send_msg(msg)
assert post.call_args[1] == {
- 'data': msg['data'],
- 'headers': {'Content-Type': 'text/plain'},
- 'timeout': 10
+ "data": msg["data"],
+ "headers": {"Content-Type": "text/plain"},
+ "timeout": 10,
}
def test_send_msg_discord(default_conf, mocker):
-
- default_conf["discord"] = {
- 'enabled': True,
- 'webhook_url': "https://webhookurl..."
- }
+ default_conf["discord"] = {"enabled": True, "webhook_url": "https://webhookurl..."}
msg_mock = MagicMock()
mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
discord = Discord(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
msg = {
- 'type': RPCMessageType.EXIT_FILL,
- 'trade_id': 1,
- 'exchange': 'Binance',
- 'pair': 'ETH/BTC',
- 'direction': 'Long',
- 'gain': "profit",
- 'close_rate': 0.005,
- 'amount': 0.8,
- 'order_type': 'limit',
- 'open_date': datetime.now() - timedelta(days=1),
- 'close_date': datetime.now(),
- 'open_rate': 0.004,
- 'current_rate': 0.005,
- 'profit_amount': 0.001,
- 'profit_ratio': 0.20,
- 'stake_currency': 'BTC',
- 'enter_tag': 'enter_tagggg',
- 'exit_reason': ExitType.STOP_LOSS.value,
+ "type": RPCMessageType.EXIT_FILL,
+ "trade_id": 1,
+ "exchange": "Binance",
+ "pair": "ETH/BTC",
+ "direction": "Long",
+ "gain": "profit",
+ "close_rate": 0.005,
+ "amount": 0.8,
+ "order_type": "limit",
+ "open_date": datetime.now() - timedelta(days=1),
+ "close_date": datetime.now(),
+ "open_rate": 0.004,
+ "current_rate": 0.005,
+ "profit_amount": 0.001,
+ "profit_ratio": 0.20,
+ "stake_currency": "BTC",
+ "enter_tag": "enter_tagggg",
+ "exit_reason": ExitType.STOP_LOSS.value,
}
discord.send_msg(msg=msg)
assert msg_mock.call_count == 1
- assert 'embeds' in msg_mock.call_args_list[0][0][0]
- assert 'title' in msg_mock.call_args_list[0][0][0]['embeds'][0]
- assert 'color' in msg_mock.call_args_list[0][0][0]['embeds'][0]
- assert 'fields' in msg_mock.call_args_list[0][0][0]['embeds'][0]
+ assert "embeds" in msg_mock.call_args_list[0][0][0]
+ assert "title" in msg_mock.call_args_list[0][0][0]["embeds"][0]
+ assert "color" in msg_mock.call_args_list[0][0][0]["embeds"][0]
+ assert "fields" in msg_mock.call_args_list[0][0][0]["embeds"][0]