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

6.4 KiB

No description has been provided for this image

This code downloads historical stock data for SPY and AAPL from Yahoo Finance and calculates daily returns. It then computes the Sortino ratio, a risk-adjusted performance metric, for these returns. The Sortino ratio focuses on downside risk, providing a better measure for evaluating strategies with asymmetric risk profiles. The code also visualizes the rolling Sortino ratio and its distribution for AAPL. This is useful for performance analysis and comparison of different investment strategies.

In [ ]:
import yfinance as yf
import numpy as np

Download historical adjusted closing prices for SPY and AAPL from Yahoo Finance

In [ ]:
data = yf.download(["SPY", "AAPL"], start="2020-01-01", end="2022-07-31")

Extract adjusted closing prices and calculate daily returns for SPY and AAPL

In [ ]:
closes = data['Adj Close']
spy_returns = closes.SPY.pct_change().dropna()
aapl_returns = closes.AAPL.pct_change().dropna()

Define a function to calculate the Sortino ratio for a series of returns

In [ ]:
def sortino_ratio(returns, adjustment_factor=0.0):
    """
    Determines the Sortino ratio of a strategy.
    
    Parameters
    ----------
    returns : pd.Series or np.ndarray
        Daily returns of the strategy, noncumulative.
    adjustment_factor : int, float
        Constant daily benchmark return throughout the period.

    Returns
    -------
    sortino_ratio : float

    Note
    -----
    See `<https://www.sunrisecapital.com/wp-content/uploads/2014/06/Futures_
    Mag_Sortino_0213.pdf>`__ for more details.
    """

    # Adjust returns by subtracting the adjustment factor and compute average annual return
    returns_risk_adj = np.asanyarray(returns - adjustment_factor)
    average_annual_return = returns_risk_adj.mean() * 252

    # Compute downside deviation by considering only negative deviations
    downside_diff = np.clip(returns_risk_adj, np.NINF, 0)
    np.square(downside_diff, out=downside_diff)
    annualized_downside_deviation = np.sqrt(downside_diff.mean()) * np.sqrt(252)

    # Calculate and return the Sortino ratio
    return average_annual_return / annualized_downside_deviation

Calculate the Sortino ratio for SPY's daily returns

In [ ]:
sortino_ratio(spy_returns)

Calculate the Sortino ratio for AAPL's daily returns

In [ ]:
sortino_ratio(aapl_returns)

Plot the rolling 30-day Sortino ratio for AAPL's returns

In [ ]:
aapl_returns.rolling(30).apply(sortino_ratio).plot()

Plot the histogram of the rolling 30-day Sortino ratio for AAPL's returns

In [ ]:
aapl_returns.rolling(30).apply(sortino_ratio).hist(bins=50)

Plot the histogram of the difference between rolling 30-day Sortino ratios of AAPL and SPY

In [ ]:
(
    aapl_returns.rolling(30).apply(sortino_ratio)
    - spy_returns.rolling(30).apply(sortino_ratio)
).hist(bins=50)

Calculate the Sortino ratio for SPY's daily returns again

In [ ]:
sortino_ratio(spy_returns)

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.