Untitled

 avatar
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