2

Background

I'm trying to implement the Black-Litterman model as a subclass of my already implemented Markowitz model. The main idea of Markowitz model is: you loop through a date_list, on each date you use the moving average approach to estimate the expected returns mu and the covariance matrix sigma, then you compute the mean-variance portfolio using the mean-variance optimiser mean_variance(mu, sigma). Conceptually, the Markowitz model is like this

class Markowitz(object):
    def __init__(self, params):
        self.price_data = ...
        self.date_list = ...

    def estimate_mu_and_sigma(self, date):
        mu = ...
        sigma = ...
        return mu, sigma

    @staticmethod
    def mean_variance_optimiser(mu, sigma):
        w = ...
        return w

    def back_test(self):
        for date in self.date_list:
            mu, sigma = self.estimate_mu_and_sigma(date)
            w = Markowitz.mean_variance_optimiser(mu, sigma)

            # do some other stuff
            pass

The only difference between Black-Litterman and Markowitz is that BL uses a different estimate method for mu and sigma than Markowitz, but the subsequent mean-variance optimisation procedure is identical. Naturally I want to subclass Markowitz to get a BL model. The problem is that in BL, the estimates of mu and sigma need additional parameters. Not only that, but this set of additional parameters also depend dynamically on date, so I can't just override Markowitz.back_test to give it additional parameters. In fact, a BL model is like this:

class BlackLitterman(Markowitz):
    def __init__(self, params, more_parms):
        super().__init__(params)
        self.some_auxiliary_data = ...

    def estimate_mu_and_sigma(self, date, dynamic_params):
        mu = ...
        sigma = ...
        return mu, sigma


    def back_test(self, more_params):
        for date in self.date_list:
            dynamic_params = ...  # depends both on date and more params
            mu, sigma = self.estimate_mu_and_sigma(date, dynamic_params)
            w = Markowitz.mean_variance_optimiser(mu, sigma)

            # do some other stuff
            pass

When I try this, the IDE already complains about BlackLitterman.estimate_mu_and_sigma overriding Markowtiz.estimate_mu_and_sigma with inconsistent signature. Also, this doesn't actually reuse the reusable code in back_test.

Could anybody tell me how to more elegantly inherit from Markowitz? Thanks!

1 Answer 1

2

You shouldn't actually try to make Markowitz your base class, but either have an abstract Model base class and implement both models as subclass, or - muc better IMHO for your use case - have one single Model concrete class that does everything except the estimate_mu_and_sigma and mean_variance_optimiser, and use the strategy pattern for those parts.

Strategy based solution:

class Estimator(object):
    def __init__(self, params, strategy):
        self.price_data = ...
        # etc
        self.strategy = strategy

    def back_test(self):
        for date in self.date_list:
            mu, sigma = self.strategy.estimate_mu_and_sigma(date)
            w = self.strategy.mean_variance_optimiser(mu, sigma)

            # do some other stuff
            pass


class MarkowitzStrategy(object):
    def __init__(self, *args, **kw):
       # ...

    def estimate_mu_and_sigma(self, date):
        mu = ...
        sigma = ...
        return mu, sigma


class BlackLittermanStrategy(object):


    def __init__(self, *args, **kw):
       # here you pass `more_params` and store them locally
       # etc so you can caculate 
       # `dynamic_params` here without polluting the Estimator class
       self.more_params = ....

    def _calc_dyn_params(self, date):
        return ...

    def estimate_mu_and_sigma(self, date):
        dynamic_params = self._calc_dyn_params(date)
        mu = ...
        sigma = ...
        return mu, sigma

Then you build the strategy with the appropriate arguments and pass it to your estimator. By splitting the variant part (the strategies) from the invariant (how to estimate), you avoid having to pollute the Estimator with irrelevant details.

NB: for the inheritance-based solution, you have to design your base class so it methods can accept all possible arguments for all possible estimation models, which is usually done using *args and **kwargs for all arguments that will vary from one concrete class to another. This doesn't really help wrt/ documentation and debugging...

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. The mean_variance_optimiser is static so I assume it can be encapsulated in Estimator? Also, is double def a typo?
@Vim if mean_variance_optimiser (which I think should be named optimize_mean_variance - functions and methods are actions, so they should use verbs) is the same whatever the strategy then it doesn't have to be part of the strategy indeed - the fact you mentionned it in the BlackLitterman class led me to think it was overridden too. And yes of course the def def thing is a typo ;-)
oops my bad. I shouldn't have mentioned it in BL. It's actually intended to inherit from Markowitz.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.