SigmaField

Practice Risk

Write a returns report from daily prices

Easy+10 pointsAbout 20 minutesRisk

The brief

The risk team's weekly report shows an annual return for every stock it covers. Until now the report multiplied the average daily simple return by 252, the trading days in a year.

For one volatile stock that figure says it gained about 4.9% a year, yet the stock ended the period below where it started. The head of risk wants the report fixed, with a short reason for the gap.

Your task

  1. Write daily_returns(prices): a pair of NumPy arrays (simple, log), each one shorter than prices. Raise ValueError for fewer than two prices, a price of zero or below, or a missing price (nan).
  2. Write annualise(prices, per_year=252): a dict with three annual figures. "log" is the mean log return times per_year, and "simple" is the simple return it implies. "arithmetic" is the old figure, the mean simple return times per_year.
  3. Write drag(prices, per_year=252): an estimate of the gap between the arithmetic and log figures. With TT simple returns RtR_t and mm = per_year, it is
drag=m2T∑t=1TRt2\text{drag} = \frac{m}{2T} \sum_{t=1}^{T} R_t^2

per_year is the number of periods in a year: 252 for daily prices, 12 for monthly ones.

The data

PRICES in the code holds 43 daily closing prices of the stock, oldest first, adjusted for splits and dividends.

Hint: Where to start

Case study 1.4.7 defines both returns from the gross return Pt/Pt−1P_t / P_{t-1}. It also lists the bad prices to check for before computing a single return.

Hint: Annualising

Log returns add across time, so the mean daily log return times 252 is the year's log return. Convert it with R=er−1R = e^{r} - 1 (case study 1.4.7). The old figure skips the compounding.

Hint: Why the figures differ

For a small return, ln⁡(1+R)≈R−R2/2\ln(1 + R) \approx R - R^2/2 (case study 1.4.7). So each day's log return falls short of its simple return by about R2/2R^2/2. Large daily swings make the gap large: this is volatility drag.