96 lines
3.8 KiB
Python
96 lines
3.8 KiB
Python
|
import datetime
|
||
|
import math
|
||
|
import random
|
||
|
|
||
|
import pytest
|
||
|
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
|
||
|
"""
|
||
|
|
||
|
random.seed(1234) # WARNING! Changing the seed will cause most tests to fail
|
||
|
all_values = []
|
||
|
for _ in range(num_prices):
|
||
|
s0 *= math.exp(
|
||
|
(mu - 0.5 * sigma**2) * (1.0 / 365.0) + sigma * math.sqrt(1.0 / 365.0) * random.gauss(mu=0, sigma=1)
|
||
|
)
|
||
|
all_values.append(round(s0, 2))
|
||
|
|
||
|
return all_values
|
||
|
|
||
|
|
||
|
def create_data():
|
||
|
"""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")
|
||
|
return ts
|
||
|
|
||
|
|
||
|
class TestReturns:
|
||
|
def test_returns_calc(self):
|
||
|
ts = create_data()
|
||
|
returns = ts.calculate_returns(
|
||
|
"2020-01-01", annual_compounded_returns=False, interval_type="years", interval_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
|
||
|
)
|
||
|
assert round(returns[1], 6) == 0.015908
|
||
|
|
||
|
returns = ts.calculate_returns(
|
||
|
"2020-04-01", annual_compounded_returns=True, interval_type="months", interval_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
|
||
|
)
|
||
|
assert round(returns[1], 6) == 0.017673
|
||
|
|
||
|
returns = ts.calculate_returns(
|
||
|
"2020-04-01", annual_compounded_returns=True, interval_type="days", interval_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")
|
||
|
with pytest.raises(DateNotFoundError):
|
||
|
ts.calculate_returns("2020-04-04", interval_type="months", interval_value=3, prior_match="exact")
|
||
|
|
||
|
def test_date_formats(self):
|
||
|
ts = create_data()
|
||
|
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)
|
||
|
|
||
|
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)
|
||
|
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)
|
||
|
|
||
|
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)
|
||
|
assert round(returns1[1], 6) == round(returns2[1], 6) == 0.073632
|
||
|
|
||
|
def test_limits(self):
|
||
|
ts = create_data()
|
||
|
FincalOptions.date_format = "%Y-%m-%d"
|
||
|
with pytest.raises(DateNotFoundError):
|
||
|
ts.calculate_returns("2020-11-25", interval_type="days", interval_value=90, closest_max_days=10)
|