Financial Performance Metrics

Anmol Bajracharya
Kernel Developer, Wolfram Research Inc.

Join the Conversation #WolframTechConf

Introduction

Analyzing the performance of investment strategies using historical data can be very helpful to improve investment decisions. 
Financial Performance metrics are the tools that measure return( or reward), risk and risk-reward ratios of an investment. 
This talk will focus on how to compute various risk and return metrics using existing functionality in Wolfram Language.

Applications

These metrics are used for performance and risk analysis of investment which is an important part of investment decision process

For example:

After analyzing an asset return we can decide if we want to include the asset in our portfolio

We can analyze the past performance of our portfolio returns

We can compare the performance of various funds

Comparing two assets

Get daily closing prices of two asset and benchmark

(* assetPrice1 = TimeSeries[FinancialData["NASDAQ:MSFT", "Close", {firstDate, lastDate}], TemporalRegularity->True]; *)
(* aassetPrice2 = TimeSeries[FinancialData["NASDAQ:GOOG", "Close", {firstDate, lastDate}], TemporalRegularity->True]; *)
(* marketPrice = TimeSeries[FinancialData["SP500", "Close", {firstDate, lastDate}], TemporalRegularity->True] *)

{assetPrice1, assetPrice2, marketPrice}

Reward

Compute simple returns:

{simpleReturnsForAsset1, simpleReturnsForAsset2, simpleReturnsForMarket} = Map[iSimpleReturns, {assetPrice1, assetPrice2, marketPrice}];
DateListPlot[
	{simpleReturnsForAsset1, simpleReturnsForAsset2, simpleReturnsForMarket}, plotOptions, 
	PlotLegends->{"Asset1", "Asset2", "Market"}
]

Compute gross returns or value of $1 investment on each asset

DateListPlot[
	Map[iGrossReturns, {simpleReturnsForAsset1, simpleReturnsForAsset2 , simpleReturnsForMarket} ], plotOptions, 
	PlotLegends->{"Asset1", "Asset2", "Market"}
]

Compare Reward metrics for Asset1 and Asset2

compareRewards = 
MapThread[ 
Block[
{
	meanReturns = iMeanReturns[#2], 
	annualizedReturns = iAnnualizedMeanReturns[#2, 252]
}, 
<|
	#1 -> <|
			"MeanReturns" -> meanReturns["GeometricMeanReturn"], 
			"AnnualizedReturns" -> annualizedReturns["GeometricMeanReturn"]
		|>
|>
]&, 
{{"Asset1", "Asset2"}, {simpleReturnsForAsset1, simpleReturnsForAsset2}}
];
Multicolumn[ Map[Dataset, compareRewards], 2]

Asset1 has higher annualized returns compared to Asset2

Risks

From above computation, Asset1 seems to be more rewarding than Asset2. So, should we invest in Asset1 instead of Asset2? Lets find out how risky is Asset2

Compare Risk metrics for Asset1 and Asset2

investmentToday = 100000;
compareRisks = 
	MapThread[ 
		Block[
			{
				variability = iVariability[#2, 252], 
				valueAtRisAndExpectedShortfall= iValueAtRiskAndExpectedShortfall[#2,investmentToday, 5]
			}, 
			Association[ #1 -> AssociateTo[variability,valueAtRisAndExpectedShortfall] ]
		]&, 
		{{"Asset1", "Asset2"}, {simpleReturnsForAsset1, simpleReturnsForAsset2}}
	];
Multicolumn[Map[Dataset, compareRisks], 2]

Value-At-Risk and ExpectedShortfall are greater in Asset2 compared to Asset1 . So, Asset2 is more riskier than Asset1.

Risk-Reward Ratios

Compare Sharpe Ratio of Asset1 and Asset2 returns

compareSharpeRatio = 
With[
{riskfreeRate = 0.06/100} (* risk-free-rate = 0.06% *) ,
MapThread[  
<|
	#1 ->AssociateTo[
			iSharpeRatio[#2,riskfreeRate ], 
			iAnnualizedSharpeRatio[#2, riskfreeRate, 252]
		]
|>&, 
	{{"Asset1", "Asset2"}, {simpleReturnsForAsset1, simpleReturnsForAsset2}}
]
];
Multicolumn[ Map[Dataset, compareSharpeRatio], 2]

Asset1 has higher SharpeRatio compared to Asset2

Compute rolling SharpeRatio

ClearAll[rollingSharpeRatio];
rollingSharpeRatio[assetreturns_, riskfreerate_, window_]:= 
	With[{excessReturn = assetreturns - riskfreerate}, 
		MovingMap[
			Divide[Mean[#], StandardDeviation[#]]&, 
			excessReturn, 
			Quantity[window, "Events"]
		]
	];
DateListPlot[
	Map[
		rollingSharpeRatio[#, riskFreeRate, 30]&, 
		{simpleReturnsForAsset1, simpleReturnsForAsset2}
	], 
	plotOptions, 
	PlotLegends->{"Asset1", "Asset2"}, 
	PlotLabel -> "Rolling Sharpe Ratio"
]

Performance metrics

Rewards

({{Rewards}, {({{   
       Using Simple returns and Log Returns
           ({{  Arithmetic Mean Return}, {Geometric Mean Return}, {Annualized Arithmetic Mean Return}, {Annualized Geometric Mean Return}, {Risk Premiums and Excess Returns  }, {ActiveReturns}})           
, 
          Using Profit Loss         

                ({{Gross Profit}, {Net Profit}})}})}})

Risks

({{Risks }, {({{      Risk Measures using Returns
      Input: {(Subscript[ℛ, 1]), (Subscript[ℛ, 2]), ..., (Subscript[ℛ, n])}  
      Other  input: benchmark returns or risk free rate       ,           Risk Measures using Drawdown
         Input: {(Subscript[𝒟, 1]), (Subscript[𝒟, 2]), ..., (Subscript[𝒟, n])}       ,            Downside Risk Mesasures
      Input: {(Subscript[ℛ, 1]), (Subscript[ℛ, 2]), ..., (Subscript[ℛ, n])} and minimum target return     }, {
({{ Variability, ({{Mean Absolute Deviation }, {Volatility or StandardDeviation }, {Variance}, {Annualized Volatility }, {Annualized Variance}, {RelativeRisk : Tracking Error}})}})
, 
Average Drawdown, 
Downside Risk or Downside Deviation}, {
({{Value-At-Risks, ({{VaR}, {VaR Ratio}, {Conditional VaR or ExpectedShortfall }})}})
, 
Maximum Drawdown, 
Semi-StandardDeviation}, {
({{Regression Analysis , ({{CAPM Beta (Systematic Risks)}, {CAPM Alpha}, {Bull Beta}, {Bear Beta}, {Beta Timing Ratio}})}})
, 
Ulcer Index, 
Downside Potential }, {Gross Loss, Pain Index, Upside Risks}, { ,  , Upside Potential }})}})

Risk-Reward Ratios

({{Risk-Reward Ratios}, {({{     Using Returns (Risks)       ,        Using Drawdowns and Return (Drawdown Risk)      ,       Using Returns and Minimum Target Return (mtr) (Dowsdie Risk)       }, {Sharpe Ratio , Calmar Ratio , Sortino Ratio }, {Annualized Sharpe Ratio , Sterling Ratio , Omega Ratio   }, {Information Ratio  , Burke Ratio ,  }, {Treynor Ratio , Martin Ratio ,  }, {Modified Sharpe Ratio , Pain Ratio ,  }, {Profit Factor , Profit-to-Drawdown,  }})}})

Building blocks for computing performance metrics

Rewards:

({{Building blocks for Reward Metrics}, {({{Price , ({{Returns}, {({{Simple Returns, (Subscript[R, t])= (((Subscript[p, t])-(Subscript[p, t-1]))/(Subscript[p, t-1]))}, {Log Returns, (Subscript[r, t])= log( 1 + (Subscript[R, t]))}, {ProfitLoss, (Subscript[pl, t]) = (Subscript[p, t]) - (Subscript[p, t-1])}})}}), ({{Cumulative Returns}, {({{SimpleReturn ->Cumulative Simple Returns, (Subscript[CR, t])}, {LogReturn ->Cumulative Log Returns, (Subscript[Cr, t])}, {ProfitLoss ->Cumulative Profit Loss, (Subscript[Cpl, t])}})}}), Gross Returns
}})}})

Risks:

({{Building blocks for Risk Metrics}, {({{
      Prices    , 
Returns, 
      Drawdowns and /or Relative Drawdowns      , 
      PositivePart or NegativePart of Excess returns for given minimum target return  

                          
}})}})

Note:
Minimum target return could be risk-free rate (eg: TBills), 0, mean return or return that meets investors expectation

Risk-Reward ratios

({{Building blocks for Risk-Rewad Ratios}, {({{             Rewards                    ,                 Risks                     }})}})

Get Data

Price of Apple Stock and SP500

Asset price ( i.e closing price of Apple stock )

(* Closing Price of Apple Stock: Data obtained using FinancialData Function *)
asset = TimeSeries[];

Closing price of S&P 500 which will be used as a benchmark

sp500 = TimeSeries[];

TimeSeriesResample[TemporalData@{asset, sp500}, "Intersection"]["Components"]

Resample asset and sp500 using time common to both time series using TimeSeriesResample

{asset, benchmark} =
Block[{resample = TimeSeriesResample[TemporalData@{asset, sp500}, "Intersection"]["Components"]}, 
Map[TimeSeries[#, TemporalRegularity->True]&, resample]
]

Plot closing price of SP500 and Apple in separate graph

Clear[plotOptions];
plotOptions = {
					AspectRatio -> 0.35, ImageSize -> 600, PlotTheme -> "Detailed", 
					Filling -> Axis, FillingStyle -> Opacity[0.1], PlotRange->Full
			  };

MapThread[
	DateListPlot[
		#1, 
		PlotLabel -> "Adj Closing Price of "<> #2, 
		Filling -> Axis,
		Sequence @ plotOptions
	]&, 
	{
		{asset, benchmark}, 
		{"Asset", "Benchmark"}
	}
]//Column

Rewards: Building blocks for computing reward

({{Building blocks for Rewards}, {({{Price , ({{Returns}, {({{Simple Returns, (Subscript[R, t])= (((Subscript[p, t])-(Subscript[p, t-1]))/(Subscript[p, t-1]))}, {Log Returns, (Subscript[r, t])= log( 1 + (Subscript[R, t]))}, {ProfitLoss, (Subscript[pl, t]) = (Subscript[p, t]) - (Subscript[p, t-1])}})}}), ({{Cumulative Returns}, {({{SimpleReturn ->Cumulative Simple Returns, (Subscript[CR, t])}, {LogReturn ->Cumulative Log Returns, (Subscript[Cr, t])}, {ProfitLoss ->Cumulative Profit Loss, (Subscript[Cpl, t])}})}}), Gross Returns
}})}})

In this talk, I will only compute performance metrics for simple returns of an asset (AAPL)  and use simple returns on SP500 as benchmark

Simple Returns

Computing simple returns, (Subscript[R, t]) = (((Subscript[p, t ])- (Subscript[p, t-1]))/(Subscript[p, t-1])) = ((Subscript[p, t])/(Subscript[p, t-1])) - 1, of an asset and a benchmark using Ratios
Input: closing pricing of asset and closing price of benchmark

Ratios[asset]

Note: Ratios return TemporalData object. This can be converted to TimeSeries object as follows. When creating time series for simple returns, we will use the same options used by original data or input.

simpleReturnsForAsset = TimeSeries[Ratios[asset], Sequence @@ asset["Options"]] - 1;
simpleReturnsForBenchmark = TimeSeries[Ratios[benchmark], Sequence @@ benchmark["Options"] ] - 1;
DateListPlot[
	{ simpleReturnsForAsset, simpleReturnsForBenchmark}, 
	PlotLegends->{"Asset", "Benchmark"}, PlotLabel -> "Simple Returns", plotOptions
]

Cumulative or Multi-period Returns using simple returns

Compute cumulative returns , (Subscript[CR, t])=(Subscript[G, t])- 1=   { (Underoverscript[ ∏, (i=1), t]) (1 + (Subscript[R, t]))} - 1 using  TimeSeriesMap  
Input: simple returns of an asset

cumulativeReturnsForAssetSR = 
	Block[{currGrossReturn = 1, grossReturn}, 
		grossReturn = TimeSeriesMap[(currGrossReturn = currGrossReturn * ( 1 + #))&, simpleReturnsForAsset];
		grossReturn - 1
	];
DateListPlot[
	cumulativeReturnsForAssetSR, Sequence @@ plotOptions, 
	PlotLabel -> "Cumulative Returns or Multi-period Returns of Asset"
]

Gross Returns

Compute gross return i.e the growth of  $1 investment on the asset using simple returns and TimeSeriesMap
Input: simple returns of an asset and benchmark

grossReturnsForAsset= 
Block[{currGrossReturn = 1}, 
	TimeSeriesMap[(currGrossReturn = currGrossReturn * ( 1 + #))&, simpleReturnsForAsset]
];

grossReturnsForBenchmark = 
	Block[{currGrossReturn = 1}, 
		TimeSeriesMap[(currGrossReturn = currGrossReturn * ( 1 + #))&, simpleReturnsForBenchmark]
	];

Equity curve: Visualize the growth of $1 investment on asset and benchmark

DateListPlot[
	{grossReturnsForAsset, grossReturnsForBenchmark}, 
	PlotLabel -> "Equity Plot: Growth of $1 investment", 
	PlotLegends->{"Asset", "Benchmark"}, 
	Sequence@@plotOptions
]

Rewards: Performance Metrics

Mean Returns

({{Input, Arithmetic Mean Return , Geometric Mean Return}, {Simple Returns ((Subscript[R, t]))
{(Subscript[R, 1]), (Subscript[R, 2]), ..., (Subscript[R, n])}, (Subscript[AM, R]) = (1/n)(Underoverscript[ ∑, (i=1), n]) (Subscript[R, i]), (Subscript[GM, R]) =((((Underoverscript[ ∏, (i=1), n])(1 + (Subscript[R, i]))))^((1/n) )) - 1}, {Log Returns  ((Subscript[r, t]))
{(Subscript[r, 1]), (Subscript[r, 2]), ..., (Subscript[r, n])}, (Subscript[AM, r]) = (1/n)(Underoverscript[ ∑, (i=1), n]) (Subscript[r, i]), (Subscript[GM, r]) = (1/n) (Underoverscript[ ∑, ( i=1), ( n)])(Subscript[r, i])}})

Compute Arithmetic Mean return using Mean:

arithmeticMeanReturnForAsset = Mean @ simpleReturnsForAsset

Compute Geometric Mean return using GeometricMean:

geometricMeanReturnForAsset = GeometricMean[ 1 + simpleReturnsForAsset] - 1

Compute ArithmeticMeanReturn and GeometricMeanReturn for Asset and Benchmark:

Dataset[<|"Asset" -> iMeanReturns[simpleReturnsForAsset], "Benchmark" -> iMeanReturns[simpleReturnsForBenchmark] |>]

Annualized Returns

({{Input, Annualized GM}, {Simple Returns ((Subscript[R, t]))
{(Subscript[R, 1]), (Subscript[R, 2]), ..., (Subscript[R, n])}, Annualized GM =  ((((Underoverscript[ ∏, (i=1), n])(1 + (Subscript[R, i]))))^((scalefactor/n) )) - 1 = (((1 + (Subscript[GM, R])))^(scalefactor ))- 1 }, {Log Returns  ((Subscript[r, t]))
{(Subscript[r, 1]), (Subscript[r, 2]), ..., (Subscript[r, n])}, Annualized GM =  ((1/n) (Underoverscript[ ∑, ( i=1), ( n)])(Subscript[r, i])) * scalefactor }})

If (Subscript[t, i])to (Subscript[t, i+1]), is one holding period, then scale factor is the number of holding periods in one year.  For instance, 
scale factor = ({{{, ({{12, for 1 month  holding period }, {252
52
6   , for 1 day holding period ( considering there are only 252 Trading days in a year)
for one week holding period
for 2 month holding period}})}})

Map[#["MinimumTimeIncrement"]&, {simpleReturnsForAsset, simpleReturnsForBenchmark}]

Since we are using monthly data, we will use 12 as a scale factor to compute annualized returns

annualizedReturnsForAsset = With[{scalefactor = 12}, GeometricMean[(1+ simpleReturnsForAsset)]^scalefactor - 1 ];
annualizedReturnsForBenchmark = With[{scalefactor = 12}, GeometricMean[(1+ simpleReturnsForBenchmark)]^scalefactor - 1 ];
{annualizedReturnsForAsset, annualizedReturnsForBenchmark }

Compute AnnualizedMeanReturn for Asset and Benchmark using ArithmeticMeanReturn and GeometricMeanReturn:

Dataset[
	<|
	"Asset" ->iAnnualizedMeanReturns[simpleReturnsForAsset, 12], 
	"Benchmark" -> iAnnualizedMeanReturns[simpleReturnsForBenchmark,12]
	|>
]

Excess Returns and Risk Premiums

({{Input, Excess Returns, Risk Premium Using Arithmetic Mean, Risk Premium Using Geometric Mean}, {Simple Returns ((Subscript[R, t]))
{(Subscript[R, 1]), (Subscript[R, 2]), ..., (Subscript[R, n])}, (Subscript[R, t])- RiskFreeRate , (1/n) (Underoverscript[∑ , (i=1), n])((Subscript[R, i])- riskFreeRate), ((((Underoverscript[ ∏, (i=1), n])(1 + (Subscript[R, i]) - riskFreeRate)))^((1/n) )) - 1}, {Log Returns  ((Subscript[r, t]))
{(Subscript[r, 1]), (Subscript[r, 2]), ..., (Subscript[r, n])}, (Subscript[r, t])- RiskFreeRate,  (1/n)(Underoverscript[ ∑, (i=1), n])( (Subscript[r, i]) - riskFreeRate),  (1/n)(Underoverscript[ ∑, (i=1), n])( (Subscript[r, i]) - riskFreeRate)}})

We will use 0.05 risk free rate for examples in this notebook. Risk free rate could be treasury bills.

riskFreeRate = 0.06/100; (* riskfreeRate = 0.06% *)
excessReturnsForAsset = simpleReturnsForAsset - riskFreeRate

Risk premium can be computed using Arithmetic Mean Return and/or Geometric Mean return of Excess returns.

riskPremiumUsingAMForAsset = Mean @ excessReturnsForAsset;
riskPremiumUsingGMForAsset = GeometricMean[( 1 + excessReturnsForAsset) ] - 1;
{riskPremiumUsingAMForAsset , riskPremiumUsingGMForAsset}

iRiskPremium[simpleReturnsForAsset,riskFreeRate]//Dataset

ActiveReturns

ActiveReturn = Annualized Asset Return  - Annualized Benchmark Return 

Compute active returns using simple return asset and benchmark

activeReturns = annualizedReturnsForAsset -  annualizedReturnsForBenchmark

Risks: Performance metrics using returns

Mean Absolute Deviation, Volatility , Variance and Tracking Error

({{Input:{(Subscript[ℛ, 1]), (Subscript[ℛ, 2]), ..., (Subscript[ℛ, n])}, ({{Risk Measures: Variability, Formula / Mathematica Function}, {Mean Absolute Deviation,  (1/n)(∑_i)|(Subscript[ℛ, i])-(OverBar[ℛ])|= MeanDeviation[{(Subscript[ℛ, 1]), (Subscript[ℛ, 2]), ..., (Subscript[ℛ, n])}]}, {Volatility , σ = StandardDeviation[{(Subscript[ℛ, 1]), (Subscript[ℛ, 2]), ..., (Subscript[ℛ, n])}]}, {Variance, variance = Variance[{(Subscript[ℛ, 1]), (Subscript[ℛ, 2]), ..., (Subscript[ℛ, n])}]}, {Annualized Volatility, (Subscript[σ, annualized ])=  (Sqrt[scalefactor]) * σ =(Sqrt[scalefactor])  * StandardDeviation[{(Subscript[ℛ, 1]), (Subscript[ℛ, 2]), ..., (Subscript[ℛ, n])}]}, {Annualized Variance, (Subscript[varaince, annualized ])=  scalefactor * variance = scalefactor *  Variance[{(Subscript[ℛ, 1]), (Subscript[ℛ, 2]), ..., (Subscript[ℛ, n])}]}, {Tracking Error, trackingError =  (Sqrt[scalefactor])* StandardDeviation[{(Subscript[ℛ, 1])-(Subscript[ℛb, 1]) , (Subscript[ℛ, 2])- (Subscript[ℛb, 2]), ..., (Subscript[ℛ, n])- (Subscript[ℛb, n])}], where benchmark returns = (Subscript[{(Subscript[ℛb, i])}, 1]^n) }})}}) 

Note: 
benchmark return could be returns on SP500
risk-free rate => 0 or T-Bill

variability = 
	Block[
		{scalefactor = 12, returnType = simpleReturnsForAsset, excessRet = simpleReturnsForAsset - simpleReturnsForBenchmark}, 
		<|
			"MeanAbsoluteDeviation" -> MeanDeviation[returnType], 
			"Volatility" -> StandardDeviation[returnType], 
			"Variance" -> Variance[returnType], 
			"AnnualizedVolatility" -> (Sqrt[scalefactor]) * StandardDeviation[returnType], 
			"AnnualizedVariance" -> scalefactor * Variance[returnType], 
			"TrackingError" -> StandardDeviation[excessRet] * (Sqrt[scalefactor])
		|>
	];
Dataset @ variability

Value-At-Risk and Expected-Shortfall

investmentToday = 100000;
alpha = 5;
Dataset @ iValueAtRiskAndExpectedShortfall[simpleReturnsForAsset, investmentToday, alpha]

Value-At-Risk, VaR(5):
- There is 5% chance that we will lose more than $16761 in next month if we invest $100000 today. 
- This can also be interpreted: 95% confidence we can be sure that we will not lose more than $16761in next month if we invest $100000 today.

ValueAtRisk-Ratio: 
we could lose more than 16.761% of our investment with 5% chance. 

Expected-Shortfall:
There is 5% chance that we will lose $23577.7 on average in next month if we invest $100000 today

VaR(α) and VaRRatio(α)

Non-parametric estimation of Value-At-Risk, VaR(α) using Quantile function
Input: Simple returns

valueAtRiskForAsset = -investmentToday * Quantile[simpleReturnsForAsset["Values"], alpha/100.]

Computing VaRRatio(α) using VaR(α) and investment

valueAtRiskRatioForAsset = valueAtRiskForAsset / investmentToday

valueAtRiskRatioForAsset * 100

Expected-Shortfall

Estimating Expected-Shortfall using Value-At-Risk, simple returns and investment amount

expectedShortFallForAsset = 
Block[{loss}, 
	loss = investmentToday * simpleReturnsForAsset["Values"];
	Mean @ Cases[loss, l_/; l ≥ valueAtRiskForAsset]
]

expectedShortFallForAsset/investmentToday

Risks: Drawdown Risk Measures

Building blocks for measuring drawdown risks

Drawdowns and RelativeDrawdowns are measures of losses.

Drawdowns represent a loss in dollars if asset price starts falling.

RelativeDrawdowns represent loss in percentage if asset price starts falling.

Running Maximum price

Compute running maximum price using TimeSeriesMap

runningMaximumPriceForAsset = 
Block[
	{currMaxPrice = asset["FirstValue"]}, 
	TimeSeriesMap[
		(currMaxPrice = Max[currMaxPrice, #])&, 
		asset
	]
]

Drawdown

Drawdown(t) =  RunningMaximumPrice(t) - CurrentPrice(t)
Using running maximum price and prices of asset, compute drawdown

drawdownForAsset = runningMaximumPriceForAsset - asset

RelativeDrawdown

RelativeDrawdown(t) = ((Drawdown(t))/(RunningMaximumPrice(t)))
Using drawdown and running maximum prices of asset , compute relative drawdown

relativeDrawdownForAsset = Divide[ drawdownForAsset, runningMaximumPriceForAsset ]

Visualize asset price, drawdown and relative drawdown

Plot asset price, running maximum price and drawdown

DateListPlot[
	{asset, runningMaximumPriceForAsset, drawdownForAsset}, 
	PlotLegends->{"AssetPrice", "RunningMaximumPrice", "Drawdown"}, 
	PlotLabel->"Asset price, running maximum price and drawdown",
	plotOptions
]

Plot drawdown and relative drawdown for assets

Column[
	{
		DateListPlot[drawdownForAsset, PlotLabel -> "Drawdown", plotOptions], 
		DateListPlot[relativeDrawdownForAsset, PlotLabel->"RelativeDrawdown", plotOptions]
	}
]

Performance metrics using RelativeDrawdown

Find the intervals for non-zero relative drawdown

Lets look at the plot of relative drawdown

DateListPlot[relativeDrawdownForAsset, PlotLabel -> "Relative Drawdown", plotOptions]

Count[relativeDrawdownForAsset["Values"], 0 | 0.]

zerosAndOnes = Boole @ Positive@relativeDrawdownForAsset["Values"]
ListLinePlot[zerosAndOnes]

Find the positions of non-zero drawdown using SequencePosition

nonzeroIntervals = SequencePosition[zerosAndOnes, {Repeated[1]}, Overlaps->False]

Length[nonzeroIntervals]

nonzeroRelativeDrawdownListForAsset =
	 With[
		{relativeDrawdownValues = relativeDrawdownForAsset["Values"]}, 
		Map[relativeDrawdownValues[[Span@@#]]&, nonzeroIntervals]
	]

Maximum Drawdown

Find maximum relative drawdown in each non-zero drawdown intervals:

maximumRelativeDrawdownInEachIntervalForAsset = Map[ Max, nonzeroRelativeDrawdownListForAsset]

Maximum relative drawdown in entire investment horizon

overAllMaxRelativeDrawdownForAsset = Max[maximumRelativeDrawdownInEachIntervalForAsset]

Max[relativeDrawdownForAsset] === overAllMaxRelativeDrawdownForAsset

Running maximum relative drawdown:

runningMaximumRelativeDrawdownForAsset = iRunningMaximum[relativeDrawdownForAsset];
DateListPlot[{runningMaximumRelativeDrawdownForAsset,relativeDrawdownForAsset}, plotOptions, PlotLegends->{"Running Maximum Relative Drawdown", "Relative Drawdown"}]

Average Drawdown

Computing average drawdown using maximum drawdown in each non-zero drawdown intervals

averageRelativeDrawdownUsingMDDForAsset = Mean @ maximumRelativeDrawdownInEachIntervalForAsset

Computing average drawdown using all positive drawdown

averageRelativeDrawdownUsingAllPositiveDDForAsset = Mean @ Flatten @ nonzeroRelativeDrawdownListForAsset

Computing average drawdown all drawdown including 0

averageRelativeDrawdownForAsset = Mean @ relativeDrawdownForAsset

Risk-Reward ratios

Sharpe Ratio

Sharpe Ratio is simply a return per unit risk we take on our investment.

Sharpe Ratio = ((Risk Premium )/Volatility) =((MeanReturn[ Excess Returns] )/Volatility)=((MeanReturn[Returns - RiskFreeRate])/(StandardDeviation[Returns]))
MeanReturn could be Arithmetic Mean or Geometric Mean
(Subscript[ExcessReturns, i])= (Subscript[R, i]) - RiskFreeRate

Lets use 0.06% risk free rate for computing Sharpe Ratio

riskFreeRate

sharpeRatio = 
Block[
	{excessReturn = simpleReturnsForAsset - riskFreeRate, volatility , meanreturn}, 
	meanreturn = iMeanReturns[excessReturn];
	volatility = StandardDeviation[excessReturn];
	<|"SharepeRatio" -> Divide[meanreturn , volatility]|>
];
Dataset @ sharpeRatio

Risk-reward tradeoff

Dataset [
	<|
		"RiskFreeAsset" -> <|"MeanReturn" -> riskFreeRate, "Volatility" -> 0|>, 
		"RiskyAsset" -> <|"MeanReturn" -> Mean @ simpleReturnsForAsset, "Volatility" -> StandardDeviation[simpleReturnsForAsset] |>
	|>
]

With[
{
expectedReturn = Mean @ simpleReturnsForAsset,
volatility = StandardDeviation @ simpleReturnsForAsset
}, 
 Show[
ListLinePlot[
	Tooltip@Association[
			"RiskFreeAsset" -> <|"Volatility" -> 0, "MeanReturn" -> riskFreeRate|>, 
			"RiskyAsset" -> <| "Volatility" -> volatility,"MeanReturn" -> expectedReturn |>
		], 
	PlotRange-> {{-0.02, 0.2}, {-0.001, 0.03}}, 
	Axes->False,Frame -> True, AspectRatio -> 1, ImageSize->400,
	PlotMarkers->{Automatic,Medium}, PlotLabel ->"Risk-Reward Tradeoff",
	PlotLegends -> Placed[SwatchLegend[{Red, Green}, {"Excess Return of Risky Asset", "Asset Risk"}], { Left, Top}], 
	FrameLabel -> {"Volatility (Risk)", "Mean Returns (Reward)"}
], 
Graphics[
	{
		{Green, Line[{{0, riskFreeRate}, { volatility,  riskFreeRate }}]}, 
		{Red, Line[{{ volatility, riskFreeRate},{ volatility,  expectedReturn}}]}
	}
]
]
]

Thank you

Question:
Email: anmolb@wolfram.com

Initialization

Closing price of two assets and benchmark

ClearAll[firstDate, lastDate, assetPrice1, assetPrice2, marketPrice];
firstDate = DateObject["2015-01-01"];
lastDate = DateObject["2018-10-16"];

(* assetPrice1 = TimeSeries[FinancialData["NASDAQ:MSFT", "Close", {firstDate, lastDate}], TemporalRegularity->True]; *)
assetPrice1 = TimeSeries[];
(* aassetPrice2 = TimeSeries[FinancialData["NASDAQ:GOOG", "Close", {firstDate, lastDate}], TemporalRegularity->True]; *)
assetPrice2 = TimeSeries[];

(* marketPrice = TimeSeries[FinancialData["SP500", "Close", {firstDate, lastDate}], TemporalRegularity->True] *)
marketPrice = TimeSeries[];
{assetPrice1, assetPrice2, marketPrice} = 
	Block[
		{resample = TimeSeriesResample[TemporalData[{assetPrice1, assetPrice2, marketPrice}], "Intersection"]["Components"]},
		Map[
			TimeSeries[#, TemporalRegularity->True]&, 
			resample 
		]
	];

assetPrice1["DeveloperProperties"]

Building Blocks

Simple Returns, Gross Returns and Cumulative Returns

ClearAll[iSimpleReturns, iGrossReturns, iCumulativeReturns];
iSimpleReturns[prices_TemporalData]/; SameQ[prices["PathCount"], 1] := 
	With[{tdOpts = prices["Options"]}, 
		TimeSeries[
			Ratios[prices], 
			Sequence @@ tdOpts
		] - 1
	];

iGrossReturns[simplereturns_TemporalData]/; SameQ[simplereturns["PathCount"], 1] := 
	Block[{currGrossReturn = 1}, 
		TimeSeriesMap[(currGrossReturn = currGrossReturn * ( 1 + #))&, simplereturns]
	];

iCumulativeReturns[simplereturns_TemporalData] /; SameQ[simplereturns["PathCount"], 1] := iGrossReturns[simplereturns] - 1

RunningMaximum, Drawdown, RelativeDrawdown, Non-zero Drawdown partition

ClearAll[iRunningMaximum];
iRunningMaximum[data_TemporalData] /; SameQ[data["PathCount"], 1] := 
	Block[{currMax = data["FirstValue"]}, 
		TimeSeriesMap[
			(currMax = Max[currMax, #])&, 
			data
		]
	]
	
ClearAll[iDrawdown];
iDrawdown[prices_TemporalData ]/; SameQ[prices["PathCount"], 1] :=
	With[{runningMaximumPrice = iRunningMaximum[prices]}, 
		Subtract[runningMaximumPrice, prices]
	];
	
ClearAll[iRelativeDrawdown];
iRelativeDrawdown[prices_TemporalData ]/; SameQ[prices["PathCount"], 1] := 
	With[{dd = iDrawdown[prices], rmax = iRunningMaximum[prices]}, 
		Divide[dd, rmax]
	]
	
ClearAll[iNonzeroDrawdownPartition];
iNonzeroDrawdownPartition[drawdown_TemporalData] /; SameQ[drawdown["PathCount"], 1] := 
	Block[{zerosAndOnes, pos, ddvalues}, 
		ddvalues = drawdown["Values"];
		zerosAndOnes = Boole @ Positive @ ddvalues;
		pos = SequencePosition[zerosAndOnes, {Repeated[1]}, Overlaps -> False];
		Map[ddvalues[[Span @@ #]]&, pos]
	]

PositivePart and NegativePart

ClearAll[iPositivePart];
iPositivePart[data_TemporalData] /; SameQ[data["PathCount"] , 1 ]:= TimeSeriesMap[Max[#, 0]&, data];
	
ClearAll[iNegativePart];
iNegativePart[data_TemporalData]/; SameQ[data["PathCount"] , 1 ] := TimeSeriesMap[Min[#, 0]&, data];

Computing Reward Metrics

MeanReturns

ClearAll[iMeanReturns];
iMeanReturns[assetreturns_TemporalData] /;  SameQ[assetreturns["PathCount"], 1] := 
	<| 
		"ArithmeticMeanReturn" -> Mean @ assetreturns, 
		"GeometricMeanReturn" -> GeometricMean[ 1 + assetreturns ] - 1
	|>

AnnualizedReturns

ClearAll[iAnnualizedMeanReturns];
iAnnualizedMeanReturns[assetreturns_TemporalData, scalefactor_] /; SameQ[assetreturns["PathCount"], 1] := 
	<|
		"ArithmeticMeanReturn" -> (Mean @ assetreturns) * scalefactor,
		"GeometricMeanReturn" -> Subtract[GeometricMean[(1 + assetreturns)]^scalefactor , 1]
	|>

ExcessReturns and RiskPremium

ClearAll[iRiskPremium, iExcessReturns, iAnnualizedRiskPremium];
iExcessReturns[assetreturns_TemporalData, riskfreerate_] /; SameQ[assetreturns["PathCount"], 1] := Subtract[assetreturns, riskfreerate];

iRiskPremium[assetreturns_TemporalData, riskfreerate_] /; SameQ[assetreturns["PathCount"], 1] := 
	Block[{excessreturns = iExcessReturns[assetreturns, riskfreerate], riskprem, annualizedriskprem}, 
		riskprem = iMeanReturns[excessreturns];
		<|
			"RiskPremium" -> riskprem
		|>
	]

iAnnualizedRiskPremium[assetreturns_TemporalData, riskfreerate_, scalefactor_] /; SameQ[assetreturns["PathCount"], 1] :=
	Block[{excessreturns = iExcessReturns[assetreturns, riskfreerate], riskprem, annualizedriskprem}, 
		annualizedriskprem = iAnnualizedMeanReturns[excessreturns, scalefactor];
		<|
			"AnnualizedRiskPremium" -> annualizedriskprem
		|>
	]

ActiveReturns

ClearAll[iActiveReturns];
iActiveReturns[assetreturns_TemporalData, benchmarkreturns_TemporalData, scalefactor_] /; SameQ[assetreturns["PathCount"], benchmarkreturns["PathCount"], 1] := 
	<|
		"ActiveReturn" -> Subtract[
							iAnnualizedMeanReturns[assetreturns, scalefactor]["GeometricMeanReturn"], 
							iAnnualizedMeanReturns[benchmarkreturns, scalefactor]["GeometricMeanReturn"]
						]
	|>

Computing Risk Metrics

Variability

ClearAll[iVariability];
iVariability[assetreturns_, scalefactor_]:=
	<|
		"MeanAbsoluteDeviation" -> MeanDeviation[assetreturns], 
		"Volatility" -> StandardDeviation[assetreturns], 
		"Variance" -> Variance[assetreturns], 
		"AnnualizedVolatility" -> (Sqrt[scalefactor]) * StandardDeviation[assetreturns], 
		"AnnualizedVariance" -> scalefactor * Variance[assetreturns]
	|>

iVariability[simpleReturnsForAsset, 12]//Dataset

Tracking Error

ClearAll[iTrackingError];
iTrackingError[assetreturns_, benchmarkreturns_, scalefactor_] :=
	Block[
		{excessRet = iExcessReturns[assetreturns, benchmarkreturns]}, 
		<|
			"TrackingError" -> StandardDeviation[excessRet] * (Sqrt[scalefactor])
		|>
	]

iTrackingError[simpleReturnsForAsset, simpleReturnsForBenchmark, 12]//Dataset

Value-At-Risk and ExpectedShortfall

ClearAll[iValueAtRisk];
iValueAtRisk[assetreturns_TemporalData, investmenttoday_, alpha_] /;SameQ[assetreturns["PathCount"], 1] := 
	With[
		{var = -investmenttoday * Quantile[assetreturns["Values"], alpha/100.]}, 
		<|
			"VaR" -> var, 
			"VaR-Ratio" -> Divide[var, investmenttoday], 
			"Investment" -> investmenttoday, 
			"Alpha" -> alpha
		|>
	]

ClearAll[iExpectedShortfall];
iExpectedShortfall[assetreturns_TemporalData, investmenttoday_, alpha_]/;SameQ[assetreturns["PathCount"], 1] := 
	Block[{loss, var, es}, 
		loss = investmenttoday * assetreturns["Values"];
		var = iValueAtRisk[assetreturns, investmenttoday, alpha]["VaR"];
		es =  Mean[ Cases[loss, l_/; l ≥ var] ];
		<|
			"ExpectedShortfall" -> es, 
			"ExpectedShortfallRatio" -> Divide[es, investmenttoday]
		|>	
	];
	
ClearAll[iValueAtRiskAndExpectedShortfall];
iValueAtRiskAndExpectedShortfall[assetreturns_TemporalData, investmenttoday_, alpha_]/;SameQ[assetreturns["PathCount"], 1] := 
Block[{loss, var, es}, 
		loss = investmenttoday * assetreturns["Values"];
		var = iValueAtRisk[assetreturns, investmenttoday, alpha];
		es =  Mean[ Cases[loss, l_/; l ≥ var["VaR"] ] ];
		AssociateTo[
			var, 
			<|
				"ExpectedShortfall" -> es, 
				"ExpectedShortfallRatio" -> Divide[es, investmenttoday]
			|>	
		]
	]

iValueAtRisk[simpleReturnsForAsset, investmentToday, 5]

iExpectedShortfall[simpleReturnsForAsset, investmentToday, 5]

Drawdown Risk Measures: MaximumDrawdown and AverageDrawdown

ClearAll[iMaximumDrawdown];

iMaximumDrawdown[drawdown_TemporalData] /;SameQ[drawdown["PathCount"], 1] :=
	<|
		"MaximumDrawdown" -> Max[drawdown], 
		"MaximumDrawdownList" -> Map[ Max, iNonzeroDrawdownPartition[drawdown] ]
	|>

ClearAll[iAverageDrawdown];
iAverageDrawdown[drawdown_TemporalData] /; SameQ[drawdown["PathCount"], 1] := 
	Mean[ 
		iMaximumDrawdown[drawdown]["MaximumDrawdownList"]
	]

Downside Risk Measures

ClearAll[iDownsideRiskMeasures];
iDownsideRiskMeasures[assetreturns_, minimumtargetreturn_] := 
	Block[
		{
			excessReturnUsingMTR, excessReturnUsingMean, 
			negativePartOfExcessReturnUsingMTR, negativePartOfExcessReturnUsingMean , 
			positivePartOfExcessReturnUsingMTR, positivePartOfExcessReturnUsingMean 
		}, 
			excessReturnUsingMTR = iExcessReturns[assetreturns, minimumtargetreturn];
			excessReturnUsingMean = iExcessReturns[assetreturns, Mean @ assetreturns];
			negativePartOfExcessReturnUsingMTR = iNegativePart[excessReturnUsingMTR];
			negativePartOfExcessReturnUsingMean = iNegativePart[excessReturnUsingMean];
			positivePartOfExcessReturnUsingMTR = iPositivePart[excessReturnUsingMTR];
			positivePartOfExcessReturnUsingMean = iPositivePart[excessReturnUsingMean];
			
			Association[
				"DownsideRisk" -> RootMeanSquare[negativePartOfExcessReturnUsingMTR], 
				"Semi-StandardDeviation" -> RootMeanSquare[negativePartOfExcessReturnUsingMean], 
				"DownsidePotential" ->  Mean[negativePartOfExcessReturnUsingMean], 
				"UpsideRisk" -> RootMeanSquare[positivePartOfExcessReturnUsingMTR] , 
				"UpsidePotential" -> Mean[positivePartOfExcessReturnUsingMean]
			]
		]

Computing Risk-Reward Ratios

Sharpe Ratio and Annualized Sharpe Ratio

ClearAll[iSharpeRatio];
iSharpeRatio[assetreturns_, riskFreeRate_] :=
	Block[{riskprem, riskPremium, annualizedRiskPremium, volatility, annualizedVolatility}, 
		riskprem = iRiskPremium[assetreturns, riskFreeRate];
		riskPremium = riskprem["RiskPremium"];
		volatility = StandardDeviation[assetreturns];
		<|
			"SharpeRatio" -> Divide[ riskPremium , volatility ], 
			"RiskFreeRate" -> AssociationThread[Keys @ riskPremium, {riskFreeRate, riskFreeRate}]
		|>
	]

ClearAll[iAnnualizedSharpeRatio];
iAnnualizedSharpeRatio[assetreturns_, riskFreeRate_, scalefactor_] :=
	Block[{riskprem, riskPremium, annualizedRiskPremium, volatility, annualizedVolatility}, 
		annualizedRiskPremium = iAnnualizedRiskPremium[assetreturns, riskFreeRate, scalefactor]["AnnualizedRiskPremium"];
		volatility = StandardDeviation[assetreturns];
		annualizedVolatility = (Sqrt[scalefactor]) * volatility;
		<|
			"AnnualizedSharpeRatio" -> Divide[annualizedRiskPremium, annualizedVolatility]
		|>
	]

iSharpeRatio[simpleReturnsForAsset, riskFreeRate]//Dataset

iAnnualizedSharpeRatio[simpleReturnsForAsset, riskFreeRate, 12]//Dataset

InformationRatio

ClearAll[iInformationRatio];
iInformationRatio[assetreturns_TemporalData, benchmarkreturns_TemporalData, scalefactor_] /; SameQ[assetreturns["PathCount"], benchmarkreturns["PathCount"], 1] :=
	Block[
		{
			activereturn = iActiveReturns[assetreturns, benchmarkreturns, scalefactor], 
			trackingError = iTrackingError[assetreturns, benchmarkreturns, scalefactor]
		},
		<|"InformationRatio" -> Divide[activereturn["ActiveReturn"], trackingError["TrackingError"] ]|>
	]

iInformationRatio[simpleReturnsForAsset, simpleReturnsForBenchmark,12]

DrawdownRatios: CalmarRatio, SterlingRatio and BurkeRatio

ClearAll[iDrawdownRatios];
iDrawdownRatios[prices_TemporalData, scalefactor_] /; SameQ[prices["PathCount"], 1] := 
	Block[{assetreturns, annualizedReturns, relativeDD, maxDD}, 
		assetreturns = iSimpleReturns[prices];
		annualizedReturns = iAnnualizedMeanReturns[assetreturns, scalefactor];
		relativeDD = iRelativeDrawdown[prices];
		maxDD = iMaximumDrawdown[relativeDD];
		<|
			"CalmarRatio" -> Divide[annualizedReturns, maxDD["MaximumDrawdown"]], 
			"SterlingRatio" -> Divide[ annualizedReturns, maxDD["MaximumDrawdown"] + 0.1 ], 
			"BurkeRatio" -> Divide[ annualizedReturns, Sqrt[ Total [(maxDD["MaximumDrawdownList"])^2] ] ]
		|>
		
	]

iDrawdownRatios[asset, 12]//Dataset

DownsideRisk Ratios: Sortino Ratio and Omega Ratio

ClearAll[iDownsideRiskRatios];
iDownsideRiskRatios[assetreturns_, minimumtargetreturn_] := 
	Block[ 
		{
			meanReturn = iMeanReturns[assetreturns], 
			downsiderisk, 
			excessReturnUsingMTR, 
			negativePartOfExcessReturnUsingMTR,
			positivePartOfExcessReturnUsingMTR
		}, 
		excessReturnUsingMTR = iExcessReturns[assetreturns, minimumtargetreturn];
		negativePartOfExcessReturnUsingMTR = iNegativePart[excessReturnUsingMTR];
		positivePartOfExcessReturnUsingMTR = iPositivePart[excessReturnUsingMTR];
		downsiderisk = RootMeanSquare[negativePartOfExcessReturnUsingMTR];
		
		<|
			"SortinoRatio" -> Divide[ meanReturn, downsiderisk], 
			"OmegaRatio" -> Abs[ 
								Divide[
									Total @ positivePartOfExcessReturnUsingMTR, 
									Total @ negativePartOfExcessReturnUsingMTR
								]
							]
		|>
	]

iDownsideRiskRatios[simpleReturnsForAsset, mtr]//Dataset