Skip to content
All library documents

Annualizing Volatility from Intraday FX Returns

Article Quant Q&A · Author: Saeed

Summary

The answer distinguishes daily returns from intraday returns when calculating historical volatility. For close-to-close volatility, it describes taking the standard deviation of daily log returns and scaling by the square root of an assumed number of trading days per year. Its example compares a calculation based on historical prices with a quoted market volatility figure, illustrating the annualization convention used there.

For 30-minute data in a continuously traded market such as FX, each full day contributes many intraday observations, so annualizing the standard deviation requires accounting for both trading days and intervals per day. The stated scaling assumes complete 24-hour coverage and a regular sampling schedule. The response does not resolve the user's return-mean calculations, and its annualization factor is convention dependent; incomplete sessions, market hours, and the chosen number of trading days can change the result.

Key ideas

  • Close-to-close historical volatility is calculated from daily returns and annualized with a square-root-of-days factor.
  • Intraday observations require accounting for the number of intervals in each trading day when annualizing volatility.
  • For 30-minute FX data, the example assumes complete 24-hour days and regular sampling.
  • A market data example is used to illustrate how a volatility calculation can match a quoted historical figure.
  • The annualization convention depends on the market calendar and data coverage.

Tags

Full text
# How to calculate daily returns and volatility from intraday price?


# How to calculate daily returns and volatility from intraday price?












Imagine I have 30-minute price data like this:

```
temp = pd.DataFrame({'price' : np.random.randint(100, 120, 500)}, index = pd.date_range(start = '2024-1-1', freq = '30min', periods = 500))
temp['rets'] = temp.price / temp.price.shift(1)
temp['log_rets'] = np.log(temp['rets'])
temp.dropna(inplace = True)

print(temp)

                    price   rets    log_rets
2024-01-01 00:30:00 111 1.11000 0.10436
2024-01-01 01:00:00 103 0.92793 -0.07480
2024-01-01 01:30:00 100 0.97087 -0.02956
2024-01-01 02:00:00 113 1.13000 0.12222
2024-01-01 02:30:00 108 0.95575 -0.04526
... ... ... ...
2024-01-11 07:30:00 102 0.89474 -0.11123
2024-01-11 08:00:00 106 1.03922 0.03847
2024-01-11 08:30:00 108 1.01887 0.01869
2024-01-11 09:00:00 117 1.08333 0.08004
2024-01-11 09:30:00 116 0.99145 -0.00858
499 rows × 3 columns
```

Are my calculations of average daily and annualized returns and volatility below correct (in percentage, not log)? If yes, do I need to multiply them by 100 to get %?

```
daily_log_rets = temp['log_rets'].resample('B').sum()

# daily:
daily_mean_rets_percentage = (np.exp(daily_log_rets.mean()) - 1)
daily_rets_volatility = (np.exp(daily_log_rets.std()) - 1) 

#annualized:
annualized_mean_rets_percentage = np.exp(daily_log_rets.mean() * 252) - 1
annualized_rets_volatility = (np.exp(daily_log_rets) - 1).std() * 252 ** .5
```

## Answer by AKdemy (score 1)

https://quant.stackexchange.com/a/80606

If zou use the standard close-to-close HV calculation you use daily data. That's why you multiply by sqrt(260) to annualize (or something close to 260, depending on trading days and your preference).

On Bloomberg HVT, looking at AMC US Equity, you see that HV for 10 days was 28.964 for AMC US Equity on 21.7.2023.

In Python:

```
dates = ['21.07.2023', '20.07.2023', '19.07.2023', '18.07.2023', '17.07.2023', '14.07.2023', '13.07.2023', '12.07.2023', '11.07.2023', '10.07.2023', '07.07.2023', '06.06.2023']
vals = [4.4, 4.33, 4.37, 4.31, 4.37, 4.33, 4.44, 4.4, 4.39, 4.24, 4.2, 4.29]
df = pd.DataFrame({"Dates":dates, "Price" : vals})
round(np.std([np.log(df.Price[i]/df.Price[i+1]) for i in range(0,9)], ddof = 1)*np.sqrt(260)*100,3)
```

which yields exactly what BBG shows.

If you have 30 min intervals, you do not have daily returns but 30 min returns. Each day has 48 returns (provided you have a complete 24h trading day like in FX). Hence, you multiply by $sqrt(260*48)$. If you have Bloomberg, look at VOLC and Hist Vol. HF for High frequency, which is really just the 30 min BFIX intervals. If you try to replicate it, you will see it also uses $sqrt(260*48)$.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.