Update metrics.py

This commit is contained in:
Stefano Ariestasia 2022-11-28 08:56:49 +09:00
parent 320535a227
commit f410b1b14d

View File

@ -1,9 +1,9 @@
import logging import logging
from typing import Dict, Tuple from typing import Dict, Tuple
from datetime import datetime
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import math
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -190,3 +190,128 @@ def calculate_cagr(days_passed: int, starting_balance: float, final_balance: flo
:return: CAGR :return: CAGR
""" """
return (final_balance / starting_balance) ** (1 / (days_passed / 365)) - 1 return (final_balance / starting_balance) ** (1 / (days_passed / 365)) - 1
def calculate_expectancy(trades: pd.DataFrame) -> float:
"""
Calculate expectancy
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
:return: expectancy
"""
if len(trades) == 0:
return 0
expectancy = 1
profit_sum = trades.loc[trades['profit_abs'] > 0, 'profit_abs'].sum()
loss_sum = abs(trades.loc[trades['profit_abs'] < 0, 'profit_abs'].sum())
nb_win_trades = len(trades.loc[trades['profit_abs'] > 0])
nb_loss_trades = len(trades.loc[trades['profit_abs'] < 0])
if (nb_win_trades > 0) and (nb_loss_trades > 0):
average_win = profit_sum / nb_win_trades
average_loss = loss_sum / nb_loss_trades
risk_reward_ratio = average_win / average_loss
winrate = nb_win_trades / len(trades)
expectancy = ((1 + risk_reward_ratio) * winrate) - 1
elif nb_win_trades == 0:
expectancy = 0
return expectancy
def calculate_sortino(trades: pd.DataFrame,
min_date: datetime, max_date: datetime) -> float:
"""
Calculate sortino
:param trades: DataFrame containing trades (requires columns profit_ratio)
:return: sortino
"""
if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date):
return 0
total_profit = trades["profit_ratio"]
days_period = (max_date - min_date).days
if days_period == 0:
return 0
# adding slippage of 0.1% per trade
# total_profit = total_profit - 0.0005
expected_returns_mean = total_profit.sum() / days_period
trades['downside_returns'] = 0
trades.loc[total_profit < 0, 'downside_returns'] = trades['profit_ratio']
down_stdev = np.std(trades['downside_returns'])
if down_stdev != 0:
sortino_ratio = expected_returns_mean / down_stdev * np.sqrt(365)
else:
# Define high (negative) sortino ratio to be clear that this is NOT optimal.
sortino_ratio = -100
# print(expected_returns_mean, down_stdev, sortino_ratio)
return sortino_ratio
def calculate_sharpe(trades: pd.DataFrame,
min_date: datetime, max_date: datetime) -> float:
"""
Calculate sharpe
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
:return: sharpe
"""
if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date):
return 0
total_profit = trades["profit_ratio"]
days_period = (max_date - min_date).days
if days_period == 0:
return 0
# adding slippage of 0.1% per trade
# total_profit = total_profit - 0.0005
expected_returns_mean = total_profit.sum() / days_period
up_stdev = np.std(total_profit)
if up_stdev != 0:
sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365)
else:
# Define high (negative) sharpe ratio to be clear that this is NOT optimal.
sharp_ratio = -100
# print(expected_returns_mean, up_stdev, sharp_ratio)
return sharp_ratio
def calculate_calmar(trades: pd.DataFrame,
min_date: datetime, max_date: datetime) -> float:
"""
Calculate calmar
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
:return: calmar
"""
if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date):
return 0
total_profit = trades["profit_ratio"]
days_period = (max_date - min_date).days
# adding slippage of 0.1% per trade
# total_profit = total_profit - 0.0005
expected_returns_mean = total_profit.sum() / days_period * 100
# calculate max drawdown
try:
_, _, _, _, _, max_drawdown = calculate_max_drawdown(
trades, value_col="profit_abs"
)
except ValueError:
max_drawdown = 0
if max_drawdown != 0:
calmar_ratio = expected_returns_mean / max_drawdown * math.sqrt(365)
else:
# Define high (negative) calmar ratio to be clear that this is NOT optimal.
calmar_ratio = -100
# print(expected_returns_mean, max_drawdown, calmar_ratio)
return calmar_ratio