mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
Fix calcs, rename ratio, add docs
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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 """
|
||||||
|
|||||||
@@ -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 += (
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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'],
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user