modify expectancy and expectancy ratio

This commit is contained in:
Stefano Ariestasia
2023-07-22 08:36:51 +09:00
parent 6874123974
commit c048e7229a
5 changed files with 57 additions and 28 deletions

View File

@@ -203,21 +203,48 @@ def calculate_expectancy(trades: pd.DataFrame) -> float:
if len(trades) == 0: if len(trades) == 0:
return 0 return 0
expectancy = 1 expectancy = 0
profit_sum = trades.loc[trades['profit_abs'] > 0, 'profit_abs'].sum() winning_trades = trades.loc[trades['profit_abs'] > 0]
loss_sum = abs(trades.loc[trades['profit_abs'] < 0, 'profit_abs'].sum()) losing_trades = trades.loc[trades['profit_abs'] < 0]
nb_win_trades = len(trades.loc[trades['profit_abs'] > 0]) profit_sum = winning_trades['profit_abs'].sum()
nb_loss_trades = len(trades.loc[trades['profit_abs'] < 0]) loss_sum = abs(losing_trades['profit_abs'].sum())
nb_win_trades = len(winning_trades)
nb_loss_trades = len(losing_trades)
if (nb_win_trades > 0) and (nb_loss_trades > 0): average_win = profit_sum / nb_win_trades
average_win = profit_sum / nb_win_trades average_loss = loss_sum / nb_loss_trades
average_loss = loss_sum / nb_loss_trades winrate = nb_win_trades / len(trades)
risk_reward_ratio = average_win / average_loss loserate = nb_loss_trades / len(trades)
winrate = nb_win_trades / len(trades) expectancy = (winrate * average_win) - (loserate * average_loss)
expectancy = ((1 + risk_reward_ratio) * winrate) - 1
elif nb_win_trades == 0: return expectancy
expectancy = 0
def calculate_expectancy_ratio(trades: pd.DataFrame) -> float:
"""
Calculate expectancy ratio
:param trades: DataFrame containing trades (requires columns close_date and profit_abs)
:return: expectancy ratio
"""
expectancy_ratio = float('inf')
if len(trades) > 0:
winning_trades = trades.loc[trades['profit_abs'] > 0]
losing_trades = trades.loc[trades['profit_abs'] < 0]
profit_sum = winning_trades['profit_abs'].sum()
loss_sum = abs(losing_trades['profit_abs'].sum())
nb_win_trades = len(winning_trades)
nb_loss_trades = len(losing_trades)
average_win = (profit_sum / nb_win_trades) if nb_win_trades > 0 else 0
average_loss = (loss_sum / nb_loss_trades) if nb_loss_trades > 0 else 0
if (average_loss > 0):
risk_reward_ratio = average_win / average_loss
winrate = nb_win_trades / len(trades)
expectancy = ((1 + risk_reward_ratio) * winrate) - 1
return expectancy return expectancy

View File

@@ -233,7 +233,7 @@ def text_table_add_metrics(strat_results: Dict) -> str:
('Calmar', f"{strat_results['calmar']:.2f}" if 'calmar' in strat_results else 'N/A'), ('Calmar', f"{strat_results['calmar']:.2f}" if 'calmar' in strat_results else 'N/A'),
('Profit factor', f'{strat_results["profit_factor"]:.2f}' if 'profit_factor' ('Profit factor', f'{strat_results["profit_factor"]:.2f}' if 'profit_factor'
in strat_results else 'N/A'), in strat_results else 'N/A'),
('Expectancy', f"{strat_results['expectancy']:.2f}" if 'expectancy' ('Expectancy Ratio', f"{strat_results['expectancy_ratio']:.2f}" if 'expectancy_ratio'
in strat_results else 'N/A'), in strat_results else 'N/A'),
('Trades per day', strat_results['trades_per_day']), ('Trades per day', strat_results['trades_per_day']),
('Avg. daily profit %', ('Avg. daily profit %',

View File

@@ -7,8 +7,9 @@ from pandas import DataFrame, concat, to_datetime
from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT, IntOrInf from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT, IntOrInf
from freqtrade.data.metrics import (calculate_cagr, calculate_calmar, calculate_csum, from freqtrade.data.metrics import (calculate_cagr, calculate_calmar, calculate_csum,
calculate_expectancy, calculate_market_change, calculate_expectancy, calculate_expectancy_ratio,
calculate_max_drawdown, calculate_sharpe, calculate_sortino) calculate_market_change, calculate_max_drawdown,
calculate_sharpe, calculate_sortino)
from freqtrade.misc import decimals_per_coin, round_coin_value from freqtrade.misc import decimals_per_coin, round_coin_value
@@ -414,7 +415,7 @@ def generate_strategy_stats(pairlist: List[str],
'profit_total_long_abs': results.loc[~results['is_short'], 'profit_abs'].sum(), 'profit_total_long_abs': results.loc[~results['is_short'], 'profit_abs'].sum(),
'profit_total_short_abs': results.loc[results['is_short'], 'profit_abs'].sum(), 'profit_total_short_abs': results.loc[results['is_short'], 'profit_abs'].sum(),
'cagr': calculate_cagr(backtest_days, start_balance, content['final_balance']), 'cagr': calculate_cagr(backtest_days, start_balance, content['final_balance']),
'expectancy': calculate_expectancy(results), 'expectancy_ratio': calculate_expectancy_ratio(results),
'sortino': calculate_sortino(results, min_date, max_date, start_balance), 'sortino': calculate_sortino(results, min_date, max_date, start_balance),
'sharpe': calculate_sharpe(results, min_date, max_date, start_balance), 'sharpe': calculate_sharpe(results, min_date, max_date, start_balance),
'calmar': calculate_calmar(results, min_date, max_date, start_balance), 'calmar': calculate_calmar(results, min_date, max_date, start_balance),

View File

@@ -18,7 +18,8 @@ from freqtrade import __version__
from freqtrade.configuration.timerange import TimeRange from freqtrade.configuration.timerange import TimeRange
from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT, Config from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT, Config
from freqtrade.data.history import load_data from freqtrade.data.history import load_data
from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.data.metrics import (calculate_expectancy, calculate_expectancy_ratio,
calculate_max_drawdown)
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, MarketDirection, SignalDirection, from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, MarketDirection, SignalDirection,
State, TradingMode) State, TradingMode)
from freqtrade.exceptions import ExchangeError, PricingError from freqtrade.exceptions import ExchangeError, PricingError

View File

@@ -13,10 +13,10 @@ from freqtrade.data.btanalysis import (BT_DATA_COLUMNS, analyze_trade_parallelis
load_backtest_metadata, load_trades, load_trades_from_db) load_backtest_metadata, load_trades, load_trades_from_db)
from freqtrade.data.history import load_data, load_pair_history from freqtrade.data.history import load_data, load_pair_history
from freqtrade.data.metrics import (calculate_cagr, calculate_calmar, calculate_csum, from freqtrade.data.metrics import (calculate_cagr, calculate_calmar, calculate_csum,
calculate_expectancy, calculate_market_change, calculate_expectancy, calculate_expectancy_ratio,
calculate_max_drawdown, calculate_sharpe, calculate_sortino, calculate_market_change, calculate_max_drawdown,
calculate_underwater, combine_dataframes_with_mean, calculate_sharpe, calculate_sortino, calculate_underwater,
create_cum_profit) combine_dataframes_with_mean, create_cum_profit)
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.util import dt_utc from freqtrade.util import dt_utc
from tests.conftest import CURRENT_TEST_STRATEGY, create_mock_trades from tests.conftest import CURRENT_TEST_STRATEGY, create_mock_trades
@@ -339,16 +339,16 @@ def test_calculate_csum(testdatadir):
csum_min, csum_max = calculate_csum(DataFrame()) csum_min, csum_max = calculate_csum(DataFrame())
def test_calculate_expectancy(testdatadir): def test_calculate_expectancy_ratio(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json" filename = testdatadir / "backtest_results/backtest-result.json"
bt_data = load_backtest_data(filename) bt_data = load_backtest_data(filename)
expectancy = calculate_expectancy(DataFrame()) expectancy_ratio = calculate_expectancy_ratio(DataFrame())
assert expectancy == 0.0 assert expectancy_ratio == 0.0
expectancy = calculate_expectancy(bt_data) expectancy_ratio = calculate_expectancy_ratio(bt_data)
assert isinstance(expectancy, float) assert isinstance(expectancy_ratio, float)
assert pytest.approx(expectancy) == 0.07151374226574791 assert pytest.approx(expectancy_ratio) == 0.07151374226574791
def test_calculate_sortino(testdatadir): def test_calculate_sortino(testdatadir):