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%`
> **Trading volume:** `0.5 BTC`
> **Profit factor:** `1.04`
> **Win / Loss:** `102 / 36`
> **Winrate:** `73.91%`
> **Expectancy (Ratio):** `4.87 (1.66)`
> **Max Drawdown:** `9.23% (0.01255 BTC)`
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`.
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.
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)`.
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
winrate: float
expectancy: float
expectancy_rate: float
expectancy_ratio: float
max_drawdown: float
max_drawdown_abs: 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')
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
loserate = (100 - winrate)
expectancy, expectancy_rate = self.__calc_expectancy(mean_winning_profit,
mean_losing_profit,
winrate,
loserate)
expectancy, expectancy_ratio = self.__calc_expectancy(mean_winning_profit,
mean_losing_profit,
winrate,
loserate)
trades_df = DataFrame([{'close_date': trade.close_date.strftime(DATETIME_PRINT_FORMAT),
'profit_abs': trade.close_profit_abs}
@ -591,7 +591,7 @@ class RPC:
'profit_factor': profit_factor,
'winrate': winrate,
'expectancy': expectancy,
'expectancy_rate': expectancy_rate,
'expectancy_ratio': expectancy_ratio,
'max_drawdown': max_drawdown,
'max_drawdown_abs': max_drawdown_abs,
'trading_volume': trading_volume,
@ -629,20 +629,18 @@ class RPC:
self, mean_winning_profit: float, mean_losing_profit: float,
winrate: float, loserate: float) -> Tuple[float, float]:
expectancy = 1.0
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 = (
expectancy = (
((winrate / 100) * mean_winning_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:
""" Returns current account balance per crypto """

View File

@ -851,7 +851,7 @@ class Telegram(RPCHandler):
best_pair_profit_ratio = stats['best_pair_profit_ratio']
winrate = stats['winrate']
expectancy = stats['expectancy']
expectancy_rate = stats['expectancy_rate']
expectancy_ratio = stats['expectancy_ratio']
if stats['trade_count'] == 0:
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"*Win / Loss:* `{stats['winning_trades']} / {stats['losing_trades']}`\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:
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_timestamp'] == 0
assert res['expectancy'] == 0
assert res['expectancy_rate'] == 0
assert res['expectancy_ratio'] == float('inf')
# Create some test data
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_fiat']) == -85.205614098
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['first_trade_humanized'] == '2 days ago'
assert stats['latest_trade_humanized'] == '17 minutes ago'
assert stats['avg_duration'] in ('0:17:40')
assert stats['best_pair'] == 'XRP/USDT'
assert stats['best_rate'] == 10.0
assert stats['expectancy_rate'] == 3.64
assert stats['expectancy_ratio'] == 0.22330888345558253
# Test non-available pair
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_sum': -1.5, 'profit_closed_ratio': -6.739057628404269e-06,
'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,
'trading_volume': 91.074,
'profit_factor': 0.0, 'winrate': 0.0, 'expectancy': -0.0033695635,
'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_sum': 1.5, 'profit_closed_ratio': 7.391275897987988e-07,
'profit_closed_percent': 0.0, 'winning_trades': 2, 'losing_trades': 0,
'profit_factor': None, 'winrate': 100.0, 'expectancy': 1.0,
'expectancy_rate': 0.0003695635, 'trading_volume': 91.074,
'profit_factor': None, 'winrate': 100.0, 'expectancy': 0.0003695635,
'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_sum': 0.5, 'profit_closed_ratio': -5.429078808526421e-06,
'profit_closed_percent': -0.0, 'winning_trades': 1, 'losing_trades': 1,
'profit_factor': 0.02775724835771106, 'winrate': 50.0, 'expectancy': -0.48612137582114445,
'expectancy_rate': 0.0028695635, 'trading_volume': 91.074,
'profit_factor': 0.02775724835771106, 'winrate': 50.0,
'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'],
'winrate': expected['winrate'],
'expectancy': expected['expectancy'],
'expectancy_rate': expected['expectancy_rate'],
'expectancy_ratio': expected['expectancy_ratio'],
'max_drawdown': ANY,
'max_drawdown_abs': ANY,
'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 '*Profit factor:*' 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]