diff --git a/README.md b/README.md index 286591064..57000301f 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ use the `last` price and values between those interpolate between ask and last price. Using `ask` price will guarantee quick success in bid, but bot will also end up paying more then would probably have been necessary. -`fiat_currency` set the fiat to use for the conversion form coin to +`fiat_display_currency` set the fiat to use for the conversion form coin to fiat in Telegram. The valid value are: "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", diff --git a/freqtrade/main.py b/freqtrade/main.py index 4a88806c7..afbb84ec4 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -124,26 +124,39 @@ def execute_sell(trade: Trade, limit: float) -> None: fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2) profit_trade = trade.calc_profit(rate=limit) - fiat_converter = CryptoToFiatConverter() - profit_fiat = fiat_converter.convert_amount( - profit_trade, - _CONF['stake_currency'], - _CONF['fiat_display_currency'] - ) + message = '*{exchange}:* Selling [{pair}]({pair_url}) with limit `{limit:.8f}`'.format( + exchange=trade.exchange, + pair=trade.pair.replace('_', '/'), + pair_url=exchange.get_pair_detail_url(trade.pair), + limit=limit + ) - rpc.send_msg('*{exchange}:* Selling [{pair}]({pair_url}) with limit `{limit:.8f}`' - '` (profit: ~{profit_percent:.2f}%, {profit_coin:.8f} {coin}`' - '` / {profit_fiat:.3f} {fiat})`'.format( - exchange=trade.exchange, - pair=trade.pair.replace('_', '/'), - pair_url=exchange.get_pair_detail_url(trade.pair), - limit=limit, + # For regular case, when the configuration exists + if 'stake_currency' in _CONF and 'fiat_display_currency' in _CONF: + fiat_converter = CryptoToFiatConverter() + profit_fiat = fiat_converter.convert_amount( + profit_trade, + _CONF['stake_currency'], + _CONF['fiat_display_currency'] + ) + message += '` (profit: ~{profit_percent:.2f}%, {profit_coin:.8f} {coin}`' \ + '` / {profit_fiat:.3f} {fiat})`'.format( profit_percent=fmt_exp_profit, profit_coin=profit_trade, coin=_CONF['stake_currency'], profit_fiat=profit_fiat, fiat=_CONF['fiat_display_currency'], - )) + ) + # Because telegram._forcesell does not have the configuration + # Ignore the FIAT value and does not show the stake_currency as well + else: + message += '` (profit: ~{profit_percent:.2f}%, {profit_coin:.8f})`'.format( + profit_percent=fmt_exp_profit, + profit_coin=profit_trade + ) + + # Send the message + rpc.send_msg(message) Trade.session.flush() diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 553cd0253..b3b17459f 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -288,6 +288,7 @@ CONF_SCHEMA = { 'max_open_trades', 'stake_currency', 'stake_amount', + 'fiat_display_currency', 'dry_run', 'minimal_roi', 'bid_strategy', diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index 605ac084b..73a00d61b 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -11,7 +11,7 @@ from freqtrade import DependencyException, OperationalException from freqtrade.analyze import SignalType from freqtrade.exchange import Exchanges from freqtrade.main import create_trade, handle_trade, init, \ - get_target_bid, _process + get_target_bid, _process, execute_sell from freqtrade.misc import get_state, State from freqtrade.persistence import Trade @@ -331,3 +331,104 @@ def test_balance_fully_last_side(mocker): def test_balance_bigger_last_ask(mocker): mocker.patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}) assert get_target_bid({'ask': 5, 'last': 10}) == 5 + + +def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) + mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) + mocker.patch('freqtrade.rpc.init', MagicMock()) + rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock()) + mocker.patch.multiple('freqtrade.main.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker) + mocker.patch.multiple('freqtrade.fiat_convert.Pymarketcap', + ticker=MagicMock(return_value={'price_usd': 15000.0}), + _cache_symbols=MagicMock(return_value={'BTC': 1})) + init(default_conf, create_engine('sqlite://')) + + # Create some test data + create_trade(0.001) + + trade = Trade.query.first() + assert trade + + # Increase the price and sell it + mocker.patch.multiple('freqtrade.main.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker_sell_up) + + execute_sell(trade=trade, limit=ticker_sell_up()['bid']) + + assert rpc_mock.call_count == 2 + assert 'Selling [BTC/ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '0.00001172' in rpc_mock.call_args_list[-1][0][0] + assert 'profit: ~6.11%, 0.00006126' in rpc_mock.call_args_list[-1][0][0] + assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0] + + +def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) + mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) + mocker.patch('freqtrade.rpc.init', MagicMock()) + rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock()) + mocker.patch.multiple('freqtrade.rpc.telegram', + _CONF=default_conf, + init=MagicMock(), + send_msg=MagicMock()) + mocker.patch.multiple('freqtrade.main.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker) + mocker.patch.multiple('freqtrade.fiat_convert.Pymarketcap', + ticker=MagicMock(return_value={'price_usd': 15000.0}), + _cache_symbols=MagicMock(return_value={'BTC': 1})) + init(default_conf, create_engine('sqlite://')) + + # Create some test data + create_trade(0.001) + + trade = Trade.query.first() + assert trade + + # Decrease the price and sell it + mocker.patch.multiple('freqtrade.main.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker_sell_down) + + execute_sell(trade=trade, limit=ticker_sell_down()['bid']) + + assert rpc_mock.call_count == 2 + assert 'Selling [BTC/ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '0.00001044' in rpc_mock.call_args_list[-1][0][0] + assert 'profit: ~-5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0] + assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0] + + +def test_execute_sell_without_conf(default_conf, ticker, ticker_sell_up, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) + mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) + mocker.patch('freqtrade.rpc.init', MagicMock()) + rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock()) + mocker.patch.multiple('freqtrade.main.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker) + init(default_conf, create_engine('sqlite://')) + + # Create some test data + create_trade(0.001) + + trade = Trade.query.first() + assert trade + + # Increase the price and sell it + mocker.patch.multiple('freqtrade.main.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker_sell_up) + mocker.patch('freqtrade.main._CONF', {}) + + execute_sell(trade=trade, limit=ticker_sell_up()['bid']) + + assert rpc_mock.call_count == 2 + assert 'Selling [BTC/ETH]' in rpc_mock.call_args_list[-1][0][0] + assert '0.00001172' in rpc_mock.call_args_list[-1][0][0] + assert '(profit: ~6.11%, 0.00006126)' in rpc_mock.call_args_list[-1][0][0] + assert 'USD' not in rpc_mock.call_args_list[-1][0][0]