Merge pull request #3390 from freqtrade/rpc/profit

improve /profit to not raise an exception if no trade is closed
This commit is contained in:
hroff-1902 2020-05-30 22:06:22 +03:00 committed by GitHub
commit 84c50bf16c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 53 deletions

View File

@ -553,6 +553,7 @@ class Trade(_DECL_BASE):
def get_best_pair(): def get_best_pair():
""" """
Get best pair with closed trade. Get best pair with closed trade.
:returns: Tuple containing (pair, profit_sum)
""" """
best_pair = Trade.session.query( best_pair = Trade.session.query(
Trade.pair, func.sum(Trade.close_profit).label('profit_sum') Trade.pair, func.sum(Trade.close_profit).label('profit_sum')

View File

@ -281,11 +281,6 @@ class RPC:
best_pair = Trade.get_best_pair() best_pair = Trade.get_best_pair()
if not best_pair:
raise RPCException('no closed trade')
bp_pair, bp_rate = best_pair
# Prepare data to display # Prepare data to display
profit_closed_coin_sum = round(sum(profit_closed_coin), 8) profit_closed_coin_sum = round(sum(profit_closed_coin), 8)
profit_closed_percent = (round(mean(profit_closed_ratio) * 100, 2) if profit_closed_ratio profit_closed_percent = (round(mean(profit_closed_ratio) * 100, 2) if profit_closed_ratio
@ -304,6 +299,8 @@ class RPC:
fiat_display_currency fiat_display_currency
) if self._fiat_converter else 0 ) if self._fiat_converter else 0
first_date = trades[0].open_date if trades else None
last_date = trades[-1].open_date if trades else None
num = float(len(durations) or 1) num = float(len(durations) or 1)
return { return {
'profit_closed_coin': profit_closed_coin_sum, 'profit_closed_coin': profit_closed_coin_sum,
@ -313,13 +310,14 @@ class RPC:
'profit_all_percent': profit_all_percent, 'profit_all_percent': profit_all_percent,
'profit_all_fiat': profit_all_fiat, 'profit_all_fiat': profit_all_fiat,
'trade_count': len(trades), 'trade_count': len(trades),
'first_trade_date': arrow.get(trades[0].open_date).humanize(), 'closed_trade_count': len([t for t in trades if not t.is_open]),
'first_trade_timestamp': int(trades[0].open_date.timestamp() * 1000), 'first_trade_date': arrow.get(first_date).humanize() if first_date else '',
'latest_trade_date': arrow.get(trades[-1].open_date).humanize(), 'first_trade_timestamp': int(first_date.timestamp() * 1000) if first_date else 0,
'latest_trade_timestamp': int(trades[-1].open_date.timestamp() * 1000), 'latest_trade_date': arrow.get(last_date).humanize() if last_date else '',
'latest_trade_timestamp': int(last_date.timestamp() * 1000) if last_date else 0,
'avg_duration': str(timedelta(seconds=sum(durations) / num)).split('.')[0], 'avg_duration': str(timedelta(seconds=sum(durations) / num)).split('.')[0],
'best_pair': bp_pair, 'best_pair': best_pair[0] if best_pair else '',
'best_rate': round(bp_rate * 100, 2), 'best_rate': round(best_pair[1] * 100, 2) if best_pair else 0,
} }
def _rpc_balance(self, stake_currency: str, fiat_display_currency: str) -> Dict: def _rpc_balance(self, stake_currency: str, fiat_display_currency: str) -> Dict:

View File

@ -311,38 +311,43 @@ class Telegram(RPC):
stake_cur = self._config['stake_currency'] stake_cur = self._config['stake_currency']
fiat_disp_cur = self._config.get('fiat_display_currency', '') fiat_disp_cur = self._config.get('fiat_display_currency', '')
try: stats = self._rpc_trade_statistics(
stats = self._rpc_trade_statistics( stake_cur,
stake_cur, fiat_disp_cur)
fiat_disp_cur) profit_closed_coin = stats['profit_closed_coin']
profit_closed_coin = stats['profit_closed_coin'] profit_closed_percent = stats['profit_closed_percent']
profit_closed_percent = stats['profit_closed_percent'] profit_closed_fiat = stats['profit_closed_fiat']
profit_closed_fiat = stats['profit_closed_fiat'] profit_all_coin = stats['profit_all_coin']
profit_all_coin = stats['profit_all_coin'] profit_all_percent = stats['profit_all_percent']
profit_all_percent = stats['profit_all_percent'] profit_all_fiat = stats['profit_all_fiat']
profit_all_fiat = stats['profit_all_fiat'] trade_count = stats['trade_count']
trade_count = stats['trade_count'] first_trade_date = stats['first_trade_date']
first_trade_date = stats['first_trade_date'] latest_trade_date = stats['latest_trade_date']
latest_trade_date = stats['latest_trade_date'] avg_duration = stats['avg_duration']
avg_duration = stats['avg_duration'] best_pair = stats['best_pair']
best_pair = stats['best_pair'] best_rate = stats['best_rate']
best_rate = stats['best_rate'] if stats['trade_count'] == 0:
markdown_msg = 'No trades yet.'
else:
# Message to display # Message to display
markdown_msg = "*ROI:* Close trades\n" \ if stats['closed_trade_count'] > 0:
f"∙ `{profit_closed_coin:.8f} {stake_cur} "\ markdown_msg = ("*ROI:* Closed trades\n"
f"({profit_closed_percent:.2f}%)`\n" \ f"∙ `{profit_closed_coin:.8f} {stake_cur} "
f"∙ `{profit_closed_fiat:.3f} {fiat_disp_cur}`\n" \ f"({profit_closed_percent:.2f}%)`\n"
f"*ROI:* All trades\n" \ f"∙ `{profit_closed_fiat:.3f} {fiat_disp_cur}`\n")
f"∙ `{profit_all_coin:.8f} {stake_cur} ({profit_all_percent:.2f}%)`\n" \ else:
f"∙ `{profit_all_fiat:.3f} {fiat_disp_cur}`\n" \ markdown_msg = "`No closed trade` \n"
f"*Total Trade Count:* `{trade_count}`\n" \
f"*First Trade opened:* `{first_trade_date}`\n" \ markdown_msg += (f"*ROI:* All trades\n"
f"*Latest Trade opened:* `{latest_trade_date}`\n" \ f"∙ `{profit_all_coin:.8f} {stake_cur} ({profit_all_percent:.2f}%)`\n"
f"*Avg. Duration:* `{avg_duration}`\n" \ f"∙ `{profit_all_fiat:.3f} {fiat_disp_cur}`\n"
f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`" f"*Total Trade Count:* `{trade_count}`\n"
self._send_msg(markdown_msg) f"*First Trade opened:* `{first_trade_date}`\n"
except RPCException as e: f"*Latest Trade opened:* `{latest_trade_date}`")
self._send_msg(str(e)) if stats['closed_trade_count'] > 0:
markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n"
f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`")
self._send_msg(markdown_msg)
@authorized_only @authorized_only
def _balance(self, update: Update, context: CallbackContext) -> None: def _balance(self, update: Update, context: CallbackContext) -> None:

View File

@ -290,8 +290,12 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
rpc = RPC(freqtradebot) rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter() rpc._fiat_converter = CryptoToFiatConverter()
with pytest.raises(RPCException, match=r'.*no closed trade*'): res = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
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 # Create some test data
freqtradebot.enter_positions() freqtradebot.enter_positions()

View File

@ -396,9 +396,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li
) )
rc = client_get(client, f"{BASE_URI}/profit") rc = client_get(client, f"{BASE_URI}/profit")
assert_response(rc, 502) assert_response(rc, 200)
assert len(rc.json) == 1 assert rc.json['trade_count'] == 0
assert rc.json == {"error": "Error querying _profit: no closed trade"}
ftbot.enter_positions() ftbot.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
@ -406,8 +405,11 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) trade.update(limit_buy_order)
rc = client_get(client, f"{BASE_URI}/profit") rc = client_get(client, f"{BASE_URI}/profit")
assert_response(rc, 502) assert_response(rc, 200)
assert rc.json == {"error": "Error querying _profit: no closed trade"} # One open trade
assert rc.json['trade_count'] == 1
assert rc.json['best_pair'] == ''
assert rc.json['best_rate'] == 0
trade.update(limit_sell_order) trade.update(limit_sell_order)
@ -429,7 +431,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li
'profit_closed_coin': 6.217e-05, 'profit_closed_coin': 6.217e-05,
'profit_closed_fiat': 0, 'profit_closed_fiat': 0,
'profit_closed_percent': 6.2, 'profit_closed_percent': 6.2,
'trade_count': 1 'trade_count': 1,
'closed_trade_count': 1,
} }

View File

@ -420,7 +420,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
telegram._profit(update=update, context=MagicMock()) telegram._profit(update=update, context=MagicMock())
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert 'no closed trade' 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() msg_mock.reset_mock()
# Create some test data # Create some test data
@ -432,7 +432,9 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
telegram._profit(update=update, context=MagicMock()) telegram._profit(update=update, context=MagicMock())
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert 'no closed trade' 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]
assert '∙ `-0.00000500 BTC (-0.50%)`' in msg_mock.call_args_list[-1][0][0]
msg_mock.reset_mock() msg_mock.reset_mock()
# Update the ticker with a market going up # Update the ticker with a market going up
@ -444,7 +446,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
telegram._profit(update=update, context=MagicMock()) telegram._profit(update=update, context=MagicMock())
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert '*ROI:* Close trades' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0]
assert '∙ `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0] assert '∙ `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0]
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]
assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]