Untitled
unknown
plain_text
14 days ago
6.8 kB
5
Indexable
# ------------------------------- # 4. Performance Metrics Calculation # ------------------------------- # Load the risk free rate data risk_free_filepath = r"\\asiapac.nom\data\MUM\IWM\India_IWM_IPAS\Reet\Momentum Strategy\Codes\Risk Free Rate.xlsx" risk_free_df = pd.read_excel(risk_free_filepath, sheet_name="Risk Free Rate", parse_dates=[0]) risk_free_df.columns = ['Date', '1m', '3m'] def calculate_sharpe_ratio(returns, period): volatility = returns.std() avg_return = returns.mean() # Map rebalance period to an annualization factor annualization_factor = {1: 12, 2: 6, 3: 4}.get(period, 12) # Default to 12 if not found return (avg_return / volatility) * (annualization_factor ** 0.5) if volatility > 0 else np.nan # Ensure that there is a 'Date' column (if not, reset the index) if 'Date' not in result_df.columns: result_df = result_df.reset_index().rename(columns={'index': 'Date'}) # Merge risk free rate data on the 'Date' column merged_df = pd.merge(result_df, risk_free_df, on='Date', how='left') if rebalance_period == 1: merged_df['rf_adjust'] = merged_df['1m'] / 12 elif rebalance_period == 2: merged_df['rf_adjust'] = merged_df['1m'] / 6 elif rebalance_period == 3: merged_df['rf_adjust'] = merged_df['1m'] / 4 else: merged_df['rf_adjust'] = 0 # Compute excess returns by subtracting the risk free rate from monthly returns merged_df['Excess Return'] = merged_df['Return'] - (merged_df['rf_adjust']/100) # Calculate performance metrics using excess returns excess_returns = merged_df['Excess Return'].dropna() sharpe_ratio = calculate_sharpe_ratio(excess_returns, rebalance_period) annualization_factor = {1: 12, 2: 6, 3: 4}.get(rebalance_period, 12) avg_monthly_excess_return = excess_returns.mean() monthly_excess_volatility = excess_returns.std() annualized_volatility = monthly_excess_volatility * (annualization_factor ** 0.5) annualised_risk_premium = avg_monthly_excess_return * annualization_factor # The rest of your existing calculations remain the same initial_aum = result_df['Portfolio Value'].iloc[0] final_aum = result_df['Portfolio Value'].iloc[-1] cumulative_return = (final_aum / initial_aum) - 1 if isinstance(start_date, str): start_date = datetime.strptime(start_date, '%Y-%m-%d') if isinstance(end_date, str): end_date = datetime.strptime(end_date, '%Y-%m-%d') num_months = (end_date.year - start_date.year) * 12 + (end_date.month - start_date.month) annualized_return = ((1 + cumulative_return) ** (12 / num_months)) - 1 def compute_max_drawdown(returns): cumulative = (1 + returns).cumprod() running_max = cumulative.cummax() drawdown = (cumulative - running_max) / running_max return drawdown.min() max_drawdown = compute_max_drawdown(result_df['Return']) # Calculate annual returns annual_returns = result_df.groupby(result_df['Date'].dt.year)['Return'].apply(lambda x: (1 + x).prod() - 1) min_annual_return = annual_returns.min() max_annual_return = annual_returns.max() # Create a DataFrame with all metrics metrics_df = pd.DataFrame({ 'Metric': [ 'Cumulative Return', 'Annualized Return', 'Average Monthly Excess Return', 'Monthly Excess Volatility', 'Annualized Volatility', 'Sharpe Ratio', 'Annualized Risk Premium', 'Maximum Drawdown', 'Minimum Annual Return', 'Maximum Annual Return' ], 'Value': [ cumulative_return, annualized_return, avg_monthly_excess_return, monthly_excess_volatility, annualized_volatility, sharpe_ratio, annualised_risk_premium, max_drawdown, min_annual_return, max_annual_return ] }) # Format the values as percentages where appropriate percentage_metrics = ['Cumulative Return', 'Annualized Return', 'Average Monthly Excess Return', 'Monthly Excess Volatility', 'Annualized Volatility', 'Maximum Drawdown', 'Minimum Annual Return', 'Maximum Annual Return', 'Annualized Risk Premium'] for metric in percentage_metrics: metrics_df.loc[metrics_df['Metric'] == metric, 'Value'] = metrics_df.loc[metrics_df['Metric'] == metric, 'Value'].apply(lambda x: f"{x:.2%}") # Format Sharpe Ratio to 2 decimal places metrics_df.loc[metrics_df['Metric'] == 'Sharpe Ratio', 'Value'] = metrics_df.loc[metrics_df['Metric'] == 'Sharpe Ratio', 'Value'].apply(lambda x: f"{x:.2f}") print("\nPerformance Metrics for the Portfolio:") print(metrics_df.to_string(index=False)) import matplotlib.pyplot as plt import matplotlib.dates as mdates import pandas as pd def plot_drawdown(result_df, months=None): # Ensure we're working with a copy to avoid modifying the original DataFrame df = result_df.copy() # If 'Date' is not the index, set it as the index if 'Date' in df.columns: df.set_index('Date', inplace=True) # Calculate drawdown drawdown = calculate_drawdown(df) # If months is specified, filter the data if months: end_date = df.index[0] + pd.DateOffset(months=months) drawdown = drawdown[drawdown.index <= end_date] plt.figure(figsize=(12, 6)) plt.plot(drawdown.index, drawdown, color='red', linewidth=1) plt.fill_between(drawdown.index, drawdown, 0, color='red', alpha=0.3) title = 'Portfolio Drawdown' if months: title += f' (First {months} Months)' plt.title(title) plt.xlabel('Date') plt.ylabel('Drawdown') plt.grid(True, linestyle='--', alpha=0.7) # Format y-axis as percentage plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: '{:.0%}'.format(y))) # Format x-axis to show dates properly plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d')) plt.gca().xaxis.set_major_locator(mdates.AutoDateLocator()) # Rotate and align the tick labels so they look better plt.gcf().autofmt_xdate() # Remove spines ax = plt.gca() ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.spines['left'].set_visible(False) ax.spines['bottom'].set_visible(False) plt.tight_layout() plt.show() # Assuming result_df is your simulation result DataFrame # Plot for the entire period plot_drawdown(result_df) # Plot for the first 18 months plot_drawdown(result_df, months=18) colors = ['#CA2420', '#737373', '#80A9AE', '#00305C', '#80003F', '#CC8D19', '#AEADB0', '#00713B', '#DCB940', '#00677A', '#9A9500', '#8F3336', '#5A7400', '#B0CAD0', '#6077A3', '#995E7A', '#DCB172', '#D4D4D4', '#6B9977', '#E5CC89', '#649CB3', '#B7B56B', '#BF8B79', '#8F9D66', '#000000']
Editor is loading...
Leave a Comment