Overall Statistics
Total Trades
24
Average Win
0.22%
Average Loss
-1.28%
Compounding Annual Return
19.926%
Drawdown
8.800%
Expectancy
-0.033
Net Profit
6.242%
Sharpe Ratio
1.434
Probabilistic Sharpe Ratio
57.239%
Loss Rate
18%
Win Rate
82%
Profit-Loss Ratio
0.17
Alpha
0.175
Beta
-0.04
Annual Standard Deviation
0.117
Annual Variance
0.014
Information Ratio
-0.068
Tracking Error
0.21
Treynor Ratio
-4.145
Total Fees
$24.00
import numpy as np
import pandas as pd
from datetime import datetime, date
from datetime import datetime, timedelta
from PortfolioOptimizerClass import PortfolioOptimizer

from clr import AddReference
AddReference("QuantConnect.Indicators")
from QuantConnect.Indicators import *

# TODO :
# fix buying daily
# Universe selection
# short selling model
# selling hourly
# rebalance weekly (weight based on RS?)
# look into small stocks large moves 35.65->33.51 which is 6% ; control via draw down?
# self.SetBrokerageModel(AlphaStreamsBrokerageModel()) # learn more about this
# fix if dt >9 and dt<18
# DONE: fix hourlyHouseKeeping
# 10 days daily STD for ROKU on 7 Jan 21 is 12.15529, mine (based on open) is 12.3907448
 

from System.Drawing import Color

class ModelA(AlphaModel): 
    
    def __init__(self, param):

        self.param                  = param
        self.symbolDataBySymbol     = {}
        self.modelResolution        = param.resolution
        self.insightsTimeDelta      = param.timedelta
        self.objectiveFunction      = param.pcmObjectiveFunction
        self.lookbackOptimization   = param.pcmLookbackOptimization
        self.portOpt                = PortfolioOptimizer(minWeight = 0, maxWeight = 1)
        self.startingMaxHoldingLimit = param.startingMaxHoldingLimit 
        
    def OnSecuritiesChanged(self, algorithm, changes):
            for added in changes.AddedSecurities:
                symbolData = self.symbolDataBySymbol.get(added.Symbol)
                if symbolData is None:
                    symbolData = SymbolData(added.Symbol, algorithm, self.param)
                    self.symbolDataBySymbol[added.Symbol] = symbolData
 
    def Update(self, algorithm, data):
        

        #if self.Portfolio.Invested: return
        liquidate_now=[]

        invested = [ x.Symbol.Value for x in algorithm.Portfolio.Values if x.Invested ] # can we make this easier via key query?
       
        for symbol, symbolData in self.symbolDataBySymbol.items():
 
            isInvested= str(symbol) in invested  
            
            if symbol != self.param.benchmark:

                symbolData.getInsight(algorithm.Securities[symbol].Price, isInvested) # Latest known price; we are at 12:00 and the last trade at 10.57 
            
                if symbolData.trade:
                    
                    if symbolData.liquidate:
                        invested.remove(str(symbol))
                        liquidate_now.append(str(symbol))
                    else:
                        invested.append(str(symbol))
                    

        # calculate optimal weights
        if invested:

            weights = self.CalculateOptimalWeights(algorithm, invested, self.objectiveFunction, self.lookbackOptimization)

            for symbol in invested:

                weight = weights[str(symbol)]
                if weight>self.startingMaxHoldingLimit and len(invested)<1/self.startingMaxHoldingLimit: weight=self.startingMaxHoldingLimit
                #self.algorithm.MarketOrder(symbol, 1)
                algorithm.SetHoldings(symbol, weight);

        if liquidate_now:
            for symbol in liquidate_now:
                weight=self.startingMaxHoldingLimit
                algorithm.Liquidate(symbol)
        
        return []
      
    def OnOrderEvent(self, orderEvent):
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        if orderEvent.Status == OrderStatus.Filled: 
            self.algorithm.Debug("{0}: {1}: {2}".format(self.Time, order.Type, orderEvent))
        
    def CalculateOptimalWeights(self, algorithm, symbols, objectiveFunction, lookbackOptimization):
            
        # get historical close prices
        historyClosePrices = algorithm.History(symbols, lookbackOptimization, Resolution.Daily)['close'].unstack(level = 0)
        
        # calculate daily returns
        returnsDf = historyClosePrices.pct_change().dropna()
        # rename the columns in the dataframe in order to have tickers and not symbol strings
        columnsList = list(returnsDf.columns)
        returnsDf.rename(columns = {columnsList[i]: algorithm.ActiveSecurities[columnsList[i]].Symbol.Value for i in range(len(columnsList))}, inplace = True)
        
        # calculate optimal weights
        weights = self.portOpt.Optimize(objectiveFunction, returnsDf)
        # convert the weights to a pandas Series
        weights = pd.Series(weights, index = returnsDf.columns, name = 'weights')
        
        return weights
        
class FrameworkAlgorithm(QCAlgorithm):
    
    def Initialize(self):

        param=paramData()
        symbols             =   [Symbol.Create(x, SecurityType.Equity, Market.USA) for x in param.tickers]
        
        self.SetStartDate(param.dateFrom[0],param.dateFrom[1],param.dateFrom[2])   
        self.SetEndDate(param.dateTo[0],param.dateTo[1],param.dateTo[2])   
        self.SetCash(param.cash)           
        
        self.liquidationBarrier=param.cash*param.stopLoss*-1
        
        self.SetBenchmark(param.benchmarkTicker)
        param.setBenchmark(self.AddEquity(param.benchmarkTicker,param.resolution).Symbol)
        
        self.UniverseSettings.Resolution = param.resolution
        self.SetWarmUp(timedelta(param.warmup)) 
        self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
        self.SetBrokerageModel(AlphaStreamsBrokerageModel()) # learn more about this
        self.SetAlpha(ModelA(param))
                                
        #myPCM = InsightWeightingPortfolioConstructionModel(rebalance = timedelta(days=252), portfolioBias = PortfolioBias.Long)
        #myPCM.RebalanceOnInsightChanges = False
        #myPCM.RebalanceOnSecurityChanges = False 
        #self.SetPortfolioConstruction(myPCM)
        
        #self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(param.maxDrawDown)) # NullRiskManagementModel() or MaximumDrawdownPercentPerSecurity(param.maxDrawDown)  > drop in profit from the max >> done daily / TODO: redo hourly? or 
        #self.SetExecution(ImmediateExecutionModel())
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.Every(TimeSpan.FromMinutes(param.runEveryXminutes)), self.hourlyHouseKeeping)

    def hourlyHouseKeeping(self):
        
            # Fail Safe - If our strategy is losing than acceptable (something is wrong)
            # Strategy suddenly losing money or logic problem/bug we did't catch when testing
            pnl= sum([self.Portfolio[symbol].NetProfit for symbol in self.Portfolio.Keys])
            #if self.LiveMode:
            if pnl < self.liquidationBarrier: 
                self.Debug(f"Fallback event triggered, liquidating with total portfolio loss of {pnl}")
                self.Liquidate()
                self.Quit()
            
            dt=int(self.Time.hour)
            if dt >9 and dt<18: # if not set still prints out of hours for self.IsMarketOpen("SPY")
                if (self.IsMarketOpen("SPY") and self.Portfolio.Invested):
                     #self.Log("\n\nPortfolio")
                    summary = {}
    
                    invested = [ x.Symbol.Value for x in self.Portfolio.Values if x.Invested ]
                    for symbol in invested:
                    
                        hold_val    = round(self.Portfolio[symbol].HoldingsValue, 2)
                        abs_val     = round(self.Portfolio[symbol].AbsoluteHoldingsValue, 2)
                        pnl         = round(self.Portfolio[symbol].UnrealizedProfit, 2)
                        qty         = self.Portfolio[symbol].Quantity
                        price       = self.Portfolio[symbol].Price
                        
                        summary[symbol]=[hold_val,abs_val,pnl,qty,price]
                        
                    df=pd.DataFrame(summary)
                    df.index = ['hold_val', 'abs_val', 'pnl', 'qty','price']
                    df=df.T
                    hold_val_total= abs(df['hold_val']).sum()
                    df = df.assign(weight=abs(df['hold_val'])/hold_val_total)
                     #self.Log(df)
                     #self.Log("\n\n")

class paramData:
    def __init__(self):
        self.dateFrom                   = (2020,9,1)    
        self.dateTo                     = (2021,1,1)   
        self.cash                       = 50000                     # how to top this up after going live?
        self.warmup                     = 28                        # starts from self.dateFrom
        #self.resolution                 = Resolution.Hour           # 10-11, etc Daily data is midnight to mifnight, 12AM EST 
        self.tickers                    = ["MSFT","ROKU","ANET","FSLY"]                  # how do I change this on request?
        self.resolution                 = Resolution.Daily           # 10-11, etc Daily data is midnight to mifnight, 12AM EST 
        self.tickers_len                = len(self.tickers)
        self.timedelta                  = timedelta(hours=240)
        self.maxDrawDown                = 0.05
        self.runEveryXminutes           = 60                        # Schedule frequency
        self.benchmarkTicker            = 'SPY'                     # can be ticker as a part of the dictionary ["MSFT:SPY"]
        self.pcmObjectiveFunction       = 'equalWeighting'          #'equalWeighting' 'maxReturn' 'riskParity' 
        self.pcmLookbackOptimization    = 63
        self.stopLoss                   = 0.15                      # % of the total cash invested
        self.startingMaxHoldingLimit    = 0.17                      # we do not allocate more than this % for each security 

        
    def setBenchmark(self, symbol):
        self.benchmark          =  symbol
        
class SymbolData:
    
    def __init__(self, symbol, algorithm, param):
        
        self.symbol             = symbol
        self.algorithm          = algorithm
        self.param              = param
        self.resolution         = param.resolution
        self.price              = 0.00 # last trading price
        self.lastPricePaidRef   = 0.00 # last purchase price reference; update with an actual price
        self.kama               = algorithm.KAMA(symbol, 10,2,30, self.resolution)
        self.variationRate      = 0.95 # tolerance level to avoid buy and immediate sell scenario
        self.mom                = algorithm.MOM(symbol, 14, self.resolution)
        self.roc                = algorithm.ROC(symbol, 9, self.resolution) 
        self.ema13              = algorithm.EMA(symbol, 13, self.resolution)
        self.ema63              = algorithm.EMA(symbol, 63, self.resolution)
        self.ema150             = algorithm.EMA(symbol, 150, self.resolution)
        self.fkama              = False
        self.fmom               = False
        self.froc               = False
        self.fema               = False
        self.rsStock            = False
        self.rsIdx              = False
        self.fbenchmark         = False
        self.lookback           = 10
        self.std                = algorithm.STD(symbol, self.lookback,self.resolution) 
        self.magnitude          = 0.025#algorithm.IndicatorExtensions.SMA(RateOfChangePercent(1),self.lookback).Current.Value
        self.lastDateTraded     = self.algorithm.Time.date()
        
        
        # Chart Plotting 
        self.kama.Updated       += self.getRSL
        self.kama.Updated       += self.OnSymbolDataUpdate
        self.dataPlot = Chart('Detail'+str(self.symbol))
        self.dataPlot.AddSeries(Series('Price', SeriesType.Line, '$'))
        self.dataPlot.AddSeries(Series('Kama', SeriesType.Line, '$'))
        self.dataPlot.AddSeries(Series('MOM', SeriesType.Line, ''))
        self.dataPlot.AddSeries(Series('EMA13', SeriesType.Line, '$'))
        self.dataPlot.AddSeries(Series('EMA63', SeriesType.Line, '$'))
        self.dataPlot.AddSeries(Series('EMA150', SeriesType.Line, '$'))
        self.dataPlot.AddSeries(Series('ROC', SeriesType.Line, ''))
        self.dataPlot.AddSeries(Series('RS-idx', SeriesType.Line, ''))
        self.dataPlot.AddSeries(Series('Std', SeriesType.Line, '$'))
        self.dataPlot.AddSeries(Series('Buy', SeriesType.Scatter, '$', Color.Green,ScatterMarkerSymbol.Circle))
        self.dataPlot.AddSeries(Series('Sell', SeriesType.Scatter, '$', Color.Red,ScatterMarkerSymbol.Circle))
        self.algorithm.AddChart(self.dataPlot)
        
    def getInsight(self, price, isInvested):    
        
        self.price              = price
        self.fkama_buy          = self.price>self.kama.Current.Value
        self.fkama_sell         = self.price<self.kama.Current.Value*self.variationRate
        self.fmom               = self.mom.Current.Value>0
        self.froc               = self.roc.Current.Value>0
        self.fema               = self.ema13.Current.Value>self.ema63.Current.Value>self.ema150.Current.Value

        self.trade              = False
        self.liquidate          = False
        
        self.fbenchmark         = self.rsStock>self.rsIdx 
        self.dateTradedDelta    = (self.algorithm.Time.date()-self.lastDateTraded).days
        #  and self.froc self.fmom and 
        
        self.algorithm.Debug(f"{str(self.symbol)}\t{str(self.algorithm.Time.date())}\tTraded\t{str(self.lastDateTraded)}\tDt\t{str(self.dateTradedDelta)}\tstd\t{self.std}\tclose\t{self.price}")    
    
        if not isInvested and self.fkama_buy and self.fema and self.fbenchmark:

            self.trade              =   True
            self.lastDateTraded     =   self.algorithm.Time.date() 
            self.algorithm.Plot('Detail'+str(self.symbol),'Buy', self.price)
        
            self.algorithm.Debug(f"\n>>>>>> Buy\t{str(self.symbol)}\tPrice\t{self.price}[{self.lastPricePaidRef}]\tMOM:{self.fmom}\trKAMA\t{self.price}\t \
                                    \nKAMA:{self.kama.Current.Value}\tFEMA:{self.fema}\tRS:{self.fbenchmark}\tSTD\t{self.std}")    
            
            self.lastPricePaidRef   =   self.price
            
                                     
        #  or not self.froc not self.fmom or 
        if isInvested and (self.fkama_sell or not self.fema or not self.fbenchmark \
                    or (self.dateTradedDelta<3 and self.price<self.lastPricePaidRef-float(str(self.std)))): # we avoid selling on the same/next day if move less than x std

            self.trade              =   True
            self.liquidate          =   True
            self.algorithm.Plot('Detail'+str(self.symbol),'Sell',self.price)
            
            self.algorithm.Debug(f"\n<<<<<<< Sell\t{str(self.symbol)}\tMOM\t{self.fmom}\tPrice\t{self.price}[{self.lastPricePaidRef}]\trKAMA\t{self.price}\t \
                                    \nKAMA\t{self.kama.Current.Value}\tFEMA\t{self.fema}\tStock\t{self.rsStock}\tIdx\t{self.rsIdx}\tSTD\t{self.std}\tPriceDrop{str(self.lastPricePaidRef-float(str(self.std)))}")
    

    def OnSymbolDataUpdate(self, sender, updated):
        
        self.algorithm.Plot('Detail'+str(self.symbol),'Price', self.price)
        self.algorithm.Plot('Detail'+str(self.symbol),'Kama', self.kama.Current.Value)
        self.algorithm.Plot('Detail'+str(self.symbol),'ROC', self.roc.Current.Value)
        self.algorithm.Plot('Detail'+str(self.symbol),'MOM', self.mom.Current.Value)
        self.algorithm.Plot('Detail'+str(self.symbol),'EMA13', self.ema13.Current.Value)
        self.algorithm.Plot('Detail'+str(self.symbol),'EMA63', self.ema63.Current.Value)
        self.algorithm.Plot('Detail'+str(self.symbol),'EMA150', self.ema150.Current.Value)
        self.algorithm.Plot('Detail'+str(self.symbol),'Std', self.std.Current.Value)
        
    def getRSL(self, sender, updated):

        # lookback days : algo weight 
        days = {40:0.5,80:0.25,160:0.25}

        rs = {}

        for symbol in [self.symbol,self.param.benchmark]:
 
            result =[]
            
            df=pd.DataFrame(self.algorithm.History(symbol, 300, Resolution.Daily)) 
            df=df.iloc[::-1]
            df=df.reset_index(level=0, drop=True)
            
            symbol = str(symbol)
            for x in days:
                result.append([symbol, x, df.iloc[0]['close'], df.iloc[x-1]['close'],days[x]])

            df = pd.DataFrame(result,columns=['Symbol','Days','Ref_Price','Close_Price','Weight'],dtype=float)
            df = df.assign(Rsl=(df['Ref_Price'])/df['Close_Price']*df['Weight'])
            
            rs[symbol] = (abs(df['Rsl']).sum()*1000)-1000
        
        self.rsStock    = rs[str(self.symbol)]
        self.rsIdx      = rs[str(self.param.benchmark)]

        self.algorithm.Plot('Detail'+str(self.symbol),'RS-idx', self.rsStock/self.rsIdx)
from clr import AddReference
AddReference("QuantConnect.Research")
#clr.AddReference('QuantConnect.Research')
from QuantConnect.Research import QuantBook

class RelativeStrengthLineCalc():

    def getRSL(self, ref_date, symbols):

        self.rsl_target_days     = [40,80,160] 
        self.rsl_target_weights  = [0.5,0.25,0.25]

        qb = QuantBook()
    
        date_end = datetime(ref_date)
        date_start = date_end - timedelta(days=300)
        
        for symbol in symbols:
            
            smbl = qb.AddEquity(symbol) # add equity data
            result =[]
            
            history = qb.History(smbl.Symbol, date_start, date_end, Resolution.Daily)
            df=pd.DataFrame(history)
            df=df.iloc[::-1]
            df=df.reset_index(level=0, drop=True)
            
            i=0
            for x in rsl_target_days:
                result.append([symbol, x, df.iloc[0]['close'], df.iloc[x-1]['close'],rsl_target_weights[i]])
                i=i+1
            df = pd.DataFrame(result,columns=['Symbol','Days','Ref_Price','Close_Price','Weight'],dtype=float)
            df = df.assign(Rsl=(df['Ref_Price'])/df['Close_Price']*df['Weight'])
            rsl=(abs(df['Rsl']).sum()*1000)-1000
            
        return  rsl
class RelativeStrengthLineCalc():

    def getRSL():

        rsl_target_days     = [40,80,160] 
        rsl_target_weights  = [0.5,0.25,0.25]
        
        return  1
import pandas as pd
import numpy as np
from scipy.optimize import minimize

class PortfolioOptimizer:
    
    '''
    Description:
        Implementation of a custom optimizer that calculates the weights for each asset to optimize a given objective function
    Details:
        Optimization can be:
            - Equal Weighting
            - Maximize Portfolio Return
            - Minimize Portfolio Standard Deviation
            - Mean-Variance (minimize Standard Deviation given a target return)
            - Maximize Portfolio Sharpe Ratio
            - Maximize Portfolio Sortino Ratio
            - Risk Parity Portfolio
        Constraints:
            - Weights must be between some given boundaries
            - Weights must sum to 1
    '''
    
    def __init__(self, 
                 minWeight = 0,
                 maxWeight = 1):
                     
        '''
        Description:
            Initialize the CustomPortfolioOptimizer
        Args:
            minWeight(float): The lower bound on portfolio weights
            maxWeight(float): The upper bound on portfolio weights
        '''
        
        self.minWeight = minWeight
        self.maxWeight = maxWeight

    def Optimize(self, objFunction, dailyReturnsDf, targetReturn = None):
        
        '''
        Description:
            Perform portfolio optimization given a series of returns
        Args:
            objFunction: The objective function to optimize (equalWeighting, maxReturn, minVariance, meanVariance, maxSharpe, maxSortino, riskParity)
            dailyReturnsDf: DataFrame of historical daily arithmetic returns
        Returns:
            Array of double with the portfolio weights (size: K x 1)
        '''
        
        # initial weights: equally weighted
        size = dailyReturnsDf.columns.size # K x 1
        self.initWeights = np.array(size * [1. / size])

        # get sample covariance matrix
        covariance = dailyReturnsDf.cov()
        # get the sample covariance matrix of only negative returns for sortino ratio
        negativeReturnsDf = dailyReturnsDf[dailyReturnsDf < 0]
        covarianceNegativeReturns = negativeReturnsDf.cov()
        
        if objFunction == 'equalWeighting':
            return self.initWeights
        
        bounds = tuple((self.minWeight, self.maxWeight) for x in range(size))
        constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1.0}]
        
        if objFunction == 'meanVariance':
            # if no target return is provided, use the resulting from equal weighting
            if targetReturn is None:
                targetReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, self.initWeights)
            constraints.append( {'type': 'eq', 'fun': lambda weights:
                                self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights) - targetReturn} )

        opt = minimize(lambda weights: self.ObjectiveFunction(objFunction, dailyReturnsDf,
                                                                covariance, covarianceNegativeReturns,
                                                                weights),
                        x0 = self.initWeights,
                        bounds = bounds,
                        constraints = constraints,
                        method = 'SLSQP')

        return opt['x']

    def ObjectiveFunction(self, objFunction, dailyReturnsDf, covariance, covarianceNegativeReturns, weights):
        
        '''
        Description:
            Compute the objective function
        Args:
            objFunction: The objective function to optimize (equalWeighting, maxReturn, minVariance, meanVariance,
                                                                maxSharpe, maxSortino, riskParity)
            dailyReturnsDf: DataFrame of historical daily returns
            covariance: Sample covariance
            covarianceNegativeReturns: Sample covariance matrix of only negative returns
            weights: Portfolio weights
        '''
    
        if objFunction == 'maxReturn':
            f = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
            return -f # convert to negative to be minimized
        elif objFunction == 'minVariance':
            f = self.CalculateAnnualizedPortfolioStd(covariance, weights)
            return f
        elif objFunction == 'meanVariance':
            f = self.CalculateAnnualizedPortfolioStd(covariance, weights)
            return f
        elif objFunction == 'maxSharpe':
            f = self.CalculateAnnualizedPortfolioSharpeRatio(dailyReturnsDf, covariance, weights)
            return -f # convert to negative to be minimized
        elif objFunction == 'maxSortino':
            f = self.CalculateAnnualizedPortfolioSortinoRatio(dailyReturnsDf, covarianceNegativeReturns, weights)
            return -f # convert to negative to be minimized
        elif objFunction == 'riskParity':
            f = self.CalculateRiskParityFunction(covariance, weights)
            return f
        else:
            raise ValueError(f'PortfolioOptimizer.ObjectiveFunction: objFunction input has to be one of equalWeighting,'
             + ' maxReturn, minVariance, meanVariance, maxSharpe, maxSortino or riskParity')
        
    def CalculateAnnualizedPortfolioReturn(self, dailyReturnsDf, weights):
        
        annualizedPortfolioReturns = np.sum( ((1 + dailyReturnsDf.mean())**252 - 1) * weights )
        
        return annualizedPortfolioReturns
            
    def CalculateAnnualizedPortfolioStd(self, covariance, weights):
        
        annualizedPortfolioStd = np.sqrt( np.dot(weights.T, np.dot(covariance * 252, weights)) )
        
        if annualizedPortfolioStd == 0:
            raise ValueError(f'PortfolioOptimizer.CalculateAnnualizedPortfolioStd: annualizedPortfolioStd cannot be zero. Weights: {weights}')
            
        return annualizedPortfolioStd
    
    def CalculateAnnualizedPortfolioNegativeStd(self, covarianceNegativeReturns, weights):
    
        annualizedPortfolioNegativeStd = np.sqrt( np.dot(weights.T, np.dot(covarianceNegativeReturns * 252, weights)) )        
    
        if annualizedPortfolioNegativeStd == 0:
            raise ValueError(f'PortfolioOptimizer.CalculateAnnualizedPortfolioNegativeStd: annualizedPortfolioNegativeStd cannot be zero. Weights: {weights}')
        
        return annualizedPortfolioNegativeStd
        
    def CalculateAnnualizedPortfolioSharpeRatio(self, dailyReturnsDf, covariance, weights):
        
        annualizedPortfolioReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
        annualizedPortfolioStd = self.CalculateAnnualizedPortfolioStd(covariance, weights)
        annualizedPortfolioSharpeRatio = annualizedPortfolioReturn / annualizedPortfolioStd
            
        return annualizedPortfolioSharpeRatio
    
    def CalculateAnnualizedPortfolioSortinoRatio(self, dailyReturnsDf, covarianceNegativeReturns, weights):
        
        annualizedPortfolioReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
        annualizedPortfolioNegativeStd = self.CalculateAnnualizedPortfolioNegativeStd(covarianceNegativeReturns, weights)
        annualizedPortfolioSortinoRatio = annualizedPortfolioReturn / annualizedPortfolioNegativeStd
            
        return annualizedPortfolioSortinoRatio
    
    def CalculateRiskParityFunction(self, covariance, weights):
        
        ''' Spinu formulation for risk parity portfolio '''
        
        assetsRiskBudget = self.initWeights
        portfolioVolatility = self.CalculateAnnualizedPortfolioStd(covariance, weights)
        
        x = weights / portfolioVolatility
        riskParity = (np.dot(x.T, np.dot(covariance, x)) / 2) - np.dot(assetsRiskBudget.T, np.log(x))
            
        return riskParity
#insights.append(Insight(symbol, self.insightsTimeDelta, InsightType.Price, symbolData.InsightDirection, None,None, None,0.1))
                #algorithm.Log(f"{symbol}\tMOM\t[{symbolData.fmom}]\t{round(symbolData.mom.Current.Value,2)}\tKAMA\t[{symbolData.fkama}]\t{round(symbolData.kama.Current.Value,2)}\
                #                \tPrice\t{symbolData.price}\tROC\t[{symbolData.froc}]\t{round(symbolData.roc.Current.Value,4)}\tEMA\t[{symbolData.fema}]\tEMA-13\t{round(symbolData.ema13.Current.Value,2)}\
                #                \tEMA-63\t{round(symbolData.ema63.Current.Value,2)}\tEMA-150\t{round(symbolData.ema150.Current.Value,2)}\taction\t{symbolData.InsightDirection}")

            
          #self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        #self.SetPortfolioConstruction(MeanVarianceOptimizationPortfolioConstructionModel(param.resolution,PortfolioBias.LongShort,1,63,param.resolution,0.02,MaximumSharpeRatioPortfolioOptimizer(0,1,0)))
        
        
            # self.rebalancingPeriod  = Expiry.EndOfMonth
            #pcm = InsightWeightingPortfolioConstructionModel(lambda time: param.rebalancingPeriod(time))
            
                        #self.InsightDirection   =   InsightDirection.Up
                                    #self.InsightDirection = InsightDirection.Flat # liqudates position - work around InsightDirection.Down which may sell and then short