Optimizing Portfolio Weights for Drawdown and Sortino Objectives
Summary
The document describes how to optimize portfolio allocations against objectives such as drawdown or the Sortino ratio. It recommends expressing the chosen risk or reward measure as a scalar function of portfolio weights, then using an optimizer suited to that function. A covariance-based quadratic program directly handles variance, but it does not generally capture drawdown or downside risk without additional assumptions. Historical estimates can serve as rough proxies, though they may not predict future risk or reflect interactions among holdings.
The responses distinguish convex objectives, for which standard methods can offer stronger solution guarantees, from non-convex objectives, which may require search or nonlinear methods that can be slower and find only local optima. A generic weight-search approach is illustrated for Sortino optimization. The central caveat is overfitting: optimizing a noisy historical metric may reflect curve fitting more than a robust portfolio improvement.
Key ideas
- Represent the portfolio objective as a scalar function of the portfolio weights.
- Variance fits covariance-based quadratic optimization, while drawdown and Sortino generally require different objective formulations.
- Historical risk measures are imperfect estimates of future portfolio risk and may miss asset interactions.
- Convexity affects which optimization methods and solution guarantees are available.
- Optimizing noisy historical metrics can lead to curve fitting and fragile allocations.
Tags
Full text
# How to define the objective function for a custom optimization problem?
# How to define the objective function for a custom optimization problem?
I would like to find the allocations that would minimize some user-defined metric (Sortino, minimum drawdown, etc) for a portfolio of assets.
How would one go about formulating the objective functions to pass to the optimizer?
I have experience in portfolio optimization for minimum variance and Sharpe, but when the objective is different, how would one go about tackling and formulating the problem?
## Answer by Ram Ahluwalia (score 11, accepted)
https://quant.stackexchange.com/a/4072
Minimum variance can be solved simply and efficiently via a quadratic optimizer as the only key input is a covariance matrix.
Drawdown or Sortino cannot be optimized via a covariance matrix unless you assume some functional relationship between co-variances/variances and your risk metric of interest. Likely you'll wind up with a similar portfolio to the minimum-variance under this strategy anyway since under the assumption of a joint normally distributed return, securities with the highest co-variance/variances will also have the highest drawdown.
The optimizer is solving for what set of weights maximizes or minimizes an objective function. So you need to formulate an objective function that represents the expected utility of your portfolio given a set of weights. The utility function would be the sum of its expected alpha and have a penalty for drawdown/sortino. A simple (crude?) way to express the expected drawdown or sortino is to assume that the expected drawdown or or sortino for a security is proxied by the historical drawdown / sortino.
You could use the PortfolioAnalytics package in R to measure the historical risk (drawdown, sortino, etc.) for a function and pass it to an optimizer. This is a bit risky since historical drawdown may not be a good predictor for future drawdown (and you have to account for interactions amongst assets). Now that you have a function for risk, use an optimizer that searches across weight space to see where your objective function is minimized. You need an optimizer that searches across weight space (such as a genetic algorithm or a random portfolio generator) rather than a quadratic or gradient-based optimizer as your drawdown function can probably not be differentiated.
Brian Peterson and Peter Carl have a nice illustration of taking arbitrary objective functions and using a search-based algorithm to solve them here. I think their presentation will help you make the ideas suggested above more concrete in practice.
Update: If your objective function is convex then a quadratic optimizer such as quadprog or a machine-learning algorithm such as stochastic gradient descent will guarantee a solution. If your objective function is non-convex then you will need to use an alternative optimization strategy (genetic algos, trust-region methods, non-linear optimizers such as NLOPTR, etc.). Several of these other methods do not guarantee convergence (indeed they might find only local optima). They are also considerably more time-consuming. The severity of this issue depends on the nature of your objective function, the time to iterate and ability to solve the optimization in parallel with random seeds, the level of precision you require given the noisiness of your inputs. For example, non-convex optimizers in general are too slow and unnecessary for applications such as High-Frequency Trading. Also, you may find solutions that are inferior to solving simpler quadratic problems where at least you can guarantee an optimum.
Tip: If you choose to use genetic algorithms such as DEOptim, you may want to consider seeding your initial population with solutions to a mean-variance quadratic optimization (along different points of the frontier) to speed convergence.
You can also accelerate the process if you invest in a CUDA GPU to handle parallel operations.
## Answer by jlowin (score 5)
https://quant.stackexchange.com/a/4077
If you're using Python, you may want to take a look at this question, to which the cvxopt library was the most popular answer.
If not, or if you don't want to use cvxopt, then the basic setup is no different than using mean-variance optimization. You will almost always characterize your problem as a function taking a single vector argument (the portfolio weights) and returning a scalar value (the risk/reward score). A generic optimizer can find the weights which maximize that score.
Do bear in mind that any optimization may be more representative of your algorithm's ability to curve-fit than something real.
Here's highly abstracted pseudocode for optimizing a Sortino ratio in Python, using SciPy's minimize optimizer. Other languages will follow a similar path, and by browsing the documentation you can introduce more sophisticated things like weight boundaries, other constraints, etc.
```
#First set up any global variables you don't need to compute each time
import numpy as np
import scipy
stock_returns = ...
risk_free = ...
#Next define your risk/reward function for a set of portfolio returns
def sortino(portfolio_returns):
downside_returns = portfolio_returns[:]
downside_returns[portfolio_returns > 0] = 0
return np.mean(portfolio_returns) / np.mean(zeroed_returns)
#Now set up your optimization problem,
#returning a negative value because we will use a minimization optimizer
def fn_to_optimize(weightsVector):
portfolio_returns = np.dot(weightsVector, stock_returns)
sortino_ratio = sortino(portfolio_returns - risk_free)
return -1 * sortino_ratio
#Finally, use a generic solver like SciPy's minimize to optimize the weights
initial_weights = np.ones(10) * 0.1
solution = scipy.optimize.minimize(fn_to_optimize, initial_weights, ...)
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.