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?

  • 2
    $\begingroup$ Usually, a good way to formulate the objective function is to make sure that it is convex. $\endgroup$
    – SRKX
    Sep 3 '12 at 22:19

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.

  • 1
    $\begingroup$ Correct me if I'm wrong, but I'd just add that when you use these "advanced" optimization functions, there is no guarantee of finding the global optima, which might also be something to take into consideration. +1 of course. $\endgroup$
    – SRKX
    Sep 3 '12 at 22:12
  • $\begingroup$ it depends on your objective function i think. If you've got a convex function, then you should use linear optimizers. If you've got a non-convex one, I have bumped in to a optimizer called DEoptim which deals with optimizing non-convex functions... $\endgroup$ Sep 3 '12 at 22:16
  • $\begingroup$ @wguan: indeed, if you have a convex function, then there is no need to use genetic/stochastic algorithms $\endgroup$
    – SRKX
    Sep 3 '12 at 22:20
  • $\begingroup$ @SRKX, yes i agree! $\endgroup$ Sep 3 '12 at 22:24
  • 1
    $\begingroup$ @john all of these methods will be extremely slow for large problems.... $\endgroup$
    – pyCthon
    Sep 4 '12 at 16:43

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, ...)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.