def test_rpc_trade_history(mocker, default_conf, markets, fee): mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple('freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets)) freqtradebot = get_patched_freqtradebot(mocker, default_conf) create_mock_trades(fee) 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) trades = rpc._rpc_trade_history(0) assert len(trades['trades']) == 2 assert trades['trades_count'] == 2 # The first closed trade is for ETC ... sorting is descending assert trades['trades'][-1]['pair'] == 'ETC/BTC' assert trades['trades'][0]['pair'] == 'XRP/BTC'
def test_rpc_start(mocker, default_conf) -> None: patch_coinmarketcap(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', validate_pairs=MagicMock(), get_ticker=MagicMock() ) freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED result = rpc._rpc_start() assert {'status': 'starting trader ...'} == result assert freqtradebot.state == State.RUNNING result = rpc._rpc_start() assert {'status': 'already running'} == result assert freqtradebot.state == State.RUNNING
def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) counts = rpc._rpc_count() assert counts["current"] == 0 # Create some test data freqtradebot.create_trades() counts = rpc._rpc_count() assert counts["current"] == 1
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, }}) ftbot = get_patched_freqtradebot(mocker, default_conf) rpc = RPC(ftbot) mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', MagicMock()) try: apiserver = ApiServer(default_conf) apiserver.add_rpc_handler(rpc) yield ftbot, TestClient(apiserver.app) # Cleanup ... ? finally: ApiServer.shutdown()
def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: patch_coinmarketcap(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', validate_pairs=MagicMock(), get_ticker=ticker, get_fee=fee, get_markets=markets ) freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED with pytest.raises(RPCException, match=r'.*trader is not running*'): rpc._rpc_trade_status() freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_trade_status() freqtradebot.create_trade() results = rpc._rpc_trade_status() assert { 'trade_id': 1, 'pair': 'ETH/BTC', 'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH', 'date': ANY, 'open_rate': 1.099e-05, 'close_rate': None, 'current_rate': 1.098e-05, 'amount': 90.99181074, 'close_profit': None, 'current_profit': -0.59, 'open_order': '(limit buy rem=0.00000000)' } == results[0]
def __init__(self, freqtrade) -> None: """ Initializes all enabled rpc modules """ self.registered_modules: List[RPCHandler] = [] self._rpc = RPC(freqtrade) config = freqtrade.config # Enable telegram if config.get('telegram', {}).get('enabled', False): logger.info('Enabling rpc.telegram ...') from freqtrade.rpc.telegram import Telegram self.registered_modules.append(Telegram(self._rpc, config)) # Enable Webhook if config.get('webhook', {}).get('enabled', False): logger.info('Enabling rpc.webhook ...') from freqtrade.rpc.webhook import Webhook self.registered_modules.append(Webhook(self._rpc, config)) # Enable local rest api server for cmd line control if config.get('api_server', {}).get('enabled', False): logger.info('Enabling rpc.api_server') from freqtrade.rpc.api_server import ApiServer self.registered_modules.append(ApiServer(self._rpc, config))
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": "******", "password": "******", } }) mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock()) server_mock = MagicMock() server_mock.cleanup = MagicMock() mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock) apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf) apiserver.cleanup() assert apiserver._server.cleanup.call_count == 1 assert log_has("Stopping API Server", caplog)
def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: patch_coinmarketcap(mocker) patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple('freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, get_markets=markets) freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) trades = rpc._rpc_count() nb_trades = len(trades) assert nb_trades == 0 # Create some test data freqtradebot.create_trade() trades = rpc._rpc_count() nb_trades = len(trades) assert nb_trades == 1
def test_rpc_balance_handle_error(default_conf, mocker): mock_balance = { 'BTC': { 'free': 10.0, 'total': 12.0, 'used': 2.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 }}), ) mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=mock_balance), get_tickers=MagicMock( side_effect=TemporaryError('Could not load ticker due to xxx'))) freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) 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'])
def test_performance_handle(default_conf, ticker, limit_buy_order, fee, limit_sell_order, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), fetch_ticker=ticker, get_fee=fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) # Create some test data freqtradebot.enter_positions() trade = Trade.query.first() assert trade # Simulate fulfilled LIMIT_BUY order for trade oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') trade.update_trade(oobj) # Simulate fulfilled LIMIT_SELL order for trade oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') trade.update_trade(oobj) trade.close_date = datetime.utcnow() trade.is_open = False res = rpc._rpc_performance() assert len(res) == 1 assert res[0]['pair'] == 'ETH/BTC' assert res[0]['count'] == 1 assert prec_satoshi(res[0]['profit_pct'], 6.2)
def test_rpc_edge_disabled(mocker, default_conf) -> None: 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.'): rpc._rpc_edge()
def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) cancel_order_mock = MagicMock() mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, cancel_order=cancel_order_mock, get_order=MagicMock(return_value={ 'status': 'closed', 'type': 'limit', 'side': 'buy' }), get_fee=fee, ) mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000) freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED with pytest.raises(RPCException, match=r'.*trader is not running*'): rpc._rpc_forcesell(None) freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*invalid argument*'): rpc._rpc_forcesell(None) msg = rpc._rpc_forcesell('all') assert msg == {'result': 'Created sell orders for all open trades.'} freqtradebot.enter_positions() msg = rpc._rpc_forcesell('all') assert msg == {'result': 'Created sell orders for all open trades.'} msg = rpc._rpc_forcesell('1') assert msg == {'result': 'Created sell order for trade 1.'} freqtradebot.state = State.STOPPED with pytest.raises(RPCException, match=r'.*trader is not running*'): rpc._rpc_forcesell(None) with pytest.raises(RPCException, match=r'.*trader is not running*'): rpc._rpc_forcesell('all') freqtradebot.state = State.RUNNING assert cancel_order_mock.call_count == 0 # make an limit-buy open trade trade = Trade.query.filter(Trade.id == '1').first() filled_amount = trade.amount / 2 mocker.patch('freqtrade.exchange.Exchange.get_order', return_value={ 'status': 'open', '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_forcesell('1') assert cancel_order_mock.call_count == 1 assert trade.amount == filled_amount freqtradebot.enter_positions() trade = Trade.query.filter(Trade.id == '2').first() amount = trade.amount # make an limit-buy open trade, if there is no 'filled', don't sell it mocker.patch('freqtrade.exchange.Exchange.get_order', return_value={ 'status': 'open', 'type': 'limit', 'side': 'buy', 'filled': None }) # check that the trade is called, which is done by ensuring exchange.cancel_order is called msg = rpc._rpc_forcesell('2') assert msg == {'result': 'Created sell order for trade 2.'} assert cancel_order_mock.call_count == 2 assert trade.amount == amount freqtradebot.enter_positions() # make an limit-sell open trade mocker.patch('freqtrade.exchange.Exchange.get_order', return_value={ 'status': 'open', 'type': 'limit', 'side': 'sell' }) msg = rpc._rpc_forcesell('3') assert msg == {'result': 'Created sell order for trade 3.'} # status quo, no exchange calls assert cancel_order_mock.call_count == 2
def test_rpc_balance_handle(default_conf, mocker, tickers): mock_balance = { 'BTC': { 'free': 10.0, 'total': 12.0, 'used': 2.0, }, 'ETH': { 'free': 1.0, 'total': 5.0, 'used': 4.0, }, 'USDT': { 'free': 5.0, 'total': 10.0, 'used': 5.0, } } mocker.patch.multiple( '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.multiple('freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=mock_balance), get_tickers=tickers, get_valid_pair_combination=MagicMock( side_effect=lambda a, b: f"{b}/{a}" if a == "USDT" else f"{a}/{b}")) default_conf['dry_run'] = False freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency']) assert prec_satoshi(result['total'], 12.309096315) assert prec_satoshi(result['value'], 184636.44472997) assert 'USD' == result['symbol'] assert result['currencies'] == [{ 'currency': 'BTC', 'free': 10.0, 'balance': 12.0, 'used': 2.0, 'est_stake': 12.0, 'stake': 'BTC', }, { 'free': 1.0, 'balance': 5.0, 'currency': 'ETH', 'est_stake': 0.30794, 'used': 4.0, 'stake': 'BTC', }, { 'free': 5.0, 'balance': 10.0, 'currency': 'USDT', 'est_stake': 0.0011563153318162476, 'used': 5.0, 'stake': 'BTC', }] assert result['total'] == 12.309096315331816
def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, limit_buy_order, limit_sell_order, mocker) -> None: mocker.patch.multiple( '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.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['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 # Create some test data freqtradebot.enter_positions() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) # Update the ticker with a market going up mocker.patch.multiple('freqtrade.exchange.Exchange', fetch_ticker=ticker_sell_up) trade.update(limit_sell_order) trade.close_date = datetime.utcnow() trade.is_open = False freqtradebot.enter_positions() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) # Update the ticker with a market going up mocker.patch.multiple('freqtrade.exchange.Exchange', fetch_ticker=ticker_sell_up) trade.update(limit_sell_order) trade.close_date = datetime.utcnow() trade.is_open = False stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05) assert prec_satoshi(stats['profit_closed_percent'], 6.2) assert prec_satoshi(stats['profit_closed_fiat'], 0.93255) assert prec_satoshi(stats['profit_all_coin'], 5.802e-05) assert prec_satoshi(stats['profit_all_percent'], 2.89) assert prec_satoshi(stats['profit_all_fiat'], 0.8703) assert stats['trade_count'] == 2 assert stats['first_trade_date'] == 'just now' assert stats['latest_trade_date'] == 'just now' assert stats['avg_duration'] == '0:00:00' assert stats['best_pair'] == 'ETH/BTC' assert prec_satoshi(stats['best_rate'], 6.2) # Test non-available pair mocker.patch( 'freqtrade.freqtradebot.FreqtradeBot.get_sell_rate', MagicMock( side_effect=DependencyException("Pair 'ETH/BTC' not available"))) stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) assert stats['trade_count'] == 2 assert stats['first_trade_date'] == 'just now' assert stats['latest_trade_date'] == 'just now' assert stats['avg_duration'] == '0:00:00' assert stats['best_pair'] == 'ETH/BTC' assert prec_satoshi(stats['best_rate'], 6.2) assert isnan(stats['profit_all_coin'])
def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_trade_status() freqtradebot.enter_positions() trades = Trade.get_open_trades() trades[0].open_order_id = None freqtradebot.exit_positions(trades) results = rpc._rpc_trade_status() assert results[0] == { 'trade_id': 1, 'pair': 'ETH/BTC', 'base_currency': 'BTC', 'open_date': ANY, 'open_date_hum': ANY, 'open_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_price': 0.0010025, 'close_rate_requested': ANY, 'sell_reason': ANY, 'sell_order_status': ANY, 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, 'ticker_interval': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, 'close_date_hum': None, 'close_timestamp': None, 'open_rate': 1.098e-05, 'close_rate': None, 'current_rate': 1.099e-05, 'amount': 91.07468124, 'stake_amount': 0.001, 'close_profit': None, 'close_profit_pct': None, 'close_profit_abs': None, 'current_profit': -0.00408133, 'current_profit_pct': -0.41, 'current_profit_abs': -4.09e-06, 'stop_loss': 9.882e-06, 'stop_loss_abs': 9.882e-06, 'stop_loss_pct': -10.0, 'stop_loss_ratio': -0.1, 'stoploss_order_id': None, 'stoploss_last_update': ANY, 'stoploss_last_update_timestamp': ANY, 'initial_stop_loss': 9.882e-06, 'initial_stop_loss_abs': 9.882e-06, 'initial_stop_loss_pct': -10.0, 'initial_stop_loss_ratio': -0.1, 'stoploss_current_dist': -1.1080000000000002e-06, 'stoploss_current_dist_ratio': -0.10081893, 'stoploss_entry_dist': -0.00010475, 'stoploss_entry_dist_ratio': -0.10448878, 'open_order': None, 'exchange': 'bittrex', } mocker.patch( 'freqtrade.freqtradebot.FreqtradeBot.get_sell_rate', MagicMock( side_effect=DependencyException("Pair 'ETH/BTC' not available"))) results = rpc._rpc_trade_status() assert isnan(results[0]['current_profit']) assert isnan(results[0]['current_rate']) assert results[0] == { 'trade_id': 1, 'pair': 'ETH/BTC', 'base_currency': 'BTC', 'open_date': ANY, 'open_date_hum': ANY, 'open_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_price': ANY, 'close_rate_requested': ANY, 'sell_reason': ANY, 'sell_order_status': ANY, 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, 'ticker_interval': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, 'close_date_hum': None, 'close_timestamp': None, 'open_rate': 1.098e-05, 'close_rate': None, 'current_rate': ANY, 'amount': 91.07468124, 'stake_amount': 0.001, 'close_profit': None, 'close_profit_pct': None, 'close_profit_abs': None, 'current_profit': ANY, 'current_profit_pct': ANY, 'current_profit_abs': ANY, 'stop_loss': 9.882e-06, 'stop_loss_abs': 9.882e-06, 'stop_loss_pct': -10.0, 'stop_loss_ratio': -0.1, 'stoploss_order_id': None, 'stoploss_last_update': ANY, 'stoploss_last_update_timestamp': ANY, 'initial_stop_loss': 9.882e-06, 'initial_stop_loss_abs': 9.882e-06, 'initial_stop_loss_pct': -10.0, 'initial_stop_loss_ratio': -0.1, 'stoploss_current_dist': ANY, 'stoploss_current_dist_ratio': ANY, 'stoploss_entry_dist': -0.00010475, 'stoploss_entry_dist_ratio': -0.10448878, 'open_order': None, 'exchange': 'bittrex', }
def test_send_msg_webhook(default_conf, mocker): default_conf["webhook"] = get_webhook_dict() msg_mock = MagicMock() mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf) # Test buy 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' } webhook.send_msg(msg=msg) assert msg_mock.call_count == 1 assert (msg_mock.call_args[0][0]["value1"] == default_conf["webhook"] ["webhookentry"]["value1"].format(**msg)) assert (msg_mock.call_args[0][0]["value2"] == default_conf["webhook"] ["webhookentry"]["value2"].format(**msg)) assert (msg_mock.call_args[0][0]["value3"] == default_conf["webhook"] ["webhookentry"]["value3"].format(**msg)) assert (msg_mock.call_args[0][0]["value4"] == default_conf["webhook"] ["webhookentry"]["value4"].format(**msg)) assert (msg_mock.call_args[0][0]["value5"] == default_conf["webhook"] ["webhookentry"]["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' } webhook.send_msg(msg=msg) assert msg_mock.call_count == 1 assert (msg_mock.call_args[0][0]["value1"] == default_conf["webhook"] ["webhookentry"]["value1"].format(**msg)) assert (msg_mock.call_args[0][0]["value2"] == default_conf["webhook"] ["webhookentry"]["value2"].format(**msg)) assert (msg_mock.call_args[0][0]["value3"] == default_conf["webhook"] ["webhookentry"]["value3"].format(**msg)) assert (msg_mock.call_args[0][0]["value4"] == default_conf["webhook"] ["webhookentry"]["value4"].format(**msg)) assert (msg_mock.call_args[0][0]["value5"] == default_conf["webhook"] ["webhookentry"]["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' } 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)) # 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' } 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)) # 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' } 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)) # 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' } 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)) # 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 } 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)) # 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 } 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)) # 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 } 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)) 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_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))
def test_send_msg(default_conf, mocker): default_conf["webhook"] = get_webhook_dict() msg_mock = MagicMock() mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf) # Test buy msg_mock = MagicMock() mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) msg = { 'type': RPCMessageType.BUY_NOTIFICATION, 'exchange': 'Bittrex', 'pair': 'ETH/BTC', '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"]["webhookbuy"]["value1"].format(**msg)) assert (msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookbuy"]["value2"].format(**msg)) assert (msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookbuy"]["value3"].format(**msg)) # Test buy cancel msg_mock = MagicMock() mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) msg = { 'type': RPCMessageType.BUY_CANCEL_NOTIFICATION, 'exchange': 'Bittrex', 'pair': 'ETH/BTC', '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"]["webhookbuycancel"]["value1"].format(**msg)) assert (msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhookbuycancel"]["value2"].format(**msg)) assert (msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhookbuycancel"]["value3"].format(**msg)) # Test sell msg_mock = MagicMock() mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) msg = { 'type': RPCMessageType.SELL_NOTIFICATION, 'exchange': 'Bittrex', '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': SellType.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"]["webhooksell"]["value1"].format(**msg)) assert (msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhooksell"]["value2"].format(**msg)) assert (msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhooksell"]["value3"].format(**msg)) # Test sell cancel msg_mock = MagicMock() mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) msg = { 'type': RPCMessageType.SELL_CANCEL_NOTIFICATION, 'exchange': 'Bittrex', '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': SellType.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"]["webhooksellcancel"]["value1"].format(**msg)) assert (msg_mock.call_args[0][0]["value2"] == default_conf["webhook"]["webhooksellcancel"]["value2"].format(**msg)) assert (msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhooksellcancel"]["value3"].format(**msg)) for msgtype in [RPCMessageType.STATUS_NOTIFICATION, RPCMessageType.WARNING_NOTIFICATION, RPCMessageType.STARTUP_NOTIFICATION]: # Test notification 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))
def test_rpc_balance_handle(default_conf, mocker): mock_balance = { 'BTC': { 'free': 10.0, 'total': 12.0, 'used': 2.0, }, 'ETH': { 'free': 1.0, 'total': 5.0, 'used': 4.0, }, 'PAX': { 'free': 5.0, 'total': 10.0, 'used': 5.0, } } mocker.patch.multiple( 'freqtrade.rpc.fiat_convert.Market', ticker=MagicMock(return_value={'price_usd': 15000.0}), ) patch_exchange(mocker) mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=mock_balance), get_ticker=MagicMock( side_effect=lambda p, r: {'bid': 100} if p == "BTC/PAX" else {'bid': 0.01}), get_valid_pair_combination=MagicMock( side_effect=lambda a, b: f"{b}/{a}" if a == "PAX" else f"{a}/{b}") ) freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() result = rpc._rpc_balance(default_conf['fiat_display_currency']) assert prec_satoshi(result['total'], 12.15) assert prec_satoshi(result['value'], 182250) assert 'USD' == result['symbol'] assert result['currencies'] == [ {'currency': 'BTC', 'free': 10.0, 'balance': 12.0, 'used': 2.0, 'est_btc': 12.0, }, {'free': 1.0, 'balance': 5.0, 'currency': 'ETH', 'est_btc': 0.05, 'used': 4.0 }, {'free': 5.0, 'balance': 10.0, 'currency': 'PAX', 'est_btc': 0.1, 'used': 5.0} ] assert result['total'] == 12.15
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 }}), ) mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, ) 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') freqtradebot.enter_positions() result, headers, fiat_profit_sum = rpc._rpc_status_table( default_conf['stake_currency'], 'USD') assert "Since" in headers assert "Pair" in headers assert 'instantly' == result[0][2] assert 'ETH/BTC' in result[0][1] assert '-0.41%' == result[0][3] assert isnan(fiat_profit_sum) # Test with fiatconvert rpc._fiat_converter = CryptoToFiatConverter() 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 'instantly' == 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') 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' mocker.patch( 'freqtrade.exchange.Exchange.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 'instantly' == result[0][2] assert 'ETH/BTC' in result[0][1] assert 'nan%' == result[0][3] assert isnan(fiat_profit_sum)
def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, limit_buy_order, limit_sell_order, markets, mocker) -> None: mocker.patch.multiple( 'freqtrade.rpc.fiat_convert.Market', ticker=MagicMock(return_value={'price_usd': 15000.0}), ) patch_exchange(mocker) mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() with pytest.raises(RPCException, match=r'.*no closed trade*'): rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) # Create some test data freqtradebot.create_trades() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) # Update the ticker with a market going up mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker_sell_up ) trade.update(limit_sell_order) trade.close_date = datetime.utcnow() trade.is_open = False freqtradebot.create_trades() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) # Update the ticker with a market going up mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker_sell_up ) trade.update(limit_sell_order) trade.close_date = datetime.utcnow() trade.is_open = False stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05) assert prec_satoshi(stats['profit_closed_percent'], 6.2) assert prec_satoshi(stats['profit_closed_fiat'], 0.93255) assert prec_satoshi(stats['profit_all_coin'], 5.632e-05) assert prec_satoshi(stats['profit_all_percent'], 2.81) assert prec_satoshi(stats['profit_all_fiat'], 0.8448) assert stats['trade_count'] == 2 assert stats['first_trade_date'] == 'just now' assert stats['latest_trade_date'] == 'just now' assert stats['avg_duration'] == '0:00:00' assert stats['best_pair'] == 'ETH/BTC' assert prec_satoshi(stats['best_rate'], 6.2) # Test non-available pair mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available"))) # invalidate ticker cache rpc._freqtrade.exchange._cached_ticker = {} stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) assert stats['trade_count'] == 2 assert stats['first_trade_date'] == 'just now' assert stats['latest_trade_date'] == 'just now' assert stats['avg_duration'] == '0:00:00' assert stats['best_pair'] == 'ETH/BTC' assert prec_satoshi(stats['best_rate'], 6.2) assert isnan(stats['profit_all_coin'])
def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_trade_status() freqtradebot.enter_positions() results = rpc._rpc_trade_status() assert { 'trade_id': 1, 'pair': 'ETH/BTC', 'base_currency': 'BTC', 'open_date': ANY, 'open_date_hum': ANY, 'is_open': ANY, 'fee_open': ANY, 'fee_close': ANY, 'open_rate_requested': ANY, 'open_trade_price': ANY, 'close_rate_requested': ANY, 'sell_reason': ANY, 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, 'ticker_interval': ANY, 'open_order_id': ANY, 'close_date': None, 'close_date_hum': None, 'open_rate': 1.098e-05, 'close_rate': None, 'current_rate': 1.099e-05, 'amount': 91.07468124, 'stake_amount': 0.001, 'close_profit': None, 'current_profit': -0.41, 'stop_loss': 0.0, 'initial_stop_loss': 0.0, 'initial_stop_loss_pct': None, 'stop_loss_pct': None, 'open_order': '(limit buy rem=0.00000000)' } == results[0] mocker.patch( 'freqtrade.freqtradebot.FreqtradeBot.get_sell_rate', MagicMock( side_effect=DependencyException(f"Pair 'ETH/BTC' not available"))) results = rpc._rpc_trade_status() assert isnan(results[0]['current_profit']) assert isnan(results[0]['current_rate']) assert { 'trade_id': 1, 'pair': 'ETH/BTC', 'base_currency': 'BTC', 'open_date': ANY, 'open_date_hum': ANY, 'is_open': ANY, 'fee_open': ANY, 'fee_close': ANY, 'open_rate_requested': ANY, 'open_trade_price': ANY, 'close_rate_requested': ANY, 'sell_reason': ANY, 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, 'ticker_interval': ANY, 'open_order_id': ANY, 'close_date': None, 'close_date_hum': None, 'open_rate': 1.098e-05, 'close_rate': None, 'current_rate': ANY, 'amount': 91.07468124, 'stake_amount': 0.001, 'close_profit': None, 'current_profit': ANY, 'stop_loss': 0.0, 'initial_stop_loss': 0.0, 'initial_stop_loss_pct': None, 'stop_loss_pct': None, 'open_order': '(limit buy rem=0.00000000)' } == results[0]
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": "******", "password": "******", } }) mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock()) server_mock = MagicMock() mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock) apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf) assert server_mock.call_count == 1 assert apiserver._config == default_conf apiserver.start_api() assert server_mock.call_count == 2 assert server_mock.call_args_list[0][0][0].host == "127.0.0.1" assert server_mock.call_args_list[0][0][0].port == 8080 assert isinstance(server_mock.call_args_list[0][0][0].app, FastAPI) assert log_has("Starting HTTP Server at 127.0.0.1:8080", caplog) assert log_has("Starting Local Rest Server.", 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.start_api() assert server_mock.call_count == 1 assert server_mock.call_args_list[0][0][0].host == "0.0.0.0" assert server_mock.call_args_list[0][0][0].port == 8089 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_re( "SECURITY WARNING - `jwt_secret_key` seems to be default.*", caplog) # Test crashing API server caplog.clear() mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', MagicMock(side_effect=Exception)) apiserver.start_api() assert log_has("Api server failed to start.", caplog)
def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_trade_status() freqtradebot.create_trades() results = rpc._rpc_trade_status() assert { 'trade_id': 1, 'pair': 'ETH/BTC', 'base_currency': 'BTC', 'open_date': ANY, 'open_date_hum': ANY, 'close_date': None, 'close_date_hum': None, 'open_rate': 1.099e-05, 'close_rate': None, 'current_rate': 1.098e-05, 'amount': 90.99181074, 'stake_amount': 0.001, 'close_profit': None, 'current_profit': -0.59, 'stop_loss': 0.0, 'initial_stop_loss': 0.0, 'initial_stop_loss_pct': None, 'stop_loss_pct': None, 'open_order': '(limit buy rem=0.00000000)' } == results[0] mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available"))) # invalidate ticker cache rpc._freqtrade.exchange._cached_ticker = {} results = rpc._rpc_trade_status() assert isnan(results[0]['current_profit']) assert isnan(results[0]['current_rate']) assert { 'trade_id': 1, 'pair': 'ETH/BTC', 'base_currency': 'BTC', 'open_date': ANY, 'open_date_hum': ANY, 'close_date': None, 'close_date_hum': None, 'open_rate': 1.099e-05, 'close_rate': None, 'current_rate': ANY, 'amount': 90.99181074, 'stake_amount': 0.001, 'close_profit': None, 'current_profit': ANY, 'stop_loss': 0.0, 'initial_stop_loss': 0.0, 'initial_stop_loss_pct': None, 'stop_loss_pct': None, 'open_order': '(limit buy rem=0.00000000)' } == results[0]
def test__init__(mocker, default_conf): 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