| Overall Statistics |
|
Total Trades 91 Average Win 0.31% Average Loss -0.63% Compounding Annual Return 14.769% Drawdown 8.700% Expectancy 0.120 Net Profit 4.714% Sharpe Ratio 0.972 Probabilistic Sharpe Ratio 47.770% Loss Rate 25% Win Rate 75% Profit-Loss Ratio 0.49 Alpha 0.135 Beta 0.12 Annual Standard Deviation 0.167 Annual Variance 0.028 Information Ratio -0.268 Tracking Error 0.232 Treynor Ratio 1.35 Total Fees $91.00 |
#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 shortimport 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):
insights=[]
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
insights.append(Insight.Price(symbol, self.insightsTimeDelta, InsightDirection.Up,
None, None, None, weight))
if liquidate_now:
for symbol in liquidate_now:
insights.append(Insight.Price(symbol, self.insightsTimeDelta, InsightDirection.Flat,
None, None, None, 1))
return insights
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 = ["MSFT","MRNA","MELI","ROKU","ANET","XRX","SHOP","EBAY","CSCO","ORCL","NOW","THO","BIDU","SPOT","DOCU","DDOG","SQ","FSLY","TMO","PFE","IOVA","EXEL","ACLS","BNTX","IBM"]
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 # 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.Log(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.Log(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.Log(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 rslclass RelativeStrengthLineCalc():
def getRSL():
rsl_target_days = [40,80,160]
rsl_target_weights = [0.5,0.25,0.25]
return 1import 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