From 325f72fd91c9c64ab2388786036b2ac88f674a97 Mon Sep 17 00:00:00 2001 From: gcarq Date: Sun, 5 Nov 2017 15:21:16 +0100 Subject: [PATCH 1/6] dry_run: keep list of open orders --- freqtrade/exchange/__init__.py | 45 ++++++++++++++++++++++--------- freqtrade/main.py | 5 ++-- freqtrade/tests/test_telegram.py | 46 +++++--------------------------- 3 files changed, 42 insertions(+), 54 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 4807ff295..080e06df5 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -1,5 +1,6 @@ import enum import logging +from random import randint from typing import List, Dict 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, Dict] = {} + 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.pop(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 7c89da55f..34d4cdbc7 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -201,12 +201,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/tests/test_telegram.py b/freqtrade/tests/test_telegram.py index 8ee830d92..8c45e2a83 100644 --- a/freqtrade/tests/test_telegram.py +++ b/freqtrade/tests/test_telegram.py @@ -6,6 +6,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 @@ -74,8 +75,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 @@ -84,26 +84,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] @@ -121,8 +105,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 @@ -153,7 +136,6 @@ 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() @@ -178,26 +160,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() @@ -206,7 +175,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.072561 (profit: ~-0.64%)' in msg_mock.call_args_list[-1][0][0] def test_performance_handle(conf, update, mocker): @@ -223,8 +192,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 From 95a17b8f984e538d921372e4050ec3abc737beaf Mon Sep 17 00:00:00 2001 From: gcarq Date: Sun, 5 Nov 2017 15:35:15 +0100 Subject: [PATCH 2/6] dry_run: remove mock value notice --- freqtrade/rpc/telegram.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 9f2559000..1dcf10fa9 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -200,7 +200,6 @@ def _profit(bot: Bot, update: Update) -> None: *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), @@ -210,8 +209,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) @@ -328,11 +325,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) From b82c4444b2c6bff6d2311ad00ae6127d0769bd09 Mon Sep 17 00:00:00 2001 From: gcarq Date: Sun, 5 Nov 2017 16:12:58 +0100 Subject: [PATCH 3/6] apply correct typehint --- freqtrade/exchange/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 080e06df5..637519166 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -1,7 +1,7 @@ import enum import logging from random import randint -from typing import List, Dict +from typing import List, Dict, Any import arrow @@ -15,7 +15,7 @@ _API: Exchange = None _CONF: dict = {} # Holds all open sell orders for dry_run -_DRY_RUN_OPEN_ORDERS: Dict[str, Dict] = {} +_DRY_RUN_OPEN_ORDERS: Dict[str, Any] = {} class Exchanges(enum.Enum): From 0a5eba64e2e588bedfc70f86258262181cc380e7 Mon Sep 17 00:00:00 2001 From: gcarq Date: Sun, 5 Nov 2017 16:13:20 +0100 Subject: [PATCH 4/6] do not remove order from dry_run order list --- freqtrade/exchange/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 637519166..11c3c3e23 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -135,7 +135,7 @@ def cancel_order(order_id: str) -> None: def get_order(order_id: str) -> Dict: if _CONF['dry_run']: - order = _DRY_RUN_OPEN_ORDERS.pop(order_id) + order = _DRY_RUN_OPEN_ORDERS[order_id] order.update({ 'id': order_id }) From 8fdd127f72310508b63b832cd6053e23475c27e1 Mon Sep 17 00:00:00 2001 From: gcarq Date: Sun, 5 Nov 2017 16:13:55 +0100 Subject: [PATCH 5/6] fix float precision rendering --- freqtrade/main.py | 4 ++-- freqtrade/rpc/telegram.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index 34d4cdbc7..46634a423 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -93,7 +93,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), @@ -189,7 +189,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), diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 1dcf10fa9..1759d6277 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -129,9 +129,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}` @@ -194,7 +194,7 @@ 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}` From 19f6ff330c1446e853ac717fabe4736e02b6770a Mon Sep 17 00:00:00 2001 From: gcarq Date: Sun, 5 Nov 2017 16:21:13 +0100 Subject: [PATCH 6/6] adapt float precision asserts --- freqtrade/tests/test_telegram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/test_telegram.py b/freqtrade/tests/test_telegram.py index 8c45e2a83..6ef929577 100644 --- a/freqtrade/tests/test_telegram.py +++ b/freqtrade/tests/test_telegram.py @@ -142,7 +142,7 @@ def test_profit_handle(conf, update, mocker): _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] @@ -175,7 +175,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.64%)' 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):