Fix calcs, rename ratio, add docs

This commit is contained in:
froggleston
2023-07-17 14:16:22 +01:00
parent 79f7f82c59
commit 6ccc12f337
7 changed files with 34 additions and 30 deletions

View File

@@ -287,12 +287,17 @@ Return a summary of your profit/loss and performance.
> **Best Performing:** `PAY/BTC: 50.23%` > **Best Performing:** `PAY/BTC: 50.23%`
> **Trading volume:** `0.5 BTC` > **Trading volume:** `0.5 BTC`
> **Profit factor:** `1.04` > **Profit factor:** `1.04`
> **Win / Loss:** `102 / 36`
> **Winrate:** `73.91%`
> **Expectancy (Ratio):** `4.87 (1.66)`
> **Max Drawdown:** `9.23% (0.01255 BTC)` > **Max Drawdown:** `9.23% (0.01255 BTC)`
The relative profit of `1.2%` is the average profit per trade. The relative profit of `1.2%` is the average profit per trade.
The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`. The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`.
Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits. Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits.
Profit Factor is calculated as gross profits / gross losses - and should serve as an overall metric for the strategy. Profit Factor is calculated as gross profits / gross losses - and should serve as an overall metric for the strategy.
Expectancy corresponds to the average return per currency unit at risk, i.e. the winrate and the risk-reward ratio (the average gain of winning trades compared to the average loss of losing trades).
Expectancy Ratio is expected profit or loss of a subsequent trade based on the performance of all past trades.
Max drawdown corresponds to the backtesting metric `Absolute Drawdown (Account)` - calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`. Max drawdown corresponds to the backtesting metric `Absolute Drawdown (Account)` - calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`.
Bot started date will refer to the date the bot was first started. For older bots, this will default to the first trade's open date. Bot started date will refer to the date the bot was first started. For older bots, this will default to the first trade's open date.

View File

@@ -138,7 +138,7 @@ class Profit(BaseModel):
profit_factor: float profit_factor: float
winrate: float winrate: float
expectancy: float expectancy: float
expectancy_rate: float expectancy_ratio: float
max_drawdown: float max_drawdown: float
max_drawdown_abs: float max_drawdown_abs: float
trading_volume: Optional[float] trading_volume: Optional[float]

View File

@@ -524,15 +524,15 @@ class RPC:
profit_factor = winning_profit / abs(losing_profit) if losing_profit else float('inf') profit_factor = winning_profit / abs(losing_profit) if losing_profit else float('inf')
mean_winning_profit = (winning_profit / winning_trades) if winning_trades > 0 else 0 mean_winning_profit = (winning_profit / winning_trades) if winning_trades > 0 else 0
mean_losing_profit = (losing_profit / losing_trades) if losing_trades > 0 else 0 mean_losing_profit = (abs(losing_profit) / losing_trades) if losing_trades > 0 else 0
winrate = (winning_trades / closed_trade_count) * 100 if closed_trade_count > 0 else 0 winrate = (winning_trades / closed_trade_count) * 100 if closed_trade_count > 0 else 0
loserate = (100 - winrate) loserate = (100 - winrate)
expectancy, expectancy_rate = self.__calc_expectancy(mean_winning_profit, expectancy, expectancy_ratio = self.__calc_expectancy(mean_winning_profit,
mean_losing_profit, mean_losing_profit,
winrate, winrate,
loserate) loserate)
trades_df = DataFrame([{'close_date': trade.close_date.strftime(DATETIME_PRINT_FORMAT), trades_df = DataFrame([{'close_date': trade.close_date.strftime(DATETIME_PRINT_FORMAT),
'profit_abs': trade.close_profit_abs} 'profit_abs': trade.close_profit_abs}
@@ -591,7 +591,7 @@ class RPC:
'profit_factor': profit_factor, 'profit_factor': profit_factor,
'winrate': winrate, 'winrate': winrate,
'expectancy': expectancy, 'expectancy': expectancy,
'expectancy_rate': expectancy_rate, 'expectancy_ratio': expectancy_ratio,
'max_drawdown': max_drawdown, 'max_drawdown': max_drawdown,
'max_drawdown_abs': max_drawdown_abs, 'max_drawdown_abs': max_drawdown_abs,
'trading_volume': trading_volume, 'trading_volume': trading_volume,
@@ -629,20 +629,18 @@ class RPC:
self, mean_winning_profit: float, mean_losing_profit: float, self, mean_winning_profit: float, mean_losing_profit: float,
winrate: float, loserate: float) -> Tuple[float, float]: winrate: float, loserate: float) -> Tuple[float, float]:
expectancy = 1.0 expectancy = (
if mean_winning_profit > 0 and abs(mean_losing_profit) > 0:
expectancy = (
(1 + (mean_winning_profit / abs(mean_losing_profit))) * (winrate / 100) - 1
)
elif mean_winning_profit == 0:
expectancy = 0.0
expectancy_rate = (
((winrate / 100) * mean_winning_profit) - ((winrate / 100) * mean_winning_profit) -
((loserate / 100) * mean_losing_profit) ((loserate / 100) * mean_losing_profit)
) )
return expectancy, expectancy_rate expectancy_ratio = float('inf')
if mean_losing_profit > 0:
expectancy_ratio = (
((1 + (mean_winning_profit / mean_losing_profit)) * (winrate / 100)) - 1
)
return expectancy, expectancy_ratio
def _rpc_balance(self, stake_currency: str, fiat_display_currency: str) -> Dict: def _rpc_balance(self, stake_currency: str, fiat_display_currency: str) -> Dict:
""" Returns current account balance per crypto """ """ Returns current account balance per crypto """

View File

@@ -851,7 +851,7 @@ class Telegram(RPCHandler):
best_pair_profit_ratio = stats['best_pair_profit_ratio'] best_pair_profit_ratio = stats['best_pair_profit_ratio']
winrate = stats['winrate'] winrate = stats['winrate']
expectancy = stats['expectancy'] expectancy = stats['expectancy']
expectancy_rate = stats['expectancy_rate'] expectancy_ratio = stats['expectancy_ratio']
if stats['trade_count'] == 0: if stats['trade_count'] == 0:
markdown_msg = f"No trades yet.\n*Bot started:* `{stats['bot_start_date']}`" markdown_msg = f"No trades yet.\n*Bot started:* `{stats['bot_start_date']}`"
@@ -879,7 +879,7 @@ class Telegram(RPCHandler):
f"*Latest Trade opened:* `{latest_trade_date}`\n" f"*Latest Trade opened:* `{latest_trade_date}`\n"
f"*Win / Loss:* `{stats['winning_trades']} / {stats['losing_trades']}`\n" f"*Win / Loss:* `{stats['winning_trades']} / {stats['losing_trades']}`\n"
f"*Winrate:* `{winrate:.2f}%`\n" f"*Winrate:* `{winrate:.2f}%`\n"
f"*Expectancy (Rate):* `{expectancy:.2f} ({expectancy_rate:.2f})`" f"*Expectancy (Ratio):* `{expectancy:.2f} ({expectancy_ratio:.2f})`"
) )
if stats['closed_trade_count'] > 0: if stats['closed_trade_count'] > 0:
markdown_msg += ( markdown_msg += (

View File

@@ -403,7 +403,7 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
assert res['latest_trade_date'] == '' assert res['latest_trade_date'] == ''
assert res['latest_trade_timestamp'] == 0 assert res['latest_trade_timestamp'] == 0
assert res['expectancy'] == 0 assert res['expectancy'] == 0
assert res['expectancy_rate'] == 0 assert res['expectancy_ratio'] == float('inf')
# Create some test data # Create some test data
create_mock_trades_usdt(fee) create_mock_trades_usdt(fee)
@@ -416,14 +416,14 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
assert pytest.approx(stats['profit_all_percent_mean']) == -57.86 assert pytest.approx(stats['profit_all_percent_mean']) == -57.86
assert pytest.approx(stats['profit_all_fiat']) == -85.205614098 assert pytest.approx(stats['profit_all_fiat']) == -85.205614098
assert pytest.approx(stats['winrate']) == 66.666666667 assert pytest.approx(stats['winrate']) == 66.666666667
assert pytest.approx(stats['expectancy']) == 0.223308883 assert pytest.approx(stats['expectancy']) == 0.9133333333333327
assert stats['trade_count'] == 7 assert stats['trade_count'] == 7
assert stats['first_trade_humanized'] == '2 days ago' assert stats['first_trade_humanized'] == '2 days ago'
assert stats['latest_trade_humanized'] == '17 minutes ago' assert stats['latest_trade_humanized'] == '17 minutes ago'
assert stats['avg_duration'] in ('0:17:40') assert stats['avg_duration'] in ('0:17:40')
assert stats['best_pair'] == 'XRP/USDT' assert stats['best_pair'] == 'XRP/USDT'
assert stats['best_rate'] == 10.0 assert stats['best_rate'] == 10.0
assert stats['expectancy_rate'] == 3.64 assert stats['expectancy_ratio'] == 0.22330888345558253
# Test non-available pair # Test non-available pair
mocker.patch(f'{EXMS}.get_rate', mocker.patch(f'{EXMS}.get_rate',

View File

@@ -829,8 +829,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
'profit_closed_percent_mean': -0.75, 'profit_closed_ratio_sum': -0.015, 'profit_closed_percent_mean': -0.75, 'profit_closed_ratio_sum': -0.015,
'profit_closed_percent_sum': -1.5, 'profit_closed_ratio': -6.739057628404269e-06, 'profit_closed_percent_sum': -1.5, 'profit_closed_ratio': -6.739057628404269e-06,
'profit_closed_percent': -0.0, 'winning_trades': 0, 'losing_trades': 2, 'profit_closed_percent': -0.0, 'winning_trades': 0, 'losing_trades': 2,
'profit_factor': 0.0, 'winrate': 0.0, 'expectancy': 0.0, 'expectancy_rate': 0.0033695635, 'profit_factor': 0.0, 'winrate': 0.0, 'expectancy': -0.0033695635,
'trading_volume': 91.074, 'expectancy_ratio': -1.0, 'trading_volume': 91.074,
} }
), ),
( (
@@ -845,8 +845,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
'profit_closed_percent_mean': 0.75, 'profit_closed_ratio_sum': 0.015, 'profit_closed_percent_mean': 0.75, 'profit_closed_ratio_sum': 0.015,
'profit_closed_percent_sum': 1.5, 'profit_closed_ratio': 7.391275897987988e-07, 'profit_closed_percent_sum': 1.5, 'profit_closed_ratio': 7.391275897987988e-07,
'profit_closed_percent': 0.0, 'winning_trades': 2, 'losing_trades': 0, 'profit_closed_percent': 0.0, 'winning_trades': 2, 'losing_trades': 0,
'profit_factor': None, 'winrate': 100.0, 'expectancy': 1.0, 'profit_factor': None, 'winrate': 100.0, 'expectancy': 0.0003695635,
'expectancy_rate': 0.0003695635, 'trading_volume': 91.074, 'expectancy_ratio': None, 'trading_volume': 91.074,
} }
), ),
( (
@@ -861,8 +861,9 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
'profit_closed_percent_mean': 0.25, 'profit_closed_ratio_sum': 0.005, 'profit_closed_percent_mean': 0.25, 'profit_closed_ratio_sum': 0.005,
'profit_closed_percent_sum': 0.5, 'profit_closed_ratio': -5.429078808526421e-06, 'profit_closed_percent_sum': 0.5, 'profit_closed_ratio': -5.429078808526421e-06,
'profit_closed_percent': -0.0, 'winning_trades': 1, 'losing_trades': 1, 'profit_closed_percent': -0.0, 'winning_trades': 1, 'losing_trades': 1,
'profit_factor': 0.02775724835771106, 'winrate': 50.0, 'expectancy': -0.48612137582114445, 'profit_factor': 0.02775724835771106, 'winrate': 50.0,
'expectancy_rate': 0.0028695635, 'trading_volume': 91.074, 'expectancy': -0.0027145635000000003, 'expectancy_ratio': -0.48612137582114445,
'trading_volume': 91.074,
} }
) )
]) ])
@@ -921,7 +922,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected)
'profit_factor': expected['profit_factor'], 'profit_factor': expected['profit_factor'],
'winrate': expected['winrate'], 'winrate': expected['winrate'],
'expectancy': expected['expectancy'], 'expectancy': expected['expectancy'],
'expectancy_rate': expected['expectancy_rate'], 'expectancy_ratio': expected['expectancy_ratio'],
'max_drawdown': ANY, 'max_drawdown': ANY,
'max_drawdown_abs': ANY, 'max_drawdown_abs': ANY,
'trading_volume': expected['trading_volume'], 'trading_volume': expected['trading_volume'],

View File

@@ -800,7 +800,7 @@ async def test_telegram_profit_handle(
assert '*Max Drawdown:*' in msg_mock.call_args_list[-1][0][0] assert '*Max Drawdown:*' in msg_mock.call_args_list[-1][0][0]
assert '*Profit factor:*' in msg_mock.call_args_list[-1][0][0] assert '*Profit factor:*' in msg_mock.call_args_list[-1][0][0]
assert '*Winrate:*' in msg_mock.call_args_list[-1][0][0] assert '*Winrate:*' in msg_mock.call_args_list[-1][0][0]
assert '*Expectancy (Rate):*' in msg_mock.call_args_list[-1][0][0] assert '*Expectancy (Ratio):*' in msg_mock.call_args_list[-1][0][0]
assert '*Trading volume:* `126 USDT`' in msg_mock.call_args_list[-1][0][0] assert '*Trading volume:* `126 USDT`' in msg_mock.call_args_list[-1][0][0]