Browse Source

Expanded the volatility function, added annualisation

switch-to-decimal
Gourav Kumar 2 years ago
parent
commit
793d5b1ad7
  1. 54
      fincal/fincal.py

54
fincal/fincal.py

@ -1,6 +1,7 @@
from __future__ import annotations
import datetime
import math
import statistics
from typing import Iterable, List, Literal, Mapping, Union
@ -191,7 +192,7 @@ class TimeSeries(TimeSeriesCore):
closest: Literal["previous", "next", "exact"] = "previous",
closest_max_days: int = -1,
if_not_found: Literal["fail", "nan"] = "fail",
compounding: bool = True,
annual_compounded_returns: bool = True,
interval_type: Literal["years", "months", "days"] = "years",
interval_value: int = 1,
date_format: str = None,
@ -269,7 +270,7 @@ class TimeSeries(TimeSeriesCore):
return as_on, float("NaN")
returns = current[1] / previous[1]
if compounding:
if annual_compounded_returns:
years = _interval_to_years(interval_type, interval_value)
returns = returns ** (1 / years)
return (current[0] if return_actual_date else as_on), returns - 1
@ -284,7 +285,7 @@ class TimeSeries(TimeSeriesCore):
prior_match: str = "closest",
closest: Literal["previous", "next", "exact"] = "previous",
if_not_found: Literal["fail", "nan"] = "fail",
compounding: bool = True,
annual_compounded_returns: bool = True,
interval_type: Literal["years", "months", "days"] = "years",
interval_value: int = 1,
date_format: str = None,
@ -371,7 +372,7 @@ class TimeSeries(TimeSeriesCore):
for i in dates:
returns = self.calculate_returns(
as_on=i,
compounding=compounding,
annual_compounded_returns=annual_compounded_returns,
interval_type=interval_type,
interval_value=interval_value,
as_on_match=as_on_match,
@ -383,21 +384,58 @@ class TimeSeries(TimeSeriesCore):
rolling_returns.sort()
return self.__class__(rolling_returns, self.frequency.symbol)
@date_parser(1, 2)
def volatility(
self,
start_date: Union[str, datetime.datetime],
end_date: Union[str, datetime.datetime],
annualized: bool = True,
from_date: Union[datetime.date, str],
to_date: Union[datetime.date, str],
frequency: Literal["D", "W", "M", "Q", "H", "Y"] = None,
as_on_match: str = "closest",
prior_match: str = "closest",
closest: Literal["previous", "next", "exact"] = "previous",
if_not_found: Literal["fail", "nan"] = "fail",
annual_compounded_returns: bool = None,
interval_type: Literal["years", "months", "days"] = "days",
interval_value: int = 1,
date_format: str = None,
annualize_volatility: bool = True,
):
"""Calculates the volatility of the time series.add()
The volatility is calculated as the standard deviaion of periodic returns.
The periodicity of returns is based on the periodicity of underlying data.
"""
if frequency is None:
frequency = self.frequency
else:
try:
frequency = getattr(AllFrequencies, frequency)
except AttributeError:
raise ValueError(f"Invalid argument for frequency {frequency}")
if annual_compounded_returns is None:
annual_compounded_returns = False if frequency.days <= 366 else True
rolling_returns = self.calculate_rolling_returns(
from_date=start_date, to_date=end_date, interval_type=self.frequency.freq_type, compounding=False
from_date=from_date,
to_date=to_date,
frequency=frequency.symbol,
as_on_match=as_on_match,
prior_match=prior_match,
closest=closest,
if_not_found=if_not_found,
annual_compounded_returns=annual_compounded_returns,
interval_type=interval_type,
interval_value=interval_value,
)
sd = statistics.stdev(rolling_returns.values)
if annualize_volatility:
if interval_type == "months":
sd *= math.sqrt(12)
elif interval_type == "days":
sd *= math.sqrt(252)
return sd

Loading…
Cancel
Save