Don't double-loop to generate profits
This commit is contained in:
parent
240923341b
commit
dffe76f109
@ -202,10 +202,10 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
msg = {
|
msg = {
|
||||||
'type': RPCMessageType.WARNING,
|
'type': RPCMessageType.WARNING,
|
||||||
'status': f"{len(open_trades)} open trades active.\n\n"
|
'status': f"{len(open_trades)} open trades active.\n\n"
|
||||||
f"Handle these trades manually on {self.exchange.name}, "
|
f"Handle these trades manually on {self.exchange.name}, "
|
||||||
f"or '/start' the bot again and use '/stopbuy' "
|
f"or '/start' the bot again and use '/stopbuy' "
|
||||||
f"to handle open trades gracefully. \n"
|
f"to handle open trades gracefully. \n"
|
||||||
f"{'Trades are simulated.' if self.config['dry_run'] else ''}",
|
f"{'Trades are simulated.' if self.config['dry_run'] else ''}",
|
||||||
}
|
}
|
||||||
self.rpc.send_msg(msg)
|
self.rpc.send_msg(msg)
|
||||||
|
|
||||||
|
@ -850,18 +850,17 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
.group_by(Trade.pair) \
|
.group_by(Trade.pair) \
|
||||||
.order_by(desc('profit_sum_abs')) \
|
.order_by(desc('profit_sum_abs')) \
|
||||||
.all()
|
.all()
|
||||||
|
return [
|
||||||
response = [
|
|
||||||
{
|
{
|
||||||
'pair': pair,
|
'pair': pair,
|
||||||
'profit': profit,
|
'profit_ratio': profit,
|
||||||
|
'profit': round(profit * 100, 2), # Compatibility mode
|
||||||
|
'profit_pct': round(profit * 100, 2),
|
||||||
'profit_abs': profit_abs,
|
'profit_abs': profit_abs,
|
||||||
'count': count
|
'count': count
|
||||||
}
|
}
|
||||||
for pair, profit, profit_abs, count in pair_rates
|
for pair, profit, profit_abs, count in pair_rates
|
||||||
]
|
]
|
||||||
[x.update({'profit': round(x['profit'] * 100, 2)}) for x in response]
|
|
||||||
return response
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_buy_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
|
def get_buy_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
|
||||||
@ -885,17 +884,16 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
.order_by(desc('profit_sum_abs')) \
|
.order_by(desc('profit_sum_abs')) \
|
||||||
.all()
|
.all()
|
||||||
|
|
||||||
response = [
|
return [
|
||||||
{
|
{
|
||||||
'buy_tag': buy_tag if buy_tag is not None else "Other",
|
'buy_tag': buy_tag if buy_tag is not None else "Other",
|
||||||
'profit': profit,
|
'profit_ratio': profit,
|
||||||
|
'profit_pct': round(profit * 100, 2),
|
||||||
'profit_abs': profit_abs,
|
'profit_abs': profit_abs,
|
||||||
'count': count
|
'count': count
|
||||||
}
|
}
|
||||||
for buy_tag, profit, profit_abs, count in buy_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
|
@staticmethod
|
||||||
def get_sell_reason_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
|
def get_sell_reason_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
|
||||||
@ -919,17 +917,16 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
.order_by(desc('profit_sum_abs')) \
|
.order_by(desc('profit_sum_abs')) \
|
||||||
.all()
|
.all()
|
||||||
|
|
||||||
response = [
|
return [
|
||||||
{
|
{
|
||||||
'sell_reason': sell_reason if sell_reason is not None else "Other",
|
'sell_reason': sell_reason if sell_reason is not None else "Other",
|
||||||
'profit': profit,
|
'profit_ratio': profit,
|
||||||
|
'profit_pct': round(profit * 100, 2),
|
||||||
'profit_abs': profit_abs,
|
'profit_abs': profit_abs,
|
||||||
'count': count
|
'count': count
|
||||||
}
|
}
|
||||||
for sell_reason, profit, profit_abs, count in sell_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
|
@staticmethod
|
||||||
def get_mix_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
|
def get_mix_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]:
|
||||||
|
@ -63,6 +63,8 @@ class Count(BaseModel):
|
|||||||
class PerformanceEntry(BaseModel):
|
class PerformanceEntry(BaseModel):
|
||||||
pair: str
|
pair: str
|
||||||
profit: float
|
profit: float
|
||||||
|
profit_ratio: float
|
||||||
|
profit_pct: float
|
||||||
profit_abs: float
|
profit_abs: float
|
||||||
count: int
|
count: int
|
||||||
|
|
||||||
|
@ -161,8 +161,6 @@ class RPC:
|
|||||||
current_rate = NAN
|
current_rate = NAN
|
||||||
else:
|
else:
|
||||||
current_rate = trade.close_rate
|
current_rate = trade.close_rate
|
||||||
|
|
||||||
buy_tag = trade.buy_tag
|
|
||||||
current_profit = trade.calc_profit_ratio(current_rate)
|
current_profit = trade.calc_profit_ratio(current_rate)
|
||||||
current_profit_abs = trade.calc_profit(current_rate)
|
current_profit_abs = trade.calc_profit(current_rate)
|
||||||
current_profit_fiat: Optional[float] = None
|
current_profit_fiat: Optional[float] = None
|
||||||
@ -193,7 +191,6 @@ class RPC:
|
|||||||
profit_pct=round(current_profit * 100, 2),
|
profit_pct=round(current_profit * 100, 2),
|
||||||
profit_abs=current_profit_abs,
|
profit_abs=current_profit_abs,
|
||||||
profit_fiat=current_profit_fiat,
|
profit_fiat=current_profit_fiat,
|
||||||
buy_tag=buy_tag,
|
|
||||||
|
|
||||||
stoploss_current_dist=stoploss_current_dist,
|
stoploss_current_dist=stoploss_current_dist,
|
||||||
stoploss_current_dist_ratio=round(stoploss_current_dist_ratio, 8),
|
stoploss_current_dist_ratio=round(stoploss_current_dist_ratio, 8),
|
||||||
|
@ -861,7 +861,7 @@ class Telegram(RPCHandler):
|
|||||||
stat_line = (
|
stat_line = (
|
||||||
f"{i+1}.\t <code>{trade['pair']}\t"
|
f"{i+1}.\t <code>{trade['pair']}\t"
|
||||||
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
|
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
|
||||||
f"({trade['profit']:.2f}%) "
|
f"({trade['profit_pct']:.2f}%) "
|
||||||
f"({trade['count']})</code>\n")
|
f"({trade['count']})</code>\n")
|
||||||
|
|
||||||
if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH:
|
if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH:
|
||||||
@ -896,7 +896,7 @@ class Telegram(RPCHandler):
|
|||||||
stat_line = (
|
stat_line = (
|
||||||
f"{i+1}.\t <code>{trade['buy_tag']}\t"
|
f"{i+1}.\t <code>{trade['buy_tag']}\t"
|
||||||
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
|
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
|
||||||
f"({trade['profit']:.2f}%) "
|
f"({trade['profit_pct']:.2f}%) "
|
||||||
f"({trade['count']})</code>\n")
|
f"({trade['count']})</code>\n")
|
||||||
|
|
||||||
if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH:
|
if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH:
|
||||||
@ -931,7 +931,7 @@ class Telegram(RPCHandler):
|
|||||||
stat_line = (
|
stat_line = (
|
||||||
f"{i+1}.\t <code>{trade['sell_reason']}\t"
|
f"{i+1}.\t <code>{trade['sell_reason']}\t"
|
||||||
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
|
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
|
||||||
f"({trade['profit']:.2f}%) "
|
f"({trade['profit_pct']:.2f}%) "
|
||||||
f"({trade['count']})</code>\n")
|
f"({trade['count']})</code>\n")
|
||||||
|
|
||||||
if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH:
|
if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH:
|
||||||
@ -1158,7 +1158,7 @@ class Telegram(RPCHandler):
|
|||||||
" `pending sell orders are marked with a double asterisk (**)`\n"
|
" `pending sell orders are marked with a double asterisk (**)`\n"
|
||||||
"*/buys <pair|none>:* `Shows the buy_tag performance`\n"
|
"*/buys <pair|none>:* `Shows the buy_tag performance`\n"
|
||||||
"*/sells <pair|none>:* `Shows the sell reason performance`\n"
|
"*/sells <pair|none>:* `Shows the sell reason performance`\n"
|
||||||
"*/mix_tag <pair|none>:* `Shows combined buy tag + sell reason performance`\n"
|
"*/mix_tags <pair|none>:* `Shows combined buy tag + sell reason performance`\n"
|
||||||
"*/trades [limit]:* `Lists last closed trades (limited to 10 by default)`\n"
|
"*/trades [limit]:* `Lists last closed trades (limited to 10 by default)`\n"
|
||||||
"*/profit [<n>]:* `Lists cumulative profit from all finished trades, "
|
"*/profit [<n>]:* `Lists cumulative profit from all finished trades, "
|
||||||
"over the last n days`\n"
|
"over the last n days`\n"
|
||||||
|
@ -822,11 +822,10 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||||||
trade.close_date = datetime.utcnow()
|
trade.close_date = datetime.utcnow()
|
||||||
trade.is_open = False
|
trade.is_open = False
|
||||||
res = rpc._rpc_performance()
|
res = rpc._rpc_performance()
|
||||||
print(str(res))
|
|
||||||
assert len(res) == 1
|
assert len(res) == 1
|
||||||
assert res[0]['pair'] == 'ETH/BTC'
|
assert res[0]['pair'] == 'ETH/BTC'
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||||
|
|
||||||
|
|
||||||
def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||||
@ -861,7 +860,7 @@ def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||||||
assert len(res) == 1
|
assert len(res) == 1
|
||||||
assert res[0]['buy_tag'] == 'Other'
|
assert res[0]['buy_tag'] == 'Other'
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||||
|
|
||||||
trade.buy_tag = "TEST_TAG"
|
trade.buy_tag = "TEST_TAG"
|
||||||
res = rpc._rpc_buy_tag_performance(None)
|
res = rpc._rpc_buy_tag_performance(None)
|
||||||
@ -869,7 +868,7 @@ def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||||||
assert len(res) == 1
|
assert len(res) == 1
|
||||||
assert res[0]['buy_tag'] == 'TEST_TAG'
|
assert res[0]['buy_tag'] == 'TEST_TAG'
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||||
|
|
||||||
|
|
||||||
def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
||||||
@ -888,17 +887,17 @@ def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
|||||||
assert len(res) == 2
|
assert len(res) == 2
|
||||||
assert res[0]['buy_tag'] == 'TEST1'
|
assert res[0]['buy_tag'] == 'TEST1'
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert prec_satoshi(res[0]['profit'], 0.5)
|
assert prec_satoshi(res[0]['profit_pct'], 0.5)
|
||||||
assert res[1]['buy_tag'] == 'Other'
|
assert res[1]['buy_tag'] == 'Other'
|
||||||
assert res[1]['count'] == 1
|
assert res[1]['count'] == 1
|
||||||
assert prec_satoshi(res[1]['profit'], 1.0)
|
assert prec_satoshi(res[1]['profit_pct'], 1.0)
|
||||||
|
|
||||||
# Test for a specific pair
|
# Test for a specific pair
|
||||||
res = rpc._rpc_buy_tag_performance('ETC/BTC')
|
res = rpc._rpc_buy_tag_performance('ETC/BTC')
|
||||||
assert len(res) == 1
|
assert len(res) == 1
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert res[0]['buy_tag'] == 'TEST1'
|
assert res[0]['buy_tag'] == 'TEST1'
|
||||||
assert prec_satoshi(res[0]['profit'], 0.5)
|
assert prec_satoshi(res[0]['profit_pct'], 0.5)
|
||||||
|
|
||||||
|
|
||||||
def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||||
@ -933,7 +932,7 @@ def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, f
|
|||||||
assert len(res) == 1
|
assert len(res) == 1
|
||||||
assert res[0]['sell_reason'] == 'Other'
|
assert res[0]['sell_reason'] == 'Other'
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||||
|
|
||||||
trade.sell_reason = "TEST1"
|
trade.sell_reason = "TEST1"
|
||||||
res = rpc._rpc_sell_reason_performance(None)
|
res = rpc._rpc_sell_reason_performance(None)
|
||||||
@ -941,7 +940,7 @@ def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, f
|
|||||||
assert len(res) == 1
|
assert len(res) == 1
|
||||||
assert res[0]['sell_reason'] == 'TEST1'
|
assert res[0]['sell_reason'] == 'TEST1'
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||||
|
|
||||||
|
|
||||||
def test_sell_reason_performance_handle_2(mocker, default_conf, markets, fee):
|
def test_sell_reason_performance_handle_2(mocker, default_conf, markets, fee):
|
||||||
@ -960,17 +959,17 @@ def test_sell_reason_performance_handle_2(mocker, default_conf, markets, fee):
|
|||||||
assert len(res) == 2
|
assert len(res) == 2
|
||||||
assert res[0]['sell_reason'] == 'sell_signal'
|
assert res[0]['sell_reason'] == 'sell_signal'
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert prec_satoshi(res[0]['profit'], 0.5)
|
assert prec_satoshi(res[0]['profit_pct'], 0.5)
|
||||||
assert res[1]['sell_reason'] == 'roi'
|
assert res[1]['sell_reason'] == 'roi'
|
||||||
assert res[1]['count'] == 1
|
assert res[1]['count'] == 1
|
||||||
assert prec_satoshi(res[1]['profit'], 1.0)
|
assert prec_satoshi(res[1]['profit_pct'], 1.0)
|
||||||
|
|
||||||
# Test for a specific pair
|
# Test for a specific pair
|
||||||
res = rpc._rpc_sell_reason_performance('ETC/BTC')
|
res = rpc._rpc_sell_reason_performance('ETC/BTC')
|
||||||
assert len(res) == 1
|
assert len(res) == 1
|
||||||
assert res[0]['count'] == 1
|
assert res[0]['count'] == 1
|
||||||
assert res[0]['sell_reason'] == 'sell_signal'
|
assert res[0]['sell_reason'] == 'sell_signal'
|
||||||
assert prec_satoshi(res[0]['profit'], 0.5)
|
assert prec_satoshi(res[0]['profit_pct'], 0.5)
|
||||||
|
|
||||||
|
|
||||||
def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||||
|
@ -812,8 +812,10 @@ def test_api_performance(botclient, fee):
|
|||||||
rc = client_get(client, f"{BASE_URI}/performance")
|
rc = client_get(client, f"{BASE_URI}/performance")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert len(rc.json()) == 2
|
assert len(rc.json()) == 2
|
||||||
assert rc.json() == [{'count': 1, 'pair': 'LTC/ETH', 'profit': 7.61, 'profit_abs': 0.01872279},
|
assert rc.json() == [{'count': 1, 'pair': 'LTC/ETH', 'profit': 7.61, 'profit_pct': 7.61,
|
||||||
{'count': 1, 'pair': 'XRP/ETH', 'profit': -5.57, 'profit_abs': -0.1150375}]
|
'profit_ratio': 0.07609203, 'profit_abs': 0.01872279},
|
||||||
|
{'count': 1, 'pair': 'XRP/ETH', 'profit': -5.57, 'profit_pct': -5.57,
|
||||||
|
'profit_ratio': -0.05570419, 'profit_abs': -0.1150375}]
|
||||||
|
|
||||||
|
|
||||||
def test_api_status(botclient, mocker, ticker, fee, markets):
|
def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||||
|
Loading…
Reference in New Issue
Block a user