Scoring for an example student

As introduced in the Scoring section, the Sharpe Ratio is an essential part of how traders are ranked in this competition. Here, we walk through an example that explains how the Sharpe Ratio would be calculated for an example contest participant.

As a reminder, here’s the Sharpe Ratio definition that we came up with at the end of that section:

\[\begin{align*} {\tt Sharpe\ Ratio} = \frac{R_{p} - r_{f}}{\sigma_{p}} \end{align*}\]

Wherein:

\[\begin{align*} R_{p} & {\sf: Portfolio\ Return} \\ r_{f} & {\sf: Risk\ free\ rate\ of\ return} \\ \sigma_{p} & {\sf: Volatility\ of\ portfolio\ return} \end{align*}\]

What risk-free rate do we use?

The Gothic Hedge Society (who runs the trading competition) reasons that on Day 1, all participants implicitly decide if they’re going to actively trade at all, as opposed to simply putting all of their money into risk-free debt with a maturity near the Competition’s end date.

Sounds like not a lot of fun and not a good way to make a fortune… but since about half of the participants will lose money in this contest, the idea isn’t actually as bad as it might sound.

Regardless, for the duration of the Competition, the value of \(r_{f}\) used to calculate Sharpes does not change, even though new risk-free rates will be published every day by the USDT. The reason why we keep \(r_{f}\) fixed is because we’re comparing your performance as a trader to the “base strategy”: simply buying debt earning \(r_{f}\) on Day 1, which would tie up all your capital for trading until maturity on the last day.

Since the Competition lasts about 3 months, the value we’ll use for \(r_{f}\) will be whatever the USDT publishes as the 3-month CMT rate on the day the Competition starts. This value will be will be posted clearly and conspicuously for everyone.

For this example, let’s suppose that on Day 1 we visited the USDT’s website and found that the 3-month CMT rate was 0.04% (annualized).

We want that on a trading day basis, so we’ll convert: since there are 252 trading days in a year, we divide by 252 (and by 100 because it’s a percent):

Let’s do that now, and store \(r_{f}\) as a variable:

## [1] "The risk-free rate is 0.0001587% / trading day."

–> Now let’s suppose some trading data for a hypothetical Contest participant.

Example Trader’s Data

Let’s say that today’s date is 23 March 2021, and a student has been participating in the competition for seven days.

Her end-of-day (EoD) Net Asset Value (NAV) and daily log returns are:

Note that there’s no way to calculate a return on the very first day because there’s nothing to compare it to.

The student’s daily portfolio returns are the log returns of her daily portfolio value. For example, on 18 March 2021, she started the day with $1,000,100.00 and ended with $1,000,050.00 in total portfolio value. Therefore the return she sees at end-of-day on 18 March 2021 was:

# 'log()' here is the natural log, not base 10.
rtn_18_mar_2021 <- log(1000050.00 / 1000100.00)
format_percent(rtn_18_mar_2021, "% / trading day.")
## [1] "-0.005% / trading day."

Let’s check that, because why not? She started the day with $1,000,100.00 NAV, so that number * (1 + \(return\)) should equal the $1,000,050.00 NAV that her account posted at day’s end:

# check
1000100.00 * (1 + rtn_18_mar_2021)
## [1] 1000050

–> Looks good.

The student’s overall geometric mean rate of return \(R_{p}\) for the entire time period is:

student_gmrr <- student_data %>%
  dplyr::select("Port. Return (%/trd day)") %>%
  dplyr::filter(!is.na(`Port. Return (%/trd day)`)) %>% 
  tibble::deframe() %>% {
    prod(1 + .)^(1/length(.)) - 1
  }
format_percent(student_gmrr, "% / trading day.")
## [1] "0.005848% / trading day."

The student’s excess return is

student_excess_return <- student_gmrr - rf
format_percent(student_excess_return, "% / trading day.")
## [1] "0.005689% / trading day."

The students volatility of returns \(\sigma_{p}\) is the standard deviation of her portfolio returns:

student_vol <- student_data %>%
  dplyr::select("Port. Return (%/trd day)") %>%
  dplyr::filter(!is.na(`Port. Return (%/trd day)`)) %>% 
  tibble::deframe() %>%
  sd()
format_percent(student_vol, "% / trading day.")
## [1] "0.0174% / trading day."

Now we have what we need to calculate the Sharpe Ratio for this student:

\[\begin{align*} {\tt Sharpe\ Ratio} = \frac{R_{p} - r_{f}}{\sigma_{p}} \end{align*}\]

Wherein:

\[\begin{align*} R_{p} & {\sf: Portfolio\ Return} \\ r_{f} & {\sf: Risk\ free\ rate\ of\ return} \\ \sigma_{p} & {\sf: Volatility\ of\ portfolio\ return} \end{align*}\]

Her Sharpe is therefore:

student_Sharpe <- (student_gmrr - rf) / student_vol
print(student_Sharpe)
## [1] 0.3270215

We repeat this calculation every day for every trader in the Competition.

Matching up Calculations w/ Excel and Other Programs (a quick note)

As simple as it may seem, calculating logarithms can differ slightly (or sometimes, significantly) depending on the precision of the program you use. It’s possible to get different answers for the same log calcs in Excel, R, and Python, and the difference gets magnified when calculating GMRR.

Therefore, don’t worry too much if you try to follow along this example and wind up with numbers that differ slightly. For the purposes of fairness in the competition, it only matters that we use the same program for everyone – which, in this case, is the log() function in R.