mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-01-28 18:00:23 +00:00
Merge pull request #11561 from mrpabloyeah/calculate-and-save-all-metrics-per-pair
Calculate and save all metrics per pair
This commit is contained in:
@@ -70,7 +70,11 @@ def generate_rejected_signals(
|
|||||||
|
|
||||||
|
|
||||||
def _generate_result_line(
|
def _generate_result_line(
|
||||||
result: DataFrame, starting_balance: float, first_column: str | list[str]
|
result: DataFrame,
|
||||||
|
min_date: datetime,
|
||||||
|
max_date: datetime,
|
||||||
|
starting_balance: float,
|
||||||
|
first_column: str | list[str],
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Generate one result dict, with "first_column" as key.
|
Generate one result dict, with "first_column" as key.
|
||||||
@@ -78,6 +82,20 @@ def _generate_result_line(
|
|||||||
profit_sum = result["profit_ratio"].sum()
|
profit_sum = result["profit_ratio"].sum()
|
||||||
# (end-capital - starting capital) / starting capital
|
# (end-capital - starting capital) / starting capital
|
||||||
profit_total = result["profit_abs"].sum() / starting_balance
|
profit_total = result["profit_abs"].sum() / starting_balance
|
||||||
|
backtest_days = (max_date - min_date).days or 1
|
||||||
|
final_balance = starting_balance + result["profit_abs"].sum()
|
||||||
|
expectancy, expectancy_ratio = calculate_expectancy(result)
|
||||||
|
winning_profit = result.loc[result["profit_abs"] > 0, "profit_abs"].sum()
|
||||||
|
losing_profit = result.loc[result["profit_abs"] < 0, "profit_abs"].sum()
|
||||||
|
profit_factor = winning_profit / abs(losing_profit) if losing_profit else 0.0
|
||||||
|
|
||||||
|
try:
|
||||||
|
drawdown = calculate_max_drawdown(
|
||||||
|
result, value_col="profit_abs", starting_balance=starting_balance
|
||||||
|
)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
drawdown = None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"key": first_column,
|
"key": first_column,
|
||||||
@@ -106,6 +124,16 @@ def _generate_result_line(
|
|||||||
"draws": len(result[result["profit_abs"] == 0]),
|
"draws": len(result[result["profit_abs"] == 0]),
|
||||||
"losses": len(result[result["profit_abs"] < 0]),
|
"losses": len(result[result["profit_abs"] < 0]),
|
||||||
"winrate": len(result[result["profit_abs"] > 0]) / len(result) if len(result) else 0.0,
|
"winrate": len(result[result["profit_abs"] > 0]) / len(result) if len(result) else 0.0,
|
||||||
|
"cagr": calculate_cagr(backtest_days, starting_balance, final_balance),
|
||||||
|
"expectancy": expectancy,
|
||||||
|
"expectancy_ratio": expectancy_ratio,
|
||||||
|
"sortino": calculate_sortino(result, min_date, max_date, starting_balance),
|
||||||
|
"sharpe": calculate_sharpe(result, min_date, max_date, starting_balance),
|
||||||
|
"calmar": calculate_calmar(result, min_date, max_date, starting_balance),
|
||||||
|
"sqn": calculate_sqn(result, starting_balance),
|
||||||
|
"profit_factor": profit_factor,
|
||||||
|
"max_drawdown_account": drawdown.relative_account_drawdown if drawdown else 0.0,
|
||||||
|
"max_drawdown_abs": drawdown.drawdown_abs if drawdown else 0.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -121,6 +149,8 @@ def generate_pair_metrics( #
|
|||||||
stake_currency: str,
|
stake_currency: str,
|
||||||
starting_balance: float,
|
starting_balance: float,
|
||||||
results: DataFrame,
|
results: DataFrame,
|
||||||
|
min_date: datetime,
|
||||||
|
max_date: datetime,
|
||||||
skip_nan: bool = False,
|
skip_nan: bool = False,
|
||||||
) -> list[dict]:
|
) -> list[dict]:
|
||||||
"""
|
"""
|
||||||
@@ -140,13 +170,18 @@ def generate_pair_metrics( #
|
|||||||
if skip_nan and result["profit_abs"].isnull().all():
|
if skip_nan and result["profit_abs"].isnull().all():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tabular_data.append(_generate_result_line(result, starting_balance, pair))
|
tabular_data.append(
|
||||||
|
_generate_result_line(result, min_date, max_date, starting_balance, pair)
|
||||||
|
)
|
||||||
|
|
||||||
# Sort by total profit %:
|
# Sort by total profit %:
|
||||||
tabular_data = sorted(tabular_data, key=lambda k: k["profit_total_abs"], reverse=True)
|
tabular_data = sorted(tabular_data, key=lambda k: k["profit_total_abs"], reverse=True)
|
||||||
|
|
||||||
# Append Total
|
# Append Total
|
||||||
tabular_data.append(_generate_result_line(results, starting_balance, "TOTAL"))
|
tabular_data.append(
|
||||||
|
_generate_result_line(results, min_date, max_date, starting_balance, "TOTAL")
|
||||||
|
)
|
||||||
|
|
||||||
return tabular_data
|
return tabular_data
|
||||||
|
|
||||||
|
|
||||||
@@ -154,6 +189,8 @@ def generate_tag_metrics(
|
|||||||
tag_type: Literal["enter_tag", "exit_reason"] | list[Literal["enter_tag", "exit_reason"]],
|
tag_type: Literal["enter_tag", "exit_reason"] | list[Literal["enter_tag", "exit_reason"]],
|
||||||
starting_balance: float,
|
starting_balance: float,
|
||||||
results: DataFrame,
|
results: DataFrame,
|
||||||
|
min_date: datetime,
|
||||||
|
max_date: datetime,
|
||||||
skip_nan: bool = False,
|
skip_nan: bool = False,
|
||||||
) -> list[dict]:
|
) -> list[dict]:
|
||||||
"""
|
"""
|
||||||
@@ -173,13 +210,17 @@ def generate_tag_metrics(
|
|||||||
if skip_nan and group["profit_abs"].isnull().all():
|
if skip_nan and group["profit_abs"].isnull().all():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tabular_data.append(_generate_result_line(group, starting_balance, tags))
|
tabular_data.append(
|
||||||
|
_generate_result_line(group, min_date, max_date, starting_balance, tags)
|
||||||
|
)
|
||||||
|
|
||||||
# Sort by total profit %:
|
# Sort by total profit %:
|
||||||
tabular_data = sorted(tabular_data, key=lambda k: k["profit_total_abs"], reverse=True)
|
tabular_data = sorted(tabular_data, key=lambda k: k["profit_total_abs"], reverse=True)
|
||||||
|
|
||||||
# Append Total
|
# Append Total
|
||||||
tabular_data.append(_generate_result_line(results, starting_balance, "TOTAL"))
|
tabular_data.append(
|
||||||
|
_generate_result_line(results, min_date, max_date, starting_balance, "TOTAL")
|
||||||
|
)
|
||||||
return tabular_data
|
return tabular_data
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
@@ -395,19 +436,33 @@ def generate_strategy_stats(
|
|||||||
stake_currency=stake_currency,
|
stake_currency=stake_currency,
|
||||||
starting_balance=start_balance,
|
starting_balance=start_balance,
|
||||||
results=results,
|
results=results,
|
||||||
|
min_date=min_date,
|
||||||
|
max_date=max_date,
|
||||||
skip_nan=False,
|
skip_nan=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
enter_tag_stats = generate_tag_metrics(
|
enter_tag_stats = generate_tag_metrics(
|
||||||
"enter_tag", starting_balance=start_balance, results=results, skip_nan=False
|
"enter_tag",
|
||||||
|
starting_balance=start_balance,
|
||||||
|
results=results,
|
||||||
|
min_date=min_date,
|
||||||
|
max_date=max_date,
|
||||||
|
skip_nan=False,
|
||||||
)
|
)
|
||||||
exit_reason_stats = generate_tag_metrics(
|
exit_reason_stats = generate_tag_metrics(
|
||||||
"exit_reason", starting_balance=start_balance, results=results, skip_nan=False
|
"exit_reason",
|
||||||
|
starting_balance=start_balance,
|
||||||
|
results=results,
|
||||||
|
min_date=min_date,
|
||||||
|
max_date=max_date,
|
||||||
|
skip_nan=False,
|
||||||
)
|
)
|
||||||
mix_tag_stats = generate_tag_metrics(
|
mix_tag_stats = generate_tag_metrics(
|
||||||
["enter_tag", "exit_reason"],
|
["enter_tag", "exit_reason"],
|
||||||
starting_balance=start_balance,
|
starting_balance=start_balance,
|
||||||
results=results,
|
results=results,
|
||||||
|
min_date=min_date,
|
||||||
|
max_date=max_date,
|
||||||
skip_nan=False,
|
skip_nan=False,
|
||||||
)
|
)
|
||||||
left_open_results = generate_pair_metrics(
|
left_open_results = generate_pair_metrics(
|
||||||
@@ -415,6 +470,8 @@ def generate_strategy_stats(
|
|||||||
stake_currency=stake_currency,
|
stake_currency=stake_currency,
|
||||||
starting_balance=start_balance,
|
starting_balance=start_balance,
|
||||||
results=results.loc[results["exit_reason"] == "force_exit"],
|
results=results.loc[results["exit_reason"] == "force_exit"],
|
||||||
|
min_date=min_date,
|
||||||
|
max_date=max_date,
|
||||||
skip_nan=True,
|
skip_nan=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -68,11 +68,21 @@ def test_text_table_bt_results(capsys):
|
|||||||
"profit_ratio": [0.1, 0.2, -0.05],
|
"profit_ratio": [0.1, 0.2, -0.05],
|
||||||
"profit_abs": [0.2, 0.4, -0.1],
|
"profit_abs": [0.2, 0.4, -0.1],
|
||||||
"trade_duration": [10, 30, 20],
|
"trade_duration": [10, 30, 20],
|
||||||
|
"close_date": [
|
||||||
|
dt_utc(2017, 11, 14, 21, 35, 00),
|
||||||
|
dt_utc(2017, 11, 14, 22, 10, 00),
|
||||||
|
dt_utc(2017, 11, 14, 22, 43, 00),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
pair_results = generate_pair_metrics(
|
pair_results = generate_pair_metrics(
|
||||||
["ETH/BTC"], stake_currency="BTC", starting_balance=4, results=results
|
["ETH/BTC"],
|
||||||
|
stake_currency="BTC",
|
||||||
|
starting_balance=4,
|
||||||
|
results=results,
|
||||||
|
min_date=dt_from_ts(1510688220),
|
||||||
|
max_date=dt_from_ts(1510700340),
|
||||||
)
|
)
|
||||||
text_table_bt_results(pair_results, stake_currency="BTC", title="title")
|
text_table_bt_results(pair_results, stake_currency="BTC", title="title")
|
||||||
text = capsys.readouterr().out
|
text = capsys.readouterr().out
|
||||||
@@ -420,6 +430,10 @@ def test_generate_pair_metrics():
|
|||||||
"profit_ratio": [0.1, 0.2],
|
"profit_ratio": [0.1, 0.2],
|
||||||
"profit_abs": [0.2, 0.4],
|
"profit_abs": [0.2, 0.4],
|
||||||
"trade_duration": [10, 30],
|
"trade_duration": [10, 30],
|
||||||
|
"close_date": [
|
||||||
|
dt_utc(2017, 11, 14, 21, 35, 00),
|
||||||
|
dt_utc(2017, 11, 14, 22, 10, 00),
|
||||||
|
],
|
||||||
"wins": [2, 0],
|
"wins": [2, 0],
|
||||||
"draws": [0, 0],
|
"draws": [0, 0],
|
||||||
"losses": [0, 0],
|
"losses": [0, 0],
|
||||||
@@ -427,7 +441,12 @@ def test_generate_pair_metrics():
|
|||||||
)
|
)
|
||||||
|
|
||||||
pair_results = generate_pair_metrics(
|
pair_results = generate_pair_metrics(
|
||||||
["ETH/BTC"], stake_currency="BTC", starting_balance=2, results=results
|
["ETH/BTC"],
|
||||||
|
stake_currency="BTC",
|
||||||
|
starting_balance=2,
|
||||||
|
results=results,
|
||||||
|
min_date=dt_from_ts(1510688220),
|
||||||
|
max_date=dt_from_ts(1510700340),
|
||||||
)
|
)
|
||||||
assert isinstance(pair_results, list)
|
assert isinstance(pair_results, list)
|
||||||
assert len(pair_results) == 2
|
assert len(pair_results) == 2
|
||||||
@@ -512,6 +531,11 @@ def test_text_table_exit_reason(capsys):
|
|||||||
"profit_ratio": [0.1, 0.2, -0.1],
|
"profit_ratio": [0.1, 0.2, -0.1],
|
||||||
"profit_abs": [0.2, 0.4, -0.2],
|
"profit_abs": [0.2, 0.4, -0.2],
|
||||||
"trade_duration": [10, 30, 10],
|
"trade_duration": [10, 30, 10],
|
||||||
|
"close_date": [
|
||||||
|
dt_utc(2017, 11, 14, 21, 35, 00),
|
||||||
|
dt_utc(2017, 11, 14, 22, 10, 00),
|
||||||
|
dt_utc(2017, 11, 14, 22, 43, 00),
|
||||||
|
],
|
||||||
"wins": [2, 0, 0],
|
"wins": [2, 0, 0],
|
||||||
"draws": [0, 0, 0],
|
"draws": [0, 0, 0],
|
||||||
"losses": [0, 0, 1],
|
"losses": [0, 0, 1],
|
||||||
@@ -520,7 +544,12 @@ def test_text_table_exit_reason(capsys):
|
|||||||
)
|
)
|
||||||
|
|
||||||
exit_reason_stats = generate_tag_metrics(
|
exit_reason_stats = generate_tag_metrics(
|
||||||
"exit_reason", starting_balance=22, results=results, skip_nan=False
|
"exit_reason",
|
||||||
|
starting_balance=22,
|
||||||
|
results=results,
|
||||||
|
min_date=dt_from_ts(1510688220),
|
||||||
|
max_date=dt_from_ts(1510700340),
|
||||||
|
skip_nan=False,
|
||||||
)
|
)
|
||||||
text_table_tags("exit_tag", exit_reason_stats, "BTC")
|
text_table_tags("exit_tag", exit_reason_stats, "BTC")
|
||||||
text = capsys.readouterr().out
|
text = capsys.readouterr().out
|
||||||
@@ -550,6 +579,11 @@ def test_generate_sell_reason_stats():
|
|||||||
"profit_ratio": [0.1, 0.2, -0.1],
|
"profit_ratio": [0.1, 0.2, -0.1],
|
||||||
"profit_abs": [0.2, 0.4, -0.2],
|
"profit_abs": [0.2, 0.4, -0.2],
|
||||||
"trade_duration": [10, 30, 10],
|
"trade_duration": [10, 30, 10],
|
||||||
|
"close_date": [
|
||||||
|
dt_utc(2017, 11, 14, 21, 35, 00),
|
||||||
|
dt_utc(2017, 11, 14, 22, 10, 00),
|
||||||
|
dt_utc(2017, 11, 14, 22, 43, 00),
|
||||||
|
],
|
||||||
"wins": [2, 0, 0],
|
"wins": [2, 0, 0],
|
||||||
"draws": [0, 0, 0],
|
"draws": [0, 0, 0],
|
||||||
"losses": [0, 0, 1],
|
"losses": [0, 0, 1],
|
||||||
@@ -558,7 +592,12 @@ def test_generate_sell_reason_stats():
|
|||||||
)
|
)
|
||||||
|
|
||||||
exit_reason_stats = generate_tag_metrics(
|
exit_reason_stats = generate_tag_metrics(
|
||||||
"exit_reason", starting_balance=22, results=results, skip_nan=False
|
"exit_reason",
|
||||||
|
starting_balance=22,
|
||||||
|
results=results,
|
||||||
|
min_date=dt_from_ts(1510688220),
|
||||||
|
max_date=dt_from_ts(1510700340),
|
||||||
|
skip_nan=False,
|
||||||
)
|
)
|
||||||
roi_result = exit_reason_stats[0]
|
roi_result = exit_reason_stats[0]
|
||||||
assert roi_result["key"] == "roi"
|
assert roi_result["key"] == "roi"
|
||||||
|
|||||||
Reference in New Issue
Block a user