diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py
index a3c6656af..8ccf8bbef 100644
--- a/freqtrade/persistence/models.py
+++ b/freqtrade/persistence/models.py
@@ -850,7 +850,8 @@ class Trade(_DECL_BASE, LocalTrade):
.group_by(Trade.pair) \
.order_by(desc('profit_sum_abs')) \
.all()
- return [
+
+ response = [
{
'pair': pair,
'profit': profit,
@@ -859,6 +860,8 @@ class Trade(_DECL_BASE, LocalTrade):
}
for pair, profit, profit_abs, count in pair_rates
]
+ [x.update({'profit': round(x['profit'] * 100, 2)}) for x in response]
+ return response
@staticmethod
def get_buy_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
@@ -868,36 +871,31 @@ class Trade(_DECL_BASE, LocalTrade):
NOTE: Not supported in Backtesting.
"""
+ filters = [Trade.is_open.is_(False)]
if(pair is not None):
- tag_perf = Trade.query.with_entities(
- Trade.buy_tag,
- func.sum(Trade.close_profit).label('profit_sum'),
- func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
- func.count(Trade.pair).label('count')
- ).filter(Trade.is_open.is_(False))\
- .filter(Trade.pair == pair) \
- .order_by(desc('profit_sum_abs')) \
- .all()
- else:
- tag_perf = Trade.query.with_entities(
- Trade.buy_tag,
- func.sum(Trade.close_profit).label('profit_sum'),
- func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
- func.count(Trade.pair).label('count')
- ).filter(Trade.is_open.is_(False))\
- .group_by(Trade.buy_tag) \
- .order_by(desc('profit_sum_abs')) \
- .all()
+ filters.append(Trade.pair == pair)
- return [
+ buy_tag_perf = Trade.query.with_entities(
+ Trade.buy_tag,
+ func.sum(Trade.close_profit).label('profit_sum'),
+ func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
+ func.count(Trade.pair).label('count')
+ ).filter(*filters)\
+ .group_by(Trade.buy_tag) \
+ .order_by(desc('profit_sum_abs')) \
+ .all()
+
+ response = [
{
'buy_tag': buy_tag if buy_tag is not None else "Other",
'profit': profit,
'profit_abs': profit_abs,
'count': count
}
- for buy_tag, profit, profit_abs, count in tag_perf
+ for buy_tag, profit, profit_abs, count in buy_tag_perf
]
+ [x.update({'profit': round(x['profit'] * 100, 2)}) for x in response]
+ return response
@staticmethod
def get_sell_reason_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
@@ -906,36 +904,32 @@ class Trade(_DECL_BASE, LocalTrade):
Can either be average for all pairs or a specific pair provided
NOTE: Not supported in Backtesting.
"""
- if(pair is not None):
- tag_perf = Trade.query.with_entities(
- Trade.sell_reason,
- func.sum(Trade.close_profit).label('profit_sum'),
- func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
- func.count(Trade.pair).label('count')
- ).filter(Trade.is_open.is_(False))\
- .filter(Trade.pair == pair) \
- .order_by(desc('profit_sum_abs')) \
- .all()
- else:
- tag_perf = Trade.query.with_entities(
- Trade.sell_reason,
- func.sum(Trade.close_profit).label('profit_sum'),
- func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
- func.count(Trade.pair).label('count')
- ).filter(Trade.is_open.is_(False))\
- .group_by(Trade.sell_reason) \
- .order_by(desc('profit_sum_abs')) \
- .all()
- return [
+ filters = [Trade.is_open.is_(False)]
+ if(pair is not None):
+ filters.append(Trade.pair == pair)
+
+ sell_tag_perf = Trade.query.with_entities(
+ Trade.sell_reason,
+ func.sum(Trade.close_profit).label('profit_sum'),
+ func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
+ func.count(Trade.pair).label('count')
+ ).filter(*filters)\
+ .group_by(Trade.sell_reason) \
+ .order_by(desc('profit_sum_abs')) \
+ .all()
+
+ response = [
{
'sell_reason': sell_reason if sell_reason is not None else "Other",
'profit': profit,
'profit_abs': profit_abs,
'count': count
}
- for sell_reason, profit, profit_abs, count in tag_perf
+ for sell_reason, profit, profit_abs, count in sell_tag_perf
]
+ [x.update({'profit': round(x['profit'] * 100, 2)}) for x in response]
+ return response
@staticmethod
def get_mix_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
@@ -944,34 +938,25 @@ class Trade(_DECL_BASE, LocalTrade):
Can either be average for all pairs or a specific pair provided
NOTE: Not supported in Backtesting.
"""
- if(pair is not None):
- tag_perf = Trade.query.with_entities(
- Trade.id,
- Trade.buy_tag,
- Trade.sell_reason,
- func.sum(Trade.close_profit).label('profit_sum'),
- func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
- func.count(Trade.pair).label('count')
- ).filter(Trade.is_open.is_(False))\
- .filter(Trade.pair == pair) \
- .order_by(desc('profit_sum_abs')) \
- .all()
- else:
- tag_perf = Trade.query.with_entities(
- Trade.id,
- Trade.buy_tag,
- Trade.sell_reason,
- func.sum(Trade.close_profit).label('profit_sum'),
- func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
- func.count(Trade.pair).label('count')
- ).filter(Trade.is_open.is_(False))\
- .group_by(Trade.id) \
- .order_by(desc('profit_sum_abs')) \
- .all()
+ filters = [Trade.is_open.is_(False)]
+ if(pair is not None):
+ filters.append(Trade.pair == pair)
+
+ mix_tag_perf = Trade.query.with_entities(
+ Trade.id,
+ Trade.buy_tag,
+ Trade.sell_reason,
+ func.sum(Trade.close_profit).label('profit_sum'),
+ func.sum(Trade.close_profit_abs).label('profit_sum_abs'),
+ func.count(Trade.pair).label('count')
+ ).filter(*filters)\
+ .group_by(Trade.id) \
+ .order_by(desc('profit_sum_abs')) \
+ .all()
return_list: List[Dict] = []
- for id, buy_tag, sell_reason, profit, profit_abs, count in tag_perf:
+ for id, buy_tag, sell_reason, profit, profit_abs, count in mix_tag_perf:
buy_tag = buy_tag if buy_tag is not None else "Other"
sell_reason = sell_reason if sell_reason is not None else "Other"
@@ -993,6 +978,7 @@ class Trade(_DECL_BASE, LocalTrade):
'count': 1 + return_list[i]["count"]}
i += 1
+ [x.update({'profit': round(x['profit'] * 100, 2)}) for x in return_list]
return return_list
@staticmethod
diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py
index 4ef9213eb..42d502cd8 100644
--- a/freqtrade/rpc/rpc.py
+++ b/freqtrade/rpc/rpc.py
@@ -685,8 +685,7 @@ class RPC:
Shows a performance statistic from finished trades
"""
pair_rates = Trade.get_overall_performance()
- # Round and convert to %
- [x.update({'profit': round(x['profit'] * 100, 2)}) for x in pair_rates]
+
return pair_rates
def _rpc_buy_tag_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]:
@@ -695,8 +694,7 @@ class RPC:
Shows a performance statistic from finished trades
"""
buy_tags = Trade.get_buy_tag_performance(pair)
- # Round and convert to %
- [x.update({'profit': round(x['profit'] * 100, 2)}) for x in buy_tags]
+
return buy_tags
def _rpc_sell_reason_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]:
@@ -705,8 +703,7 @@ class RPC:
Shows a performance statistic from finished trades
"""
sell_reasons = Trade.get_sell_reason_performance(pair)
- # Round and convert to %
- [x.update({'profit': round(x['profit'] * 100, 2)}) for x in sell_reasons]
+
return sell_reasons
def _rpc_mix_tag_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]:
@@ -715,8 +712,7 @@ class RPC:
Shows a performance statistic from finished trades
"""
mix_tags = Trade.get_mix_tag_performance(pair)
- # Round and convert to %
- [x.update({'profit': round(x['profit'] * 100, 2)}) for x in mix_tags]
+
return mix_tags
def _rpc_count(self) -> Dict[str, float]:
diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py
index f8c923958..78805a456 100644
--- a/tests/rpc/test_rpc.py
+++ b/tests/rpc/test_rpc.py
@@ -827,6 +827,120 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
assert res[0]['count'] == 1
assert prec_satoshi(res[0]['profit'], 6.2)
+ # TEST FOR TRADES WITH NO BUY TAG
+ # TEST TRADE WITH ONE BUY_TAG AND OTHER TWO TRADES WITH THE SAME TAG
+ # TEST THE SAME FOR A PAIR
+
+
+def test_buy_tag_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
+ trade.update(limit_buy_order)
+
+ # Simulate fulfilled LIMIT_SELL order for trade
+ trade.update(limit_sell_order)
+
+ trade.close_date = datetime.utcnow()
+ trade.is_open = False
+ res = rpc._rpc_buy_tag_performance(None)
+ assert len(res) == 1
+ assert res[0]['pair'] == 'ETH/BTC'
+ assert res[0]['count'] == 1
+ assert prec_satoshi(res[0]['profit'], 6.2)
+
+ # TEST FOR TRADES WITH NO SELL REASON
+ # TEST TRADE WITH ONE SELL REASON AND OTHER TWO TRADES WITH THE SAME reason
+ # TEST THE SAME FOR A PAIR
+
+
+def test_sell_reason_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
+ trade.update(limit_buy_order)
+
+ # Simulate fulfilled LIMIT_SELL order for trade
+ trade.update(limit_sell_order)
+
+ trade.close_date = datetime.utcnow()
+ trade.is_open = False
+ res = rpc._rpc_sell_reason_performance(None)
+ assert len(res) == 1
+ assert res[0]['pair'] == 'ETH/BTC'
+ assert res[0]['count'] == 1
+ assert prec_satoshi(res[0]['profit'], 6.2)
+
+ # TEST FOR TRADES WITH NO TAGS
+ # TEST TRADE WITH ONE TAG MIX AND OTHER TWO TRADES WITH THE SAME TAG MIX
+ # TEST THE SAME FOR A PAIR
+
+
+def test_mix_tag_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
+ trade.update(limit_buy_order)
+
+ # Simulate fulfilled LIMIT_SELL order for trade
+ trade.update(limit_sell_order)
+
+ trade.close_date = datetime.utcnow()
+ trade.is_open = False
+ res = rpc._rpc_mix_tag_performance(None)
+ assert len(res) == 1
+ assert res[0]['pair'] == 'ETH/BTC'
+ assert res[0]['count'] == 1
+ assert prec_satoshi(res[0]['profit'], 6.2)
+
def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
index 01d6d92cf..306181eae 100644
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -978,6 +978,105 @@ def test_performance_handle(default_conf, update, ticker, fee,
assert 'Performance' in msg_mock.call_args_list[0][0][0]
assert 'ETH/BTC\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0]
+ # TEST FOR TRADES WITH NO BUY TAG
+ # TEST TRADE WITH ONE BUY_TAG AND OTHER TWO TRADES WITH THE SAME TAG
+ # TEST THE SAME FOR A PAIR
+
+
+def test_buy_tag_performance_handle(default_conf, update, ticker, fee,
+ limit_buy_order, limit_sell_order, mocker) -> None:
+ mocker.patch.multiple(
+ 'freqtrade.exchange.Exchange',
+ fetch_ticker=ticker,
+ get_fee=fee,
+ )
+ telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
+ patch_get_signal(freqtradebot)
+
+ # Create some test data
+ freqtradebot.enter_positions()
+ trade = Trade.query.first()
+ assert trade
+
+ # Simulate fulfilled LIMIT_BUY order for trade
+ trade.update(limit_buy_order)
+
+ # Simulate fulfilled LIMIT_SELL order for trade
+ trade.update(limit_sell_order)
+
+ trade.close_date = datetime.utcnow()
+ trade.is_open = False
+ telegram._buy_tag_performance(update=update, context=MagicMock())
+ assert msg_mock.call_count == 1
+ assert 'Performance' in msg_mock.call_args_list[0][0][0]
+ assert 'ETH/BTC\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0]
+
+ # TEST FOR TRADES WITH NO SELL REASON
+ # TEST TRADE WITH ONE SELL REASON AND OTHER TWO TRADES WITH THE SAME reason
+ # TEST THE SAME FOR A PAIR
+
+
+def test_sell_reason_performance_handle(default_conf, update, ticker, fee,
+ limit_buy_order, limit_sell_order, mocker) -> None:
+ mocker.patch.multiple(
+ 'freqtrade.exchange.Exchange',
+ fetch_ticker=ticker,
+ get_fee=fee,
+ )
+ telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
+ patch_get_signal(freqtradebot)
+
+ # Create some test data
+ freqtradebot.enter_positions()
+ trade = Trade.query.first()
+ assert trade
+
+ # Simulate fulfilled LIMIT_BUY order for trade
+ trade.update(limit_buy_order)
+
+ # Simulate fulfilled LIMIT_SELL order for trade
+ trade.update(limit_sell_order)
+
+ trade.close_date = datetime.utcnow()
+ trade.is_open = False
+ telegram._sell_reason_performance(update=update, context=MagicMock())
+ assert msg_mock.call_count == 1
+ assert 'Performance' in msg_mock.call_args_list[0][0][0]
+ assert 'ETH/BTC\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0]
+
+ # TEST FOR TRADES WITH NO TAGS
+ # TEST TRADE WITH ONE TAG MIX AND OTHER TWO TRADES WITH THE SAME TAG MIX
+ # TEST THE SAME FOR A PAIR
+
+
+def test_mix_tag_performance_handle(default_conf, update, ticker, fee,
+ limit_buy_order, limit_sell_order, mocker) -> None:
+ mocker.patch.multiple(
+ 'freqtrade.exchange.Exchange',
+ fetch_ticker=ticker,
+ get_fee=fee,
+ )
+ telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
+ patch_get_signal(freqtradebot)
+
+ # Create some test data
+ freqtradebot.enter_positions()
+ trade = Trade.query.first()
+ assert trade
+
+ # Simulate fulfilled LIMIT_BUY order for trade
+ trade.update(limit_buy_order)
+
+ # Simulate fulfilled LIMIT_SELL order for trade
+ trade.update(limit_sell_order)
+
+ trade.close_date = datetime.utcnow()
+ trade.is_open = False
+ telegram._mix_tag_performance(update=update, context=MagicMock())
+ assert msg_mock.call_count == 1
+ assert 'Performance' in msg_mock.call_args_list[0][0][0]
+ assert 'ETH/BTC\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0]
+
def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
mocker.patch.multiple(