diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index c0e390832..6205138da 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -1,6 +1,7 @@ import enum import logging -from typing import List, Dict +from random import randint +from typing import List, Dict, Any import arrow @@ -13,6 +14,9 @@ logger = logging.getLogger(__name__) _API: Exchange = None _CONF: dict = {} +# Holds all open sell orders for dry_run +_DRY_RUN_OPEN_ORDERS: Dict[str, Any] = {} + class Exchanges(enum.Enum): """ @@ -66,14 +70,36 @@ def validate_pairs(pairs: List[str]) -> None: def buy(pair: str, rate: float, amount: float) -> str: if _CONF['dry_run']: - return 'dry_run_buy' + global _DRY_RUN_OPEN_ORDERS + order_id = 'dry_run_buy_{}'.format(randint(0, 1e6)) + _DRY_RUN_OPEN_ORDERS[order_id] = { + 'pair': pair, + 'rate': rate, + 'amount': amount, + 'type': 'LIMIT_BUY', + 'remaining': 0.0, + 'opened': arrow.utcnow().datetime, + 'closed': arrow.utcnow().datetime, + } + return order_id return _API.buy(pair, rate, amount) def sell(pair: str, rate: float, amount: float) -> str: if _CONF['dry_run']: - return 'dry_run_sell' + global _DRY_RUN_OPEN_ORDERS + order_id = 'dry_run_sell_{}'.format(randint(0, 1e6)) + _DRY_RUN_OPEN_ORDERS[order_id] = { + 'pair': pair, + 'rate': rate, + 'amount': amount, + 'type': 'LIMIT_SELL', + 'remaining': 0.0, + 'opened': arrow.utcnow().datetime, + 'closed': arrow.utcnow().datetime, + } + return order_id return _API.sell(pair, rate, amount) @@ -109,16 +135,11 @@ def cancel_order(order_id: str) -> None: def get_order(order_id: str) -> Dict: if _CONF['dry_run']: - return { - 'id': 'dry_run_sell', - 'type': 'LIMIT_SELL', - 'pair': 'mocked', - 'opened': arrow.utcnow().datetime, - 'rate': 0.07256060, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': arrow.utcnow().datetime, - } + order = _DRY_RUN_OPEN_ORDERS[order_id] + order.update({ + 'id': order_id + }) + return order return _API.get_order(order_id) diff --git a/freqtrade/main.py b/freqtrade/main.py index b091ff1dd..d06204ef7 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -100,7 +100,7 @@ def execute_sell(trade: Trade, limit: float) -> None: trade.close_date = datetime.utcnow() fmt_exp_profit = round(trade.calc_profit(limit) * 100, 2) - message = '*{}:* Selling [{}]({}) with limit `{:f} (profit: ~{}%)`'.format( + message = '*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%)`'.format( trade.exchange, trade.pair.replace('_', '/'), exchange.get_pair_detail_url(trade.pair), @@ -196,7 +196,7 @@ def create_trade(stake_amount: float) -> Optional[Trade]: order_id = exchange.buy(pair, buy_limit, amount) # Create trade entity and return - message = '*{}:* Buying [{}]({}) with limit `{:f}`'.format( + message = '*{}:* Buying [{}]({}) with limit `{:.8f}`'.format( exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), @@ -208,12 +208,11 @@ def create_trade(stake_amount: float) -> Optional[Trade]: return Trade(pair=pair, stake_amount=stake_amount, amount=amount, - fee=fee * 2, + fee=fee*2, open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), - open_order_id=order_id, - is_open=True) + open_order_id=order_id) def init(config: dict, db_url: Optional[str] = None) -> None: diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index d2f23a86b..9a0a478f3 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -141,9 +141,9 @@ def _status(bot: Bot, update: Update) -> None: *Current Pair:* [{pair}]({market_url}) *Open Since:* `{date}` *Amount:* `{amount}` -*Open Rate:* `{open_rate}` +*Open Rate:* `{open_rate:.8f}` *Close Rate:* `{close_rate}` -*Current Rate:* `{current_rate}` +*Current Rate:* `{current_rate:.8f}` *Close Profit:* `{close_profit}` *Current Profit:* `{current_profit:.2f}%` *Open Order:* `{open_order}` @@ -243,13 +243,12 @@ def _profit(bot: Bot, update: Update) -> None: bp_pair, bp_rate = best_pair markdown_msg = """ -*ROI:* `{profit_btc:.6f} ({profit:.2f}%)` +*ROI:* `{profit_btc:.8f} ({profit:.2f}%)` *Trade Count:* `{trade_count}` *First Trade opened:* `{first_trade_date}` *Latest Trade opened:* `{latest_trade_date}` *Avg. Duration:* `{avg_duration}` *Best Performing:* `{best_pair}: {best_rate:.2f}%` -{dry_run_info} """.format( profit_btc=round(sum(profit_amounts), 8), profit=round(sum(profits) * 100, 2), @@ -259,8 +258,6 @@ def _profit(bot: Bot, update: Update) -> None: avg_duration=str(timedelta(seconds=sum(durations) / float(len(durations)))).split('.')[0], best_pair=bp_pair, best_rate=round(bp_rate * 100, 2), - dry_run_info='\n*NOTE:* These values are mocked because *dry_run* is enabled!' - if _CONF['dry_run'] else '' ) send_msg(markdown_msg, bot=bot) @@ -377,11 +374,7 @@ def _performance(bot: Bot, update: Update) -> None: profit=round(rate * 100, 2) ) for i, (pair, rate) in enumerate(pair_rates)) - message = 'Performance:\n{}\n{}'.format( - stats, - 'NOTE: These values are mocked because dry_run is enabled.' - if _CONF['dry_run'] else '' - ) + message = 'Performance:\n{}'.format(stats) logger.debug(message) send_msg(message, parse_mode=ParseMode.HTML) diff --git a/freqtrade/tests/test_telegram.py b/freqtrade/tests/test_telegram.py index 9e45aa39d..31f44ec55 100644 --- a/freqtrade/tests/test_telegram.py +++ b/freqtrade/tests/test_telegram.py @@ -8,6 +8,7 @@ import pytest from jsonschema import validate from telegram import Bot, Update, Message, Chat +from freqtrade import exchange from freqtrade.main import init, create_trade from freqtrade.misc import update_state, State, get_state, CONF_SCHEMA from freqtrade.persistence import Trade @@ -77,8 +78,7 @@ def test_status_handle(conf, update, mocker): 'bid': 0.07256061, 'ask': 0.072661, 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_order_id')) + })) init(conf, 'sqlite://') # Create some test data @@ -87,26 +87,10 @@ def test_status_handle(conf, update, mocker): Trade.session.add(trade) Trade.session.flush() - # Trigger status while we don't know the open_rate yet - _status(bot=MagicBot(), update=update) - - # Simulate fulfilled LIMIT_BUY order for trade - trade.update({ - 'id': 'mocked_limit_buy', - 'type': 'LIMIT_BUY', - 'pair': 'mocked', - 'opened': datetime.utcnow(), - 'rate': 0.07256060, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': datetime.utcnow(), - }) - Trade.session.flush() - # Trigger status while we have a fulfilled order for the open trade _status(bot=MagicBot(), update=update) - assert msg_mock.call_count == 3 + assert msg_mock.call_count == 2 assert '[BTC_ETH]' in msg_mock.call_args_list[-1][0][0] @@ -156,8 +140,7 @@ def test_profit_handle(conf, update, mocker): 'bid': 0.07256061, 'ask': 0.072661, 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_limit_buy')) + })) init(conf, 'sqlite://') # Create some test data @@ -188,14 +171,13 @@ def test_profit_handle(conf, update, mocker): }) trade.close_date = datetime.utcnow() - trade.open_order_id = None trade.is_open = False Trade.session.add(trade) Trade.session.flush() _profit(bot=MagicBot(), update=update) assert msg_mock.call_count == 2 - assert '*ROI:* `1.507013 (10.05%)`' in msg_mock.call_args_list[-1][0][0] + assert '*ROI:* `1.50701325 (10.05%)`' in msg_mock.call_args_list[-1][0][0] assert 'Best Performing:* `BTC_ETH: 10.05%`' in msg_mock.call_args_list[-1][0][0] @@ -213,26 +195,13 @@ def test_forcesell_handle(conf, update, mocker): 'bid': 0.07256061, 'ask': 0.072661, 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_order_id')) + })) init(conf, 'sqlite://') # Create some test data trade = create_trade(15.0) assert trade - # Simulate fulfilled LIMIT_BUY order for trade - trade.update({ - 'id': 'mocked_limit_buy', - 'type': 'LIMIT_BUY', - 'pair': 'mocked', - 'opened': datetime.utcnow(), - 'rate': 0.07256060, - 'amount': 206.43811673387373, - 'remaining': 0.0, - 'closed': datetime.utcnow(), - }) - Trade.session.add(trade) Trade.session.flush() @@ -241,7 +210,7 @@ def test_forcesell_handle(conf, update, mocker): assert msg_mock.call_count == 2 assert 'Selling [BTC/ETH]' in msg_mock.call_args_list[-1][0][0] - assert '0.072561 (profit: ~-0.5%)' in msg_mock.call_args_list[-1][0][0] + assert '0.07256061 (profit: ~-0.64%)' in msg_mock.call_args_list[-1][0][0] def test_performance_handle(conf, update, mocker): @@ -258,8 +227,7 @@ def test_performance_handle(conf, update, mocker): 'bid': 0.07256061, 'ask': 0.072661, 'last': 0.07256061 - }), - buy=MagicMock(return_value='mocked_order_id')) + })) init(conf, 'sqlite://') # Create some test data