Critical-Line Algortihm for Portfolio Optimization: An Open-Source Utils

最小分散ポートフォリオ


CLAでは、転換点の概念から制約付きのポートフォリオの最適化を行う。一方で、効率フロンティアの左端の最小分散ポートフォリオはこの転換点上にあるとは限らない。よって、この左端の最小分散ポートフォリオから最後の転換点までのフロンティア曲線はCLAでは求められない。与えられた$${{\bf \omega}}$$と$${{\bf V}}$$から、凸最適化によって最小分散ポートフォリオを計算するコードは以下のようになる。

    def getMinVar(self):
        # Get the minimum variance solution
        var=[]
        for w in self.w:
            a=np.dot(np.dot(w.T,self.covar),w)
            var.append(a)
        return min(var)**.5,self.w[var.index(min(var))]

最大シャープレシオポートフォリオ

最小分散ポートフォリオの場合と同様に、一連の転換点上でシャープレシオを最大にするポートフォリオが、最大シャープレシオポートフォリオだとは限らない。二つの隣り合う転換点での解$${{\bf \omega_0}}$$と$${{\bf \omega_1}}$$の$${\alpha: \alpha\in[0,1]}$$を使った線型結合の$${{\bf \omega}= \alpha{\bf \omega}_0+ (1-\alpha){\bf \omega_1}}$$もまた解である。シャープレシオは$${\lambda}$$の単峰的関数であり、$${\omega_0}$$から$${\omega_1}$$では$${\lambda}$$は増加しない。よって、$${\lambda}$$は$${\alpha}$$の単調減少関数である。これを用いて、各転換点の間でのシャープレシオの最大値を計算し、これからフロンティア曲線全体での最大シャープレシオを求めることができる。

    def goldenSection(self,obj,a,b,**kargs):
        # Golden section method. Maximum if kargs['minimum']==False is passed
        from math import log,ceil
        tol,sign,args=1.0e-9,1,None
        if 'minimum' in kargs and kargs['minimum']==False:sign=-1
        if 'args' in kargs:args=kargs['args']
        numIter=int(ceil(-2.078087*log(tol/abs(b-a))))
        r=0.618033989
        c=1.0-r
        # Initialize
        x1=r*a+c*b;x2=c*a+r*b
        f1=sign*obj(x1,*args);f2=sign*obj(x2,*args)
        # Loop
        for i in range(numIter):
            if f1>f2:
                a=x1
                x1=x2;f1=f2
                x2=c*a+r*b;f2=sign*obj(x2,*args)
            else:
                b=x2
                x2=x1;f2=f1
                x1=r*a+c*b;f1=sign*obj(x1,*args)
        if f1<f2:return x1,sign*f1
        else:return x2,sign*f2
#---------------------------------------------------------------
    def evalSR(self,a,w0,w1):
        # Evaluate SR of the portfolio within the convex combination
        w=a*w0+(1-a)*w1
        b=np.dot(w.T,self.mean)[0,0]
        c=np.dot(np.dot(w.T,self.covar),w)[0,0]**.5
        return b/c
#---------------------------------------------------------------
    def getMaxSR(self):
        # Get the max Sharpe ratio portfolio
        #1) Compute the local max SR portfolio between any two neighbor turning points
        w_sr,sr=[],[]
        for i in range(len(self.w)-1):
            w0=np.copy(self.w[i])
            w1=np.copy(self.w[i+1])
            kargs={'minimum':False,'args':(w0,w1)}
            a,b=self.goldenSection(self.evalSR,0,1,**kargs)
            w_sr.append(a*w0+(1-a)*w1)
            sr.append(b)
        return max(sr),w_sr[sr.index(max(sr))]

効率フロンティア

 凸最適化によって、隣り合う転換点の間の最小分散ポートフォリオ求め、効率フロンティアを導出する。

---------------------------------------------------------------
    def efFrontier(self,points):
        # Get the efficient frontier
        mu,sigma,weights=[],[],[]
        a=np.linspace(0,1,int(points/len(self.w)))[:-1] # remove the 1, to avoid duplications
        b=range(len(self.w)-1)
        for i in b:
            w0,w1=self.w[i],self.w[i+1]
            if i==b[-1]:a=np.linspace(0,1,int(points/len(self.w))) # include the 1 in the last iteration
            for j in a:
                w=w1*j+(1-j)*w0
                weights.append(np.copy(w))
                mu.append(np.dot(w.T,self.mean)[0,0])
                sigma.append(np.dot(np.dot(w.T,self.covar),w)[0,0]**.5)
        return mu,sigma,weights


この記事が気に入ったらサポートをしてみませんか?