Merge pull request #5879 from freqtrade/improve_pct_formatting

Improve pct formatting
This commit is contained in:
Matthias 2021-11-11 19:30:55 +01:00 committed by GitHub
commit 4d1ce51207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 63 additions and 60 deletions

View File

@ -113,7 +113,7 @@ def ohlcv_fill_up_missing_data(dataframe: DataFrame, timeframe: str, pair: str)
pct_missing = (len_after - len_before) / len_before if len_before > 0 else 0 pct_missing = (len_after - len_before) / len_before if len_before > 0 else 0
if len_before != len_after: if len_before != len_after:
message = (f"Missing data fillup for {pair}: before: {len_before} - after: {len_after}" message = (f"Missing data fillup for {pair}: before: {len_before} - after: {len_after}"
f" - {round(pct_missing * 100, 2)}%") f" - {pct_missing:.2%}")
if pct_missing > 0.01: if pct_missing > 0.01:
logger.info(message) logger.info(message)
else: else:

View File

@ -284,10 +284,10 @@ class HyperoptTools():
return (f"{results_metrics['total_trades']:6d} trades. " return (f"{results_metrics['total_trades']:6d} trades. "
f"{results_metrics['wins']}/{results_metrics['draws']}" f"{results_metrics['wins']}/{results_metrics['draws']}"
f"/{results_metrics['losses']} Wins/Draws/Losses. " f"/{results_metrics['losses']} Wins/Draws/Losses. "
f"Avg profit {results_metrics['profit_mean'] * 100: 6.2f}%. " f"Avg profit {results_metrics['profit_mean']:7.2%}. "
f"Median profit {results_metrics['profit_median'] * 100: 6.2f}%. " f"Median profit {results_metrics['profit_median']:7.2%}. "
f"Total profit {results_metrics['profit_total_abs']: 11.8f} {stake_currency} " f"Total profit {results_metrics['profit_total_abs']:11.8f} {stake_currency} "
f"({results_metrics['profit_total'] * 100: 7.2f}%). " f"({results_metrics['profit_total']:8.2%}). "
f"Avg duration {results_metrics['holding_avg']} min." f"Avg duration {results_metrics['holding_avg']} min."
) )

View File

@ -725,22 +725,22 @@ def text_table_add_metrics(strat_results: Dict) -> str:
strat_results['stake_currency'])), strat_results['stake_currency'])),
('Absolute profit ', round_coin_value(strat_results['profit_total_abs'], ('Absolute profit ', round_coin_value(strat_results['profit_total_abs'],
strat_results['stake_currency'])), strat_results['stake_currency'])),
('Total profit %', f"{round(strat_results['profit_total'] * 100, 2)}%"), ('Total profit %', f"{strat_results['profit_total']:.2%}"),
('Trades per day', strat_results['trades_per_day']), ('Trades per day', strat_results['trades_per_day']),
('Avg. daily profit %', ('Avg. daily profit %',
f"{round(strat_results['profit_total'] / strat_results['backtest_days'] * 100, 2)}%"), f"{(strat_results['profit_total'] / strat_results['backtest_days']):.2%}"),
('Avg. stake amount', round_coin_value(strat_results['avg_stake_amount'], ('Avg. stake amount', round_coin_value(strat_results['avg_stake_amount'],
strat_results['stake_currency'])), strat_results['stake_currency'])),
('Total trade volume', round_coin_value(strat_results['total_volume'], ('Total trade volume', round_coin_value(strat_results['total_volume'],
strat_results['stake_currency'])), strat_results['stake_currency'])),
('', ''), # Empty line to improve readability ('', ''), # Empty line to improve readability
('Best Pair', f"{strat_results['best_pair']['key']} " ('Best Pair', f"{strat_results['best_pair']['key']} "
f"{round(strat_results['best_pair']['profit_sum_pct'], 2)}%"), f"{strat_results['best_pair']['profit_sum']:.2%}"),
('Worst Pair', f"{strat_results['worst_pair']['key']} " ('Worst Pair', f"{strat_results['worst_pair']['key']} "
f"{round(strat_results['worst_pair']['profit_sum_pct'], 2)}%"), f"{strat_results['worst_pair']['profit_sum']:.2%}"),
('Best trade', f"{best_trade['pair']} {round(best_trade['profit_ratio'] * 100, 2)}%"), ('Best trade', f"{best_trade['pair']} {best_trade['profit_ratio']:.2%}"),
('Worst trade', f"{worst_trade['pair']} " ('Worst trade', f"{worst_trade['pair']} "
f"{round(worst_trade['profit_ratio'] * 100, 2)}%"), f"{worst_trade['profit_ratio']:.2%}"),
('Best day', round_coin_value(strat_results['backtest_best_day_abs'], ('Best day', round_coin_value(strat_results['backtest_best_day_abs'],
strat_results['stake_currency'])), strat_results['stake_currency'])),
@ -758,7 +758,7 @@ def text_table_add_metrics(strat_results: Dict) -> str:
('Max balance', round_coin_value(strat_results['csum_max'], ('Max balance', round_coin_value(strat_results['csum_max'],
strat_results['stake_currency'])), strat_results['stake_currency'])),
('Drawdown', f"{round(strat_results['max_drawdown'] * 100, 2)}%"), ('Drawdown', f"{strat_results['max_drawdown']:.2%}"),
('Drawdown', round_coin_value(strat_results['max_drawdown_abs'], ('Drawdown', round_coin_value(strat_results['max_drawdown_abs'],
strat_results['stake_currency'])), strat_results['stake_currency'])),
('Drawdown high', round_coin_value(strat_results['max_drawdown_high'], ('Drawdown high', round_coin_value(strat_results['max_drawdown_high'],
@ -767,7 +767,7 @@ def text_table_add_metrics(strat_results: Dict) -> str:
strat_results['stake_currency'])), strat_results['stake_currency'])),
('Drawdown Start', strat_results['drawdown_start']), ('Drawdown Start', strat_results['drawdown_start']),
('Drawdown End', strat_results['drawdown_end']), ('Drawdown End', strat_results['drawdown_end']),
('Market change', f"{round(strat_results['market_change'] * 100, 2)}%"), ('Market change', f"{strat_results['market_change']:.2%}"),
] ]
return tabulate(metrics, headers=["Metric", "Value"], tablefmt="orgtbl") return tabulate(metrics, headers=["Metric", "Value"], tablefmt="orgtbl")
@ -864,5 +864,5 @@ def show_sorted_pairlist(config: Dict, backtest_stats: Dict):
print(f"Pairs for Strategy {strategy}: \n[") print(f"Pairs for Strategy {strategy}: \n[")
for result in results['results_per_pair']: for result in results['results_per_pair']:
if result["key"] != 'TOTAL': if result["key"] != 'TOTAL':
print(f'"{result["key"]}", // {round(result["profit_mean_pct"], 2)}%') print(f'"{result["key"]}", // {result["profit_mean"]:.2%}')
print("]") print("]")

View File

@ -972,6 +972,7 @@ class Trade(_DECL_BASE, LocalTrade):
if not any(item["mix_tag"] == mix_tag for item in return_list): if not any(item["mix_tag"] == mix_tag for item in return_list):
return_list.append({'mix_tag': mix_tag, return_list.append({'mix_tag': mix_tag,
'profit': profit, 'profit': profit,
'profit_pct': round(profit * 100, 2),
'profit_abs': profit_abs, 'profit_abs': profit_abs,
'count': count}) 'count': count})
else: else:
@ -980,11 +981,11 @@ class Trade(_DECL_BASE, LocalTrade):
return_list[i] = { return_list[i] = {
'mix_tag': mix_tag, 'mix_tag': mix_tag,
'profit': profit + return_list[i]["profit"], 'profit': profit + return_list[i]["profit"],
'profit_pct': round(profit + return_list[i]["profit"] * 100, 2),
'profit_abs': profit_abs + return_list[i]["profit_abs"], 'profit_abs': profit_abs + return_list[i]["profit_abs"],
'count': 1 + return_list[i]["count"]} 'count': 1 + return_list[i]["count"]}
i += 1 i += 1
[x.update({'profit': round(x['profit'] * 100, 2)}) for x in return_list]
return return_list return return_list
@staticmethod @staticmethod

View File

@ -169,8 +169,8 @@ def add_max_drawdown(fig, row, trades: pd.DataFrame, df_comb: pd.DataFrame,
df_comb.loc[timeframe_to_prev_date(timeframe, lowdate), 'cum_profit'], df_comb.loc[timeframe_to_prev_date(timeframe, lowdate), 'cum_profit'],
], ],
mode='markers', mode='markers',
name=f"Max drawdown {max_drawdown * 100:.2f}%", name=f"Max drawdown {max_drawdown:.2%}",
text=f"Max drawdown {max_drawdown * 100:.2f}%", text=f"Max drawdown {max_drawdown:.2%}",
marker=dict( marker=dict(
symbol='square-open', symbol='square-open',
size=9, size=9,
@ -192,7 +192,7 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
# Trades can be empty # Trades can be empty
if trades is not None and len(trades) > 0: if trades is not None and len(trades) > 0:
# Create description for sell summarizing the trade # Create description for sell summarizing the trade
trades['desc'] = trades.apply(lambda row: f"{round(row['profit_ratio'] * 100, 1)}%, " trades['desc'] = trades.apply(lambda row: f"{row['profit_ratio']:.2%}, "
f"{row['sell_reason']}, " f"{row['sell_reason']}, "
f"{row['trade_duration']} min", f"{row['trade_duration']} min",
axis=1) axis=1)

View File

@ -50,7 +50,7 @@ class PriceFilter(IPairList):
""" """
active_price_filters = [] active_price_filters = []
if self._low_price_ratio != 0: if self._low_price_ratio != 0:
active_price_filters.append(f"below {self._low_price_ratio * 100}%") active_price_filters.append(f"below {self._low_price_ratio:.1%}")
if self._min_price != 0: if self._min_price != 0:
active_price_filters.append(f"below {self._min_price:.8f}") active_price_filters.append(f"below {self._min_price:.8f}")
if self._max_price != 0: if self._max_price != 0:
@ -82,7 +82,7 @@ class PriceFilter(IPairList):
changeperc = compare / ticker['last'] changeperc = compare / ticker['last']
if changeperc > self._low_price_ratio: if changeperc > self._low_price_ratio:
self.log_once(f"Removed {pair} from whitelist, " self.log_once(f"Removed {pair} from whitelist, "
f"because 1 unit is {changeperc * 100:.3f}%", logger.info) f"because 1 unit is {changeperc:.3%}", logger.info)
return False return False
# Perform low_amount check # Perform low_amount check

View File

@ -34,7 +34,7 @@ class SpreadFilter(IPairList):
Short whitelist method description - used for startup-messages Short whitelist method description - used for startup-messages
""" """
return (f"{self.name} - Filtering pairs with ask/bid diff above " return (f"{self.name} - Filtering pairs with ask/bid diff above "
f"{self._max_spread_ratio * 100}%.") f"{self._max_spread_ratio:.2%}.")
def _validate_pair(self, pair: str, ticker: Dict[str, Any]) -> bool: def _validate_pair(self, pair: str, ticker: Dict[str, Any]) -> bool:
""" """
@ -47,7 +47,7 @@ class SpreadFilter(IPairList):
spread = 1 - ticker['bid'] / ticker['ask'] spread = 1 - ticker['bid'] / ticker['ask']
if spread > self._max_spread_ratio: if spread > self._max_spread_ratio:
self.log_once(f"Removed {pair} from whitelist, because spread " self.log_once(f"Removed {pair} from whitelist, because spread "
f"{spread * 100:.3f}% > {self._max_spread_ratio * 100}%", f"{spread * 100:.3%} > {self._max_spread_ratio:.3%}",
logger.info) logger.info)
return False return False
else: else:

View File

@ -95,6 +95,7 @@ class Profit(BaseModel):
avg_duration: str avg_duration: str
best_pair: str best_pair: str
best_rate: float best_rate: float
best_pair_profit_ratio: float
winning_trades: int winning_trades: int
losing_trades: int losing_trades: int

View File

@ -224,9 +224,8 @@ class RPC:
trade.pair, refresh=False, side="sell") trade.pair, refresh=False, side="sell")
except (PricingError, ExchangeError): except (PricingError, ExchangeError):
current_rate = NAN current_rate = NAN
trade_percent = (100 * trade.calc_profit_ratio(current_rate))
trade_profit = trade.calc_profit(current_rate) trade_profit = trade.calc_profit(current_rate)
profit_str = f'{trade_percent:.2f}%' profit_str = f'{trade.calc_profit_ratio(current_rate):.2%}'
if self._fiat_converter: if self._fiat_converter:
fiat_profit = self._fiat_converter.convert_amount( fiat_profit = self._fiat_converter.convert_amount(
trade_profit, trade_profit,
@ -534,7 +533,8 @@ class RPC:
'latest_trade_timestamp': int(last_date.timestamp() * 1000) if last_date else 0, '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': best_pair[0] if best_pair else '', 'best_pair': best_pair[0] if best_pair else '',
'best_rate': round(best_pair[1] * 100, 2) if best_pair else 0, 'best_rate': round(best_pair[1] * 100, 2) if best_pair else 0, # Deprecated
'best_pair_profit_ratio': best_pair[1] if best_pair else 0,
'winning_trades': winning_trades, 'winning_trades': winning_trades,
'losing_trades': losing_trades, 'losing_trades': losing_trades,
} }

View File

@ -264,7 +264,7 @@ class Telegram(RPCHandler):
msg['profit_extra'] = '' msg['profit_extra'] = ''
message = ("{emoji} *{exchange}:* Selling {pair} (#{trade_id})\n" message = ("{emoji} *{exchange}:* Selling {pair} (#{trade_id})\n"
"*Profit:* `{profit_percent:.2f}%{profit_extra}`\n" "*Profit:* `{profit_ratio:.2%}{profit_extra}`\n"
"*Buy Tag:* `{buy_tag}`\n" "*Buy Tag:* `{buy_tag}`\n"
"*Sell Reason:* `{sell_reason}`\n" "*Sell Reason:* `{sell_reason}`\n"
"*Duration:* `{duration} ({duration_min:.1f} min)`\n" "*Duration:* `{duration} ({duration_min:.1f} min)`\n"
@ -397,19 +397,19 @@ class Telegram(RPCHandler):
"*Close Rate:* `{close_rate}`" if r['close_rate'] else "", "*Close Rate:* `{close_rate}`" if r['close_rate'] else "",
"*Current Rate:* `{current_rate:.8f}`", "*Current Rate:* `{current_rate:.8f}`",
("*Current Profit:* " if r['is_open'] else "*Close Profit: *") ("*Current Profit:* " if r['is_open'] else "*Close Profit: *")
+ "`{profit_pct:.2f}%`", + "`{profit_ratio:.2%}`",
] ]
if (r['stop_loss_abs'] != r['initial_stop_loss_abs'] if (r['stop_loss_abs'] != r['initial_stop_loss_abs']
and r['initial_stop_loss_pct'] is not None): and r['initial_stop_loss_ratio'] is not None):
# Adding initial stoploss only if it is different from stoploss # Adding initial stoploss only if it is different from stoploss
lines.append("*Initial Stoploss:* `{initial_stop_loss_abs:.8f}` " lines.append("*Initial Stoploss:* `{initial_stop_loss_abs:.8f}` "
"`({initial_stop_loss_pct:.2f}%)`") "`({initial_stop_loss_ratio:.2%})`")
# Adding stoploss and stoploss percentage only if it is not None # Adding stoploss and stoploss percentage only if it is not None
lines.append("*Stoploss:* `{stop_loss_abs:.8f}` " + lines.append("*Stoploss:* `{stop_loss_abs:.8f}` " +
("`({stop_loss_pct:.2f}%)`" if r['stop_loss_pct'] else "")) ("`({stop_loss_ratio:.2%})`" if r['stop_loss_ratio'] else ""))
lines.append("*Stoploss distance:* `{stoploss_current_dist:.8f}` " lines.append("*Stoploss distance:* `{stoploss_current_dist:.8f}` "
"`({stoploss_current_dist_pct:.2f}%)`") "`({stoploss_current_dist_ratio:.2%})`")
if r['open_order']: if r['open_order']:
if r['sell_order_status']: if r['sell_order_status']:
lines.append("*Open Order:* `{open_order}` - `{sell_order_status}`") lines.append("*Open Order:* `{open_order}` - `{sell_order_status}`")
@ -612,11 +612,11 @@ class Telegram(RPCHandler):
fiat_disp_cur, fiat_disp_cur,
start_date) start_date)
profit_closed_coin = stats['profit_closed_coin'] profit_closed_coin = stats['profit_closed_coin']
profit_closed_percent_mean = stats['profit_closed_percent_mean'] profit_closed_ratio_mean = stats['profit_closed_ratio_mean']
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_mean = stats['profit_all_percent_mean'] profit_all_ratio_mean = stats['profit_all_ratio_mean']
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']
@ -624,7 +624,7 @@ class Telegram(RPCHandler):
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_pair_profit_ratio = stats['best_pair_profit_ratio']
if stats['trade_count'] == 0: if stats['trade_count'] == 0:
markdown_msg = 'No trades yet.' markdown_msg = 'No trades yet.'
else: else:
@ -632,7 +632,7 @@ class Telegram(RPCHandler):
if stats['closed_trade_count'] > 0: if stats['closed_trade_count'] > 0:
markdown_msg = ("*ROI:* Closed trades\n" markdown_msg = ("*ROI:* Closed trades\n"
f"∙ `{round_coin_value(profit_closed_coin, stake_cur)} " f"∙ `{round_coin_value(profit_closed_coin, stake_cur)} "
f"({profit_closed_percent_mean:.2f}%) " f"({profit_closed_ratio_mean:.2%}) "
f"({profit_closed_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n" f"({profit_closed_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
f"∙ `{round_coin_value(profit_closed_fiat, fiat_disp_cur)}`\n") f"∙ `{round_coin_value(profit_closed_fiat, fiat_disp_cur)}`\n")
else: else:
@ -641,7 +641,7 @@ class Telegram(RPCHandler):
markdown_msg += ( markdown_msg += (
f"*ROI:* All trades\n" f"*ROI:* All trades\n"
f"∙ `{round_coin_value(profit_all_coin, stake_cur)} " f"∙ `{round_coin_value(profit_all_coin, stake_cur)} "
f"({profit_all_percent_mean:.2f}%) " f"({profit_all_ratio_mean:.2%}) "
f"({profit_all_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n" f"({profit_all_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n" f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n"
f"*Total Trade Count:* `{trade_count}`\n" f"*Total Trade Count:* `{trade_count}`\n"
@ -652,7 +652,7 @@ class Telegram(RPCHandler):
) )
if stats['closed_trade_count'] > 0: if stats['closed_trade_count'] > 0:
markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n" markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n"
f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`") f"*Best Performing:* `{best_pair}: {best_pair_profit_ratio:.2%}`")
self._send_msg(markdown_msg, reload_able=True, callback_path="update_profit", self._send_msg(markdown_msg, reload_able=True, callback_path="update_profit",
query=update.callback_query) query=update.callback_query)
@ -755,10 +755,10 @@ class Telegram(RPCHandler):
output += ("\n*Estimated Value*:\n" output += ("\n*Estimated Value*:\n"
f"\t`{result['stake']}: " f"\t`{result['stake']}: "
f"{round_coin_value(result['total'], result['stake'], False)}`" f"{round_coin_value(result['total'], result['stake'], False)}`"
f" `({result['starting_capital_pct']}%)`\n" f" `({result['starting_capital_ratio']:.2%})`\n"
f"\t`{result['symbol']}: " f"\t`{result['symbol']}: "
f"{round_coin_value(result['value'], result['symbol'], False)}`" f"{round_coin_value(result['value'], result['symbol'], False)}`"
f" `({result['starting_capital_fiat_pct']}%)`\n") f" `({result['starting_capital_fiat_ratio']:.2%})`\n")
self._send_msg(output, reload_able=True, callback_path="update_balance", self._send_msg(output, reload_able=True, callback_path="update_balance",
query=update.callback_query) query=update.callback_query)
except RPCException as e: except RPCException as e:
@ -893,7 +893,7 @@ class Telegram(RPCHandler):
trades_tab = tabulate( trades_tab = tabulate(
[[arrow.get(trade['close_date']).humanize(), [[arrow.get(trade['close_date']).humanize(),
trade['pair'] + " (#" + str(trade['trade_id']) + ")", trade['pair'] + " (#" + str(trade['trade_id']) + ")",
f"{(100 * trade['close_profit']):.2f}% ({trade['close_profit_abs']})"] f"{(trade['close_profit']):.2%} ({trade['close_profit_abs']})"]
for trade in trades['trades']], for trade in trades['trades']],
headers=[ headers=[
'Close Date', 'Close Date',
@ -945,7 +945,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_pct']:.2f}%) " f"({trade['profit_ratio']:.2%}) "
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:
@ -980,7 +980,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_pct']:.2f}%) " f"({trade['profit_ratio']:.2%}) "
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:
@ -1015,7 +1015,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_pct']:.2f}%) " f"({trade['profit_ratio']:.2%}) "
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:
@ -1050,7 +1050,7 @@ class Telegram(RPCHandler):
stat_line = ( stat_line = (
f"{i+1}.\t <code>{trade['mix_tag']}\t" f"{i+1}.\t <code>{trade['mix_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']:.2%}) "
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:

View File

@ -765,7 +765,7 @@ class IStrategy(ABC, HyperStrategyMixin):
if self.trailing_stop_positive is not None and high_profit > sl_offset: if self.trailing_stop_positive is not None and high_profit > sl_offset:
stop_loss_value = self.trailing_stop_positive stop_loss_value = self.trailing_stop_positive
logger.debug(f"{trade.pair} - Using positive stoploss: {stop_loss_value} " logger.debug(f"{trade.pair} - Using positive stoploss: {stop_loss_value} "
f"offset: {sl_offset:.4g} profit: {current_profit:.4f}%") f"offset: {sl_offset:.4g} profit: {current_profit:.2%}")
trade.adjust_stop_loss(high or current_rate, stop_loss_value) trade.adjust_stop_loss(high or current_rate, stop_loss_value)

View File

@ -1004,7 +1004,7 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
assert len(res) == 1 assert len(res) == 1
assert res[0]['mix_tag'] == 'Other Other' assert res[0]['mix_tag'] == 'Other 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 = "TESTBUY" trade.buy_tag = "TESTBUY"
trade.sell_reason = "TESTSELL" trade.sell_reason = "TESTSELL"
@ -1013,7 +1013,7 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
assert len(res) == 1 assert len(res) == 1
assert res[0]['mix_tag'] == 'TESTBUY TESTSELL' assert res[0]['mix_tag'] == 'TESTBUY TESTSELL'
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_mix_tag_performance_handle_2(mocker, default_conf, markets, fee): def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
@ -1032,10 +1032,10 @@ def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
assert len(res) == 2 assert len(res) == 2
assert res[0]['mix_tag'] == 'TEST1 sell_signal' assert res[0]['mix_tag'] == 'TEST1 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]['mix_tag'] == 'Other roi' assert res[1]['mix_tag'] == 'Other 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_mix_tag_performance('ETC/BTC') res = rpc._rpc_mix_tag_performance('ETC/BTC')
@ -1043,7 +1043,7 @@ def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
assert len(res) == 1 assert len(res) == 1
assert res[0]['count'] == 1 assert res[0]['count'] == 1
assert res[0]['mix_tag'] == 'TEST1 sell_signal' assert res[0]['mix_tag'] == 'TEST1 sell_signal'
assert prec_satoshi(res[0]['profit'], 0.5) assert prec_satoshi(res[0]['profit_pct'], 0.5)
def test_rpc_count(mocker, default_conf, ticker, fee) -> None: def test_rpc_count(mocker, default_conf, ticker, fee) -> None:

View File

@ -717,6 +717,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
assert rc.json() == {'avg_duration': ANY, assert rc.json() == {'avg_duration': ANY,
'best_pair': 'XRP/BTC', 'best_pair': 'XRP/BTC',
'best_rate': 1.0, 'best_rate': 1.0,
'best_pair_profit_ratio': 0.01,
'first_trade_date': ANY, 'first_trade_date': ANY,
'first_trade_timestamp': ANY, 'first_trade_timestamp': ANY,
'latest_trade_date': '5 minutes ago', 'latest_trade_date': '5 minutes ago',

View File

@ -189,16 +189,16 @@ def test_telegram_status(default_conf, update, mocker) -> None:
'amount': 90.99181074, 'amount': 90.99181074,
'stake_amount': 90.99181074, 'stake_amount': 90.99181074,
'buy_tag': None, 'buy_tag': None,
'close_profit_pct': None, 'close_profit_ratio': None,
'profit': -0.0059, 'profit': -0.0059,
'profit_pct': -0.59, 'profit_ratio': -0.0059,
'initial_stop_loss_abs': 1.098e-05, 'initial_stop_loss_abs': 1.098e-05,
'stop_loss_abs': 1.099e-05, 'stop_loss_abs': 1.099e-05,
'sell_order_status': None, 'sell_order_status': None,
'initial_stop_loss_pct': -0.05, 'initial_stop_loss_ratio': -0.0005,
'stoploss_current_dist': 1e-08, 'stoploss_current_dist': 1e-08,
'stoploss_current_dist_pct': -0.02, 'stoploss_current_dist_ratio': -0.0002,
'stop_loss_pct': -0.01, 'stop_loss_ratio': -0.0001,
'open_order': '(limit buy rem=0.00000000)', 'open_order': '(limit buy rem=0.00000000)',
'is_open': True 'is_open': True
}]), }]),

View File

@ -3352,7 +3352,7 @@ def test_trailing_stop_loss_positive(
) )
# stop-loss not reached, adjusted stoploss # stop-loss not reached, adjusted stoploss
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
caplog_text = f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 0.0249%" caplog_text = f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 2.49%"
if trail_if_reached: if trail_if_reached:
assert not log_has(caplog_text, caplog) assert not log_has(caplog_text, caplog)
assert not log_has("ETH/USDT - Adjusting stoploss...", caplog) assert not log_has("ETH/USDT - Adjusting stoploss...", caplog)
@ -3372,7 +3372,7 @@ def test_trailing_stop_loss_positive(
) )
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert log_has( assert log_has(
f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 0.0572%", f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 5.72%",
caplog caplog
) )
assert log_has("ETH/USDT - Adjusting stoploss...", caplog) assert log_has("ETH/USDT - Adjusting stoploss...", caplog)

View File

@ -171,7 +171,7 @@ def test_plot_trades(testdatadir, caplog):
assert len(trades) == len(trade_buy.x) assert len(trades) == len(trade_buy.x)
assert trade_buy.marker.color == 'cyan' assert trade_buy.marker.color == 'cyan'
assert trade_buy.marker.symbol == 'circle-open' assert trade_buy.marker.symbol == 'circle-open'
assert trade_buy.text[0] == '4.0%, roi, 15 min' assert trade_buy.text[0] == '3.99%, roi, 15 min'
trade_sell = find_trace_in_fig_data(figure.data, 'Sell - Profit') trade_sell = find_trace_in_fig_data(figure.data, 'Sell - Profit')
assert isinstance(trade_sell, go.Scatter) assert isinstance(trade_sell, go.Scatter)
@ -179,7 +179,7 @@ def test_plot_trades(testdatadir, caplog):
assert len(trades.loc[trades['profit_ratio'] > 0]) == len(trade_sell.x) assert len(trades.loc[trades['profit_ratio'] > 0]) == len(trade_sell.x)
assert trade_sell.marker.color == 'green' assert trade_sell.marker.color == 'green'
assert trade_sell.marker.symbol == 'square-open' assert trade_sell.marker.symbol == 'square-open'
assert trade_sell.text[0] == '4.0%, roi, 15 min' assert trade_sell.text[0] == '3.99%, roi, 15 min'
trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss') trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss')
assert isinstance(trade_sell_loss, go.Scatter) assert isinstance(trade_sell_loss, go.Scatter)
@ -187,7 +187,7 @@ def test_plot_trades(testdatadir, caplog):
assert len(trades.loc[trades['profit_ratio'] <= 0]) == len(trade_sell_loss.x) assert len(trades.loc[trades['profit_ratio'] <= 0]) == len(trade_sell_loss.x)
assert trade_sell_loss.marker.color == 'red' assert trade_sell_loss.marker.color == 'red'
assert trade_sell_loss.marker.symbol == 'square-open' assert trade_sell_loss.marker.symbol == 'square-open'
assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min' assert trade_sell_loss.text[5] == '-10.45%, stop_loss, 720 min'
def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog): def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog):