Today, we begin a project to build a Shiny application that allows a user to build a portfolio and calculate/visualize its Sortino Ratio.

The final app is viewable here but we’ll spend the next 3 posts constructing that.

By way of brief background and motivation for this project, the Sortino Ratio is a measure of the return/risk ratio of a portfolio and is an important tool for evaluating the risk adjusted returns of a portfolio and the skill of the portfolio manager. A higher Sortino indicates a portfolio manager who is generating higher returns per unit of risk absorbed. Identifying better performing portfolios and portfolio managers is crucial for quants, data scientist and analysts at banks, endowments, investment advisors, funds of funds, hedge funds - all enterprise-level participants in the world of asset management. From a data science perspective, we want to accomplish this using a reproducible work flow and an appealing, digestible end product. As someone who used to work in finance and now works at RStudio, I find a Sortino Ratio Shiny app to be a thrilling combination of portfolio theory, statistical analysis and data visualization. Let’s get to it!

The Sortino Ratio equation is as follows:

\[Sortino~Ratio_{portfolio}=\frac{(\overline{Return_{portfolio}-MAR})}{\sqrt{\sum_{t=1}^n min(R_t-MAR,~0)^2}/n}\]

The denominator in that equation (called the Downside Deviation, semi-deviation or downside risk) can be thought of as the deviation of the returns that fall below some target rate of return for the portfolio. That target rate is called the Minimum Acceptable Rate, or MAR. The numerator is the mean portfolio return minus the MAR and can be thought of as excess returns.

The theory behind the Sortino Ratio is that the riskiness of a portfolio is better measured by the deviation of returns *below* a target return, instead of by the standard deviation of all returns. This stands in contradistinction to the more commonly used Sharpe Ratio, which measures return/risk by the ratio of the returns above the risk free rate divided by the standard deviation of *all* returns. By way of history, Harry Markowitz, Nobel laureate and father of modern portfolio theory, noted that downside deviation might be a better measure of risk than the standard deviation of all returns, but its calculation was computationally too expensive^{1} (it was 1959, if he only he’d had R on his laptop).

Frank Sortino’s original article on downside deviation, “On the Use and Misuse of Downside Risk,” was published in the Journal of Portfolio Management in 1996 but you’ll need a paid subscription to access that.

More background is available from the CME group, Red Rock Capital and CFA pubs.

As for our project, we will proceed in 3 steps:

```
1. Build a portfolio and calculate the Sortino Ratio using 3 methods (today's post)
2. Visualize the Sortino Ratio using ggplot and highcharter (next week)
3. Wrap to an interactive Shiny App (in two weeks)
```

When working with the Sortino Ratio, we have two critical choices: how to construct the portfolio using assets and weights, and which MAR to use. Our Shiny application at project’s end will allow a user to make these choices and see how the Sortino Ratio changes. For today, we will go with the following portfolio and MAR:

Assets and Weights

```
+ SPY (S&P500 fund) weighted 25%
+ EFA (a non-US equities fund) weighted 25%
+ IJS (a small-cap value fund) weighted 20%
+ EEM (an emerging-mkts fund) weighted 20%
+ AGG (a bond fund) weighted 10%
```

Minimum Acceptable Rate

```
+ MAR = .008 or .8%
+ Note we are holding this portfolio to a higher standard than being above 0%.
```

Let’s load our packages:

```
library(tidyverse)
library(tidyquant)
library(timetk)
```

First, we import daily prices for the five ETFs, using `getSymbols`

to grab the data, `map(~Ad(get(.)))`

to select adjusted prices only, and `reduce(merge)`

to mash our five prices into one `xts`

object.

```
# The symbols vector holds our tickers.
symbols <- c("SPY","EFA", "IJS", "EEM","AGG")
# The prices object will hold our raw price data..
prices <-
getSymbols(symbols, src = 'yahoo', from = "2005-01-01",
auto.assign = TRUE, warnings = FALSE) %>%
map(~Ad(get(.))) %>%
reduce(merge) %>%
`colnames<-`(symbols)
```

Next we choose our asset weights and assign them to the variable `w`

. We will also assign the MAR of .008 to the variable `MAR`

.

```
w <- c(0.25, 0.25, 0.20, 0.20, 0.10)
MAR <- .008
```

Next we convert those to monthly log returns, using two methods. For the first method, we stay in the `xts`

world.

```
prices_monthly <- to.monthly(prices, indexAt = "last", OHLC = FALSE)
asset_returns_xts <- na.omit(Return.calculate(prices_monthly, method = "log"))
```

We invoke the function `Return.portfolio(asset_returns_xts, weights = w)`

from `PerformanceAnalytics`

and pass in our asset returns and weights. This will return portfolio returns in `xts`

format.

`portfolio_returns_xts <- Return.portfolio(asset_returns_xts, weights = w)`

We now have an `xts`

object of portfolio returns called `portfolio_returns_xts`

.

Now let’s perform the same transformations in the tidy world.

First, we go from daily prices to monthly asset returns.

```
# Tidyverse method, to long, tidy format
asset_returns_long <-
prices %>%
to.monthly(indexAt = "last", OHLC = FALSE) %>%
tk_tbl(preserve_index = TRUE, rename_index = "date") %>%
gather(asset, returns, -date) %>%
group_by(asset) %>%
mutate(returns = (log(returns) - log(lag(returns))))
```

For portfolio returns, we call the `tq_portfolio`

function from `tidyquant`

.

```
portfolio_returns_tidy <-
asset_returns_long %>%
tq_portfolio(assets_col = asset,
returns_col = returns,
weights = w,
col_rename = "returns")
```

We now have a tidy `tibble`

object of portfolio returns called `portfolio_returns_tidy`

.

Let’s take a quick peek at both of our objects for a sanity check.

`head(portfolio_returns_xts)`

```
## portfolio.returns
## 2005-02-28 0.03829297
## 2005-03-31 -0.03418759
## 2005-04-29 -0.02018237
## 2005-05-31 0.02441793
## 2005-06-30 0.02032339
## 2005-07-29 0.04228071
```

`head(portfolio_returns_tidy)`

```
## # A tibble: 6 x 2
## date returns
## <date> <dbl>
## 1 2005-01-31 0.00000000
## 2 2005-02-28 0.03829297
## 3 2005-03-31 -0.03418759
## 4 2005-04-29 -0.02018237
## 5 2005-05-31 0.02441793
## 6 2005-06-30 0.02032339
```

There is one big difference that we will handle below: for January of 2005, `portfolio_returns_tidy`

contains `0.00`

, and `portfolio_returns_xts`

excludes the observation completely. That will make a difference because 0.00 is below our MAR.

On to the Sortino analysis. Calculating the Sortino Ratio in the `xts`

world is almost depressingly convenient. We call `SortinoRatio(portfolio_returns_xts, MAR = MAR)`

, passing our portfolio returns and MAR tot he built-in function from `PerformanceAnalytics`

.

```
sortino_xts <-
SortinoRatio(portfolio_returns_xts, MAR = MAR) %>%
`colnames<-`("ratio")
```

From a substantive perspective, we could stop here and start visualizing with `highcharter`

.

Instead, we will run the calculation by-hand, implementing the equation for the Sortino Ratio via pipes and `dplyr`

. It’s not a verbose piped workflow. In short, we call `summarise(ratio = mean(returns - MAR)/sqrt(sum(pmin(returns - MAR, 0)^2)/nrow(.)))`

.

Note the use of `slice(-1)`

to remove the first row. I want to delete that first 0.00 for January of 2005 to be consistent with the `xts`

operations, but that is an important choice and one that could be questioned. Perhaps we should instead re-wrangle our `xts`

object to make it consistent? Either way, we want to be explicit about the choice so that others can reproduce this work later.

```
sortino_byhand <-
portfolio_returns_tidy %>%
slice(-1) %>%
summarise(ratio = mean(returns - MAR)/sqrt(sum(pmin(returns - MAR, 0)^2)/nrow(.)))
sortino_byhand
```

```
## # A tibble: 1 x 1
## ratio
## <dbl>
## 1 -0.06831889
```

Now on to `tidyquant`

, which allows us to apply the `SortinoRatio`

function from `PerformanceAnalytics`

to a `tibble`

. As long as we are passing it the same data as we passed originally with the `xts`

object, we expect the same result.

```
sortino_tidy <-
portfolio_returns_tidy %>%
slice(-1) %>%
tq_performance(Ra = returns,
performance_fun = SortinoRatio,
MAR = MAR,
method = "full") %>%
`colnames<-`("ratio")
```

Let’s compare our 3 Sortino objects.

`sortino_xts[1]`

`## [1] -0.06831889`

`sortino_byhand$ratio`

`## [1] -0.06831889`

`sortino_tidy$ratio`

`## [1] -0.06831889`

We have consistent results from `xts`

, `tidyquant`

and our by-hand piped calculation. It might feel like a lot of work to get the same result three times but it forced us to look under the hood of the built-in functions and it might serve us well in the future should we have data or a project that fits better with one of the three methods.

That’s all for today. Next time we will visualize the Sortino Ratio and its data slicing implications using `ggplot2`

and `highcharter`

. Thanks for reading.

Markowitz, Harry. Portfolio Selection: Efficient Diversification of Investments, John Wiley & Sons, 1959.↩