|
| 1 | +""" |
| 2 | +Volatility is a statistical measure of the dispersion of returns for a |
| 3 | +given portfolio. In finance, it is commonly used as a proxy for risk. |
| 4 | +This function calculates the standard deviation of daily log returns and |
| 5 | +annualizes it, giving an estimate of how much the portfolio's value |
| 6 | +fluctuates on a yearly basis. |
| 7 | +""" |
| 8 | + |
| 9 | +import pandas as pd |
| 10 | +import numpy as np |
| 11 | +from investing_algorithm_framework import BacktestReport |
| 12 | + |
| 13 | + |
| 14 | +def get_volatility(backtest_report: BacktestReport) -> float: |
| 15 | + """ |
| 16 | + Calculate the annualized volatility of portfolio net values. |
| 17 | +
|
| 18 | + Args: |
| 19 | + backtest_report: BacktestReport object with snapshots and |
| 20 | + number_of_days |
| 21 | +
|
| 22 | + Returns: |
| 23 | + Float: Annualized volatility as a float |
| 24 | + """ |
| 25 | + snapshots = backtest_report.get_snapshots() |
| 26 | + |
| 27 | + if len(snapshots) < 2: |
| 28 | + return 0.0 # Not enough data to calculate volatility |
| 29 | + |
| 30 | + # Build DataFrame from snapshots |
| 31 | + records = [ |
| 32 | + (snapshot.total_value, snapshot.created_at) for snapshot in snapshots |
| 33 | + ] |
| 34 | + df = pd.DataFrame(records, columns=['total_value', 'created_at']) |
| 35 | + df['created_at'] = pd.to_datetime(df['created_at']) |
| 36 | + df = df.sort_values('created_at').drop_duplicates('created_at').copy() |
| 37 | + |
| 38 | + # Calculate log returns |
| 39 | + df['log_return'] = np.log(df['total_value'] / df['total_value'].shift(1)) |
| 40 | + df = df.dropna() |
| 41 | + |
| 42 | + if df.empty: |
| 43 | + return 0.0 |
| 44 | + |
| 45 | + daily_volatility = df['log_return'].std() |
| 46 | + |
| 47 | + # Estimate trading days per year based on snapshot frequency |
| 48 | + total_days = backtest_report.number_of_days |
| 49 | + num_observations = len(df) |
| 50 | + |
| 51 | + if total_days > 0: |
| 52 | + trading_days_per_year = (num_observations / total_days) * 365 |
| 53 | + else: |
| 54 | + trading_days_per_year = 252 # Default fallback |
| 55 | + |
| 56 | + return daily_volatility * np.sqrt(trading_days_per_year) |
0 commit comments