Python: Generating various weightage for portfolio of stocks

Viewed 49

Trying to optimize a portfolio with 3 stocks. How do I optimize the weightage of each stock to maximize sharpe ratio based on the formula below. I have used a weightage of (0.3,0.2,0.5) for starters but would like a way to optimize it.

Requirement: Minimum weightage per stock needs to be > 0.05

A_return = 0.05
B_return = 0.06
C_return = 0.04

A_stdv = 0.03
B_stdv = 0.05
C_stdv = 0.04

corr_AB = 0.21
corr_AC = 0.08
corr_BC = 0.36

weight_A = 0.3
weight_B = 0.2
weight_C = 0.5

sharpe_ratio = (
    (weight_A * A_return) + (weight_B * B_return) + (weight_B * B_return)
) / np.sqrt(
    ((weight_A**2) * (A_stdv**2))
    + ((weight_B**2) * (B_stdv**2))
    + ((weight_C**2) * (C_stdv**2))
    + (2 * corr_AB * A_stdv * B_stdv * weight_A * weight_B)
    + (2 * corr_AC * A_stdv * C_stdv * weight_A * weight_C)
    + (2 * corr_BC * B_stdv * C_stdv * weight_B * weight_C)
)

print(sharpe_ratio)​  # 1.3861547395951404

Not sure if I need to use a loop to iterate the different combinations?

1 Answers

Here is how I would do it:

import numpy as np
import pandas as pd

A_return = 0.05; B_return = 0.06; C_return = 0.04
A_stdv = 0.03; B_stdv = 0.05; C_stdv = 0.04
corr_AB = 0.21; corr_AC = 0.08; corr_BC = 0.36


def sharpe_ratio(weight_A, weight_B, weight_C):
    """Helper function.
    """
    return (
        (weight_A * A_return) + (weight_B * B_return) + (weight_B * B_return)
    ) / np.sqrt(
        ((weight_A**2) * (A_stdv**2))
        + ((weight_B**2) * (B_stdv**2))
        + ((weight_C**2) * (C_stdv**2))
        + (2 * corr_AB * A_stdv * B_stdv * weight_A * weight_B)
        + (2 * corr_AC * A_stdv * C_stdv * weight_A * weight_C)
        + (2 * corr_BC * B_stdv * C_stdv * weight_B * weight_C)
    )
df = (
    pd.DataFrame(
        [
            [i / 100, j / 100, k / 100]
            for i in range(6, 101)
            for j in range(6, 101)
            for k in range(6, 101)
        ],
        columns=["weight_A", "weight_B", "weight_C"],
    )
    .assign(
        sharpe_ratio=lambda x: sharpe_ratio(x["weight_A"], x["weight_B"], x["weight_C"])
    )
    .sort_values(by="sharpe_ratio")
)
# Check that output is the same as your example
print(df[(df["weight_A"] == 0.3) & (df["weight_B"] == 0.2) & (df["weight_C"] == 0.5)])
# Output
        weight_A  weight_B  weight_C  sharpe_ratio
217974       0.3       0.2       0.5      1.386155
# Look up best parameters to maximize sharp ratio
print(df.loc[df["sharpe_ratio"].idxmax(), :])
# Output
weight_A        0.99000
weight_B        1.00000
weight_C        0.06000
sharpe_ratio    2.64413
Related