Added function to generate test data and tests for volatility
This commit is contained in:
parent
97731b4c12
commit
c992905bf6
@ -3,18 +3,37 @@ import math
|
||||
import random
|
||||
|
||||
import pytest
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from fincal.core import AllFrequencies, Frequency
|
||||
from fincal.exceptions import DateNotFoundError
|
||||
from fincal.fincal import TimeSeries, create_date_series
|
||||
from fincal.utils import FincalOptions
|
||||
|
||||
|
||||
def create_prices(s0: float, mu: float, sigma: float, num_prices: int) -> list:
|
||||
"""Generates a price following a geometric brownian motion process based on the input of the arguments:
|
||||
- s0: Asset inital price.
|
||||
- mu: Interest rate expressed annual terms.
|
||||
- sigma: Volatility expressed annual terms.
|
||||
- seed: seed for the random number generator
|
||||
- num_prices: number of prices to generate
|
||||
"""Generates a price following a geometric brownian motion process based on the input of the arguments.
|
||||
|
||||
Since this function is used only to generate data for tests, the seed is fixed as 1234.
|
||||
Many of the tests rely on exact values generated using this seed.
|
||||
If the seed is changed, those tests will fail.
|
||||
|
||||
Parameters:
|
||||
------------
|
||||
s0: float
|
||||
Asset inital price.
|
||||
|
||||
mu: float
|
||||
Interest rate expressed annual terms.
|
||||
|
||||
sigma: float
|
||||
Volatility expressed annual terms.
|
||||
|
||||
num_prices: int
|
||||
number of prices to generate
|
||||
|
||||
Returns:
|
||||
--------
|
||||
Returns a list of values generated using GBM algorithm
|
||||
"""
|
||||
|
||||
random.seed(1234) # WARNING! Changing the seed will cause most tests to fail
|
||||
@ -28,68 +47,124 @@ def create_prices(s0: float, mu: float, sigma: float, num_prices: int) -> list:
|
||||
return all_values
|
||||
|
||||
|
||||
def create_data():
|
||||
"""Creates TimeSeries data"""
|
||||
def create_test_timeseries(
|
||||
frequency: Frequency, num: int = 1000, skip_weekends: bool = False, mu: float = 0.1, sigma: float = 0.05
|
||||
) -> TimeSeries:
|
||||
"""Creates TimeSeries data
|
||||
|
||||
dates = create_date_series("2017-01-01", "2020-10-31", "D", skip_weekends=True)
|
||||
values = create_prices(1000, 0.1, 0.05, 1000)
|
||||
ts = TimeSeries(dict(zip(dates, values)), frequency="D")
|
||||
Parameters:
|
||||
-----------
|
||||
frequency: Frequency
|
||||
The frequency of the time series data to be generated.
|
||||
|
||||
num: int
|
||||
Number of date: value pairs to be generated.
|
||||
|
||||
skip_weekends: bool
|
||||
Whether weekends (saturday, sunday) should be skipped.
|
||||
Gets used only if the frequency is daily.
|
||||
|
||||
mu: float
|
||||
Mean return for the values.
|
||||
|
||||
sigma: float
|
||||
standard deviation of the values.
|
||||
|
||||
Returns:
|
||||
--------
|
||||
Returns a TimeSeries object
|
||||
"""
|
||||
|
||||
start_date = datetime.datetime(2017, 1, 1)
|
||||
timedelta_dict = {
|
||||
frequency.freq_type: int(frequency.value * num * (7 / 5 if frequency == "D" and skip_weekends else 1))
|
||||
}
|
||||
end_date = start_date + relativedelta(**timedelta_dict)
|
||||
dates = create_date_series(start_date, end_date, frequency.symbol, skip_weekends=skip_weekends)
|
||||
values = create_prices(1000, mu, sigma, num)
|
||||
ts = TimeSeries(dict(zip(dates, values)), frequency=frequency.symbol)
|
||||
return ts
|
||||
|
||||
|
||||
class TestReturns:
|
||||
def test_returns_calc(self):
|
||||
ts = create_data()
|
||||
ts = create_test_timeseries()
|
||||
returns = ts.calculate_returns(
|
||||
"2020-01-01", annual_compounded_returns=False, interval_type="years", interval_value=1
|
||||
"2020-01-01", annual_compounded_returns=False, return_period_unit="years", return_period_value=1
|
||||
)
|
||||
assert round(returns[1], 6) == 0.112913
|
||||
|
||||
returns = ts.calculate_returns(
|
||||
"2020-04-01", annual_compounded_returns=False, interval_type="months", interval_value=3
|
||||
"2020-04-01", annual_compounded_returns=False, return_period_unit="months", return_period_value=3
|
||||
)
|
||||
assert round(returns[1], 6) == 0.015908
|
||||
|
||||
returns = ts.calculate_returns(
|
||||
"2020-04-01", annual_compounded_returns=True, interval_type="months", interval_value=3
|
||||
"2020-04-01", annual_compounded_returns=True, return_period_unit="months", return_period_value=3
|
||||
)
|
||||
assert round(returns[1], 6) == 0.065167
|
||||
|
||||
returns = ts.calculate_returns(
|
||||
"2020-04-01", annual_compounded_returns=False, interval_type="days", interval_value=90
|
||||
"2020-04-01", annual_compounded_returns=False, return_period_unit="days", return_period_value=90
|
||||
)
|
||||
assert round(returns[1], 6) == 0.017673
|
||||
|
||||
returns = ts.calculate_returns(
|
||||
"2020-04-01", annual_compounded_returns=True, interval_type="days", interval_value=90
|
||||
"2020-04-01", annual_compounded_returns=True, return_period_unit="days", return_period_value=90
|
||||
)
|
||||
assert round(returns[1], 6) == 0.073632
|
||||
|
||||
with pytest.raises(DateNotFoundError):
|
||||
ts.calculate_returns("2020-04-04", interval_type="days", interval_value=90, as_on_match="exact")
|
||||
ts.calculate_returns("2020-04-04", return_period_unit="days", return_period_value=90, as_on_match="exact")
|
||||
with pytest.raises(DateNotFoundError):
|
||||
ts.calculate_returns("2020-04-04", interval_type="months", interval_value=3, prior_match="exact")
|
||||
ts.calculate_returns("2020-04-04", return_period_unit="months", return_period_value=3, prior_match="exact")
|
||||
|
||||
def test_date_formats(self):
|
||||
ts = create_data()
|
||||
ts = create_test_timeseries()
|
||||
FincalOptions.date_format = "%d-%m-%Y"
|
||||
with pytest.raises(ValueError):
|
||||
ts.calculate_returns("2020-04-10", annual_compounded_returns=True, interval_type="days", interval_value=90)
|
||||
ts.calculate_returns(
|
||||
"2020-04-10", annual_compounded_returns=True, return_period_unit="days", return_period_value=90
|
||||
)
|
||||
|
||||
returns1 = ts.calculate_returns("2020-04-01", interval_type="days", interval_value=90, date_format="%Y-%m-%d")
|
||||
returns2 = ts.calculate_returns("01-04-2020", interval_type="days", interval_value=90)
|
||||
returns1 = ts.calculate_returns(
|
||||
"2020-04-01", return_period_unit="days", return_period_value=90, date_format="%Y-%m-%d"
|
||||
)
|
||||
returns2 = ts.calculate_returns("01-04-2020", return_period_unit="days", return_period_value=90)
|
||||
assert round(returns1[1], 6) == round(returns2[1], 6) == 0.073632
|
||||
|
||||
FincalOptions.date_format = "%m-%d-%Y"
|
||||
with pytest.raises(ValueError):
|
||||
ts.calculate_returns("2020-04-01", annual_compounded_returns=True, interval_type="days", interval_value=90)
|
||||
ts.calculate_returns(
|
||||
"2020-04-01", annual_compounded_returns=True, return_period_unit="days", return_period_value=90
|
||||
)
|
||||
|
||||
returns1 = ts.calculate_returns("2020-04-01", interval_type="days", interval_value=90, date_format="%Y-%m-%d")
|
||||
returns2 = ts.calculate_returns("04-01-2020", interval_type="days", interval_value=90)
|
||||
returns1 = ts.calculate_returns(
|
||||
"2020-04-01", return_period_unit="days", return_period_value=90, date_format="%Y-%m-%d"
|
||||
)
|
||||
returns2 = ts.calculate_returns("04-01-2020", return_period_unit="days", return_period_value=90)
|
||||
assert round(returns1[1], 6) == round(returns2[1], 6) == 0.073632
|
||||
|
||||
def test_limits(self):
|
||||
ts = create_data()
|
||||
FincalOptions.date_format = "%Y-%m-%d"
|
||||
ts = create_test_timeseries()
|
||||
with pytest.raises(DateNotFoundError):
|
||||
ts.calculate_returns("2020-11-25", interval_type="days", interval_value=90, closest_max_days=10)
|
||||
ts.calculate_returns("2020-11-25", return_period_unit="days", return_period_value=90, closest_max_days=10)
|
||||
|
||||
|
||||
class TestVolatility:
|
||||
def test_daily_ts(self):
|
||||
ts = create_test_timeseries(AllFrequencies.D)
|
||||
assert len(ts) == 1000
|
||||
sd = ts.volatility(annualize_volatility=False)
|
||||
assert round(sd, 6) == 0.002622
|
||||
sd = ts.volatility()
|
||||
assert round(sd, 6) == 0.050098
|
||||
sd = ts.volatility(annual_compounded_returns=True)
|
||||
assert round(sd, 4) == 37.9329
|
||||
sd = ts.volatility(return_period_unit="months", annual_compounded_returns=True)
|
||||
assert round(sd, 4) == 0.6778
|
||||
sd = ts.volatility(return_period_unit="years")
|
||||
assert round(sd, 6) == 0.023164
|
||||
sd = ts.volatility(from_date="2017-10-01", to_date="2019-08-31", annualize_volatility=True)
|
||||
assert round(sd, 6) == 0.050559
|
||||
|
Loading…
Reference in New Issue
Block a user