首页 > 解决方案 > Python:如何在允许方法内修改的同时重用基本方法?

问题描述

背景

我正在尝试将 Black-Litterman 模型实现为我已经实现的Markowitz 模型的子类。Markowitz 模型的主要思想是:你循环一个date_list,在每个上date你使用移动平均方法来估计预期收益mu和协方差矩阵sigma,然后你使用均值方差优化器计算均值方差投资组合mean_variance(mu, sigma)。从概念上讲,Markowitz 模型是这样的

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

Black-Litterman 和 Markowitz 之间的唯一区别是 BL 使用与 Markowitz 不同的估计方法musigma但随后的均值方差优化过程是相同的。自然我想继承Markowitz一个 BL 模型。问题在于,在 BL 中,需要额外参数的mu估计sigma。不仅如此,这组附加参数还动态依赖于date,所以我不能直接覆盖Markowitz.back_test给它附加参数。实际上,一个 BL 模型是这样的:

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

当我尝试这个时,IDE 已经抱怨用不一致的签名BlackLitterman.estimate_mu_and_sigma覆盖。Markowtiz.estimate_mu_and_sigma此外,这实际上并没有重用back_test.

谁能告诉我如何更优雅地继承 from Markowitz?谢谢!

标签: pythonoopinheritanceoverriding

解决方案


您实际上不应该尝试创建Markowitz基类,而是拥有一个抽象Model基类并将两个模型都实现为子类,或者 - 恕我直言,您的用例更好 - 有一个Model具体类可以完成除estimate_mu_and_sigmaandmean_variance_optimiser和使用之外的所有操作这些部分的策略模式。

基于策略的解决方案:

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

然后,您使用适当的参数构建策略并将其传递给您的估算器。通过将变量部分(策略)与不变量(如何估计)分开,您可以避免用不相关的细节污染 Estimator。

注意:对于基于继承的解决方案,您必须设计您的基类,以便它的方法可以接受所有可能的估计模型的所有可能参数,这通常使用*args**kwargs对于从一个具体类到另一个具体类不同的所有参数来完成。这并没有真正帮助 wrt/ 文档和调试......


推荐阅读