Files
strategy-lab/to_explore/pyquantnews/32_KellyBetting.ipynb
David Brazda e3da60c647 daily update
2024-10-21 20:57:56 +02:00

6.9 KiB

No description has been provided for this image

This code calculates the Kelly fraction, the optimal leverage for maximizing long-term growth, using historical stock returns. It fetches annual returns for the S&P 500 index, computes rolling statistics, and applies a numerical integration method to optimize the leverage. The code also visualizes the cumulative compounded returns using the Kelly strategy. This is useful in practice for portfolio management, risk management, and enhancing investment performance through optimized leverage.

In [ ]:
from openbb_terminal.sdk import openbb
In [ ]:
import numpy as np
from scipy.optimize import minimize_scalar
from scipy.integrate import quad
from scipy.stats import norm

Fetch annual returns for the S&P 500 index since 1950

In [ ]:
annual_returns = (
    openbb.economy.index(["^GSPC"], start_date="1950-01-01", column="Close")
    .resample("A")
    .last()
    .pct_change()
    .dropna()
)

Compute rolling mean and standard deviation over a 25-year window

In [ ]:
return_params = annual_returns["^GSPC"].rolling(25).agg(["mean", "std"]).dropna()

Define a function to calculate the negative value of the expected log return

In [ ]:
def norm_integral(f, mean, std):
    """Calculates the negative expected log return
    
    Parameters
    ----------
    f : float
        Leverage factor
    mean : float
        Mean return
    std : float
        Standard deviation of returns
    
    Returns
    -------
    float
        Negative expected log return
    """
    
    val, er = quad(
        lambda s: np.log(1 + f * s) * norm.pdf(s, mean, std),
        mean - 3 * std,
        mean + 3 * std,
    )
    return -val

Define a function to optimize the Kelly fraction using the minimize_scalar method

In [ ]:
def get_kelly(data):
    """Optimizes the Kelly fraction
    
    Parameters
    ----------
    data : pd.Series
        Contains mean and standard deviation of returns
    
    Returns
    -------
    float
        Optimal Kelly fraction
    """
    
    solution = minimize_scalar(
        norm_integral, args=(data["mean"], data["std"]), bounds=[0, 2], method="bounded"
    )
    return solution.x

Calculate the Kelly fraction for each rolling window and add it to the annual returns DataFrame

In [ ]:
annual_returns['f'] = return_params.apply(get_kelly, axis=1)

Visualize the cumulative compounded returns using the Kelly strategy

In [ ]:
(
    annual_returns[["^GSPC"]]
    .assign(kelly=annual_returns["^GSPC"].mul(annual_returns.f.shift()))
    .dropna()
    .loc["1900":]
    .add(1)
    .cumprod()
    .sub(1)
    .plot(lw=2)
)

Pick an arbitrary point for mean and standard deviation to calculate optimal Kelly fraction

In [ ]:
m = .058
s = .216

Optimize the Kelly fraction for the given mean and standard deviation

In [ ]:
sol = minimize_scalar(norm_integral, args=(m, s), bounds=[0.0, 2.0], method="bounded")
print("Optimal Kelly fraction: {:.4f}".format(sol.x))

This formula can result in Kelly fractions higher than 1. In this case, it is theoretically advantageous to use leverage to purchase additional securities on margin.

PyQuant News is where finance practitioners level up with Python for quant finance, algorithmic trading, and market data analysis. Looking to get started? Check out the fastest growing, top-selling course to get started with Python for quant finance. For educational purposes. Not investment advise. Use at your own risk.