Compare commits
6 Commits
56af7c33aa
...
4563a89bc2
Author | SHA1 | Date | |
---|---|---|---|
4563a89bc2 | |||
23bc0b71a6 | |||
b4597da4af | |||
f48d79afb9 | |||
4173736014 | |||
b9366541f3 |
29
dict_iter.py
Normal file
29
dict_iter.py
Normal file
@ -0,0 +1,29 @@
|
||||
import pandas
|
||||
|
||||
from fincal.fincal import TimeSeries
|
||||
|
||||
dfd = pandas.read_csv('test_files/nav_history_daily - Copy.csv')
|
||||
dfm = pandas.read_csv('test_files/nav_history_monthly.csv')
|
||||
|
||||
data_d = [(i.date, i.nav) for i in dfd.itertuples() if i.amfi_code == 118825]
|
||||
data_d.sort()
|
||||
data_m = [{'date': i.date, 'value': i.nav} for i in dfm.itertuples()]
|
||||
|
||||
tsd = TimeSeries(data_d, frequency='D')
|
||||
|
||||
md = dict(data_d)
|
||||
counter = 1
|
||||
for i in iter(md):
|
||||
print(i)
|
||||
counter += 1
|
||||
if counter >= 5: break
|
||||
|
||||
print('\n')
|
||||
counter = 1
|
||||
for i in reversed(md):
|
||||
print('rev', i)
|
||||
counter += 1
|
||||
if counter >= 5: break
|
||||
|
||||
x = [next(i) for i in iter(md)]
|
||||
print(x)
|
@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Iterable, List, Literal, Tuple, Union
|
||||
from typing import Iterable, List, Literal, Mapping, Sequence, Tuple, Union
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -15,30 +15,31 @@ class Frequency:
|
||||
freq_type: str
|
||||
value: int
|
||||
days: int
|
||||
symbol: str
|
||||
|
||||
|
||||
class AllFrequencies:
|
||||
D = Frequency('daily', 'days', 1, 1)
|
||||
W = Frequency('weekly', 'days', 7, 7)
|
||||
M = Frequency('monthly', 'months', 1, 30)
|
||||
Q = Frequency('quarterly', 'months', 3, 91)
|
||||
H = Frequency('half-yearly', 'months', 6, 182)
|
||||
Y = Frequency('annual', 'years', 1, 365)
|
||||
D = Frequency('daily', 'days', 1, 1, 'D')
|
||||
W = Frequency('weekly', 'days', 7, 7, 'W')
|
||||
M = Frequency('monthly', 'months', 1, 30, 'M')
|
||||
Q = Frequency('quarterly', 'months', 3, 91, 'Q')
|
||||
H = Frequency('half-yearly', 'months', 6, 182, 'H')
|
||||
Y = Frequency('annual', 'years', 1, 365, 'Y')
|
||||
|
||||
|
||||
def _preprocess_timeseries(
|
||||
data: Union[
|
||||
List[Iterable[Union[str, datetime.datetime, float]]],
|
||||
List[Dict[str, Union[float, datetime.datetime]]],
|
||||
List[Dict[Union[str, datetime.datetime], float]],
|
||||
Dict[Union[str, datetime.datetime], float]
|
||||
Sequence[Iterable[Union[str, datetime.datetime, float]]],
|
||||
Sequence[Mapping[str, Union[float, datetime.datetime]]],
|
||||
Sequence[Mapping[Union[str, datetime.datetime], float]],
|
||||
Mapping[Union[str, datetime.datetime], float]
|
||||
],
|
||||
date_format: str
|
||||
) -> List[Tuple[datetime.datetime, float]]:
|
||||
"""Converts any type of list to the correct type"""
|
||||
|
||||
if isinstance(data, list):
|
||||
if isinstance(data[0], dict):
|
||||
if isinstance(data, Sequence):
|
||||
if isinstance(data[0], Mapping):
|
||||
if len(data[0].keys()) == 2:
|
||||
current_data = [tuple(i.values()) for i in data]
|
||||
elif len(data[0].keys()) == 1:
|
||||
@ -47,7 +48,7 @@ def _preprocess_timeseries(
|
||||
raise TypeError("Could not parse the data")
|
||||
current_data = _preprocess_timeseries(current_data, date_format)
|
||||
|
||||
elif isinstance(data[0], Iterable):
|
||||
elif isinstance(data[0], Sequence):
|
||||
if isinstance(data[0][0], str):
|
||||
current_data = []
|
||||
for i in data:
|
||||
@ -60,7 +61,7 @@ def _preprocess_timeseries(
|
||||
else:
|
||||
raise TypeError("Could not parse the data")
|
||||
|
||||
elif isinstance(data, dict):
|
||||
elif isinstance(data, Mapping):
|
||||
current_data = [(k, v) for k, v in data.items()]
|
||||
current_data = _preprocess_timeseries(current_data, date_format)
|
||||
|
||||
@ -99,8 +100,8 @@ class TimeSeriesCore:
|
||||
def __init__(
|
||||
self,
|
||||
data: List[Iterable],
|
||||
date_format: str = "%Y-%m-%d",
|
||||
frequency=Literal['D', 'W', 'M', 'Q', 'H', 'Y']
|
||||
frequency: Literal['D', 'W', 'M', 'Q', 'H', 'Y'],
|
||||
date_format: str = "%Y-%m-%d"
|
||||
):
|
||||
"""Instantiate a TimeSeries object
|
||||
|
||||
@ -124,42 +125,58 @@ class TimeSeriesCore:
|
||||
data = _preprocess_timeseries(data, date_format=date_format)
|
||||
|
||||
self.time_series = dict(data)
|
||||
self.dates = set(list(self.time_series))
|
||||
if len(self.dates) != len(data):
|
||||
self.dates = list(self.time_series)
|
||||
if len(self.time_series) != len(data):
|
||||
print("Warning: The input data contains duplicate dates which have been ignored.")
|
||||
self.start_date = list(self.time_series)[0]
|
||||
self.end_date = list(self.time_series)[-1]
|
||||
self.start_date = self.dates[0]
|
||||
self.end_date = self.dates[-1]
|
||||
self.frequency = getattr(AllFrequencies, frequency)
|
||||
|
||||
def _get_slice(self, n: int):
|
||||
"""Returns a slice of the dataframe from beginning and end"""
|
||||
|
||||
printable = {}
|
||||
iter_f = iter(self.time_series)
|
||||
first_n = [next(iter_f) for i in range(n//2)]
|
||||
|
||||
iter_b = reversed(self.time_series)
|
||||
last_n = [next(iter_b) for i in range(n//2)]
|
||||
last_n.sort()
|
||||
|
||||
printable['start'] = [str((i, self.time_series[i])) for i in first_n]
|
||||
printable['end'] = [str((i, self.time_series[i])) for i in last_n]
|
||||
return printable
|
||||
|
||||
def __repr__(self):
|
||||
if len(self.time_series) > 6:
|
||||
printable_data_1 = list(self.time_series)[:3]
|
||||
printable_data_2 = list(self.time_series)[-3:]
|
||||
printable_str = "TimeSeries([{}\n\t...\n\t{}])".format(
|
||||
',\n\t'.join([str((i, self.time_series[i])) for i in printable_data_1]),
|
||||
',\n\t'.join([str((i, self.time_series[i])) for i in printable_data_2])
|
||||
printable = self._get_slice(6)
|
||||
printable_str = "{}([{}\n\t ...\n\t {}], frequency={})".format(
|
||||
self.__class__.__name__,
|
||||
',\n\t '.join(printable['start']),
|
||||
',\n\t '.join(printable['end']),
|
||||
repr(self.frequency.symbol)
|
||||
)
|
||||
else:
|
||||
printable_data = self.time_series
|
||||
printable_str = "TimeSeries([{}])".format(',\n\t'.join(
|
||||
[str((i, self.time_series[i])) for i in printable_data]))
|
||||
printable_str = "{}([{}], frequency={})".format(
|
||||
self.__class__.__name__,
|
||||
',\n\t'.join([str(i) for i in self.time_series.items()]),
|
||||
repr(self.frequency.symbol)
|
||||
)
|
||||
return printable_str
|
||||
|
||||
def __str__(self):
|
||||
if len(self.time_series) > 6:
|
||||
printable_data_1 = list(self.time_series)[:3]
|
||||
printable_data_2 = list(self.time_series)[-3:]
|
||||
printable = self._get_slice(6)
|
||||
printable_str = "[{}\n ...\n {}]".format(
|
||||
',\n '.join([str((i, self.time_series[i])) for i in printable_data_1]),
|
||||
',\n '.join([str((i, self.time_series[i])) for i in printable_data_2])
|
||||
',\n '.join(printable['start']),
|
||||
',\n '.join(printable['end']),
|
||||
)
|
||||
else:
|
||||
printable_data = self.time_series
|
||||
printable_str = "[{}]".format(',\n '.join([str((i, self.time_series[i])) for i in printable_data]))
|
||||
printable_str = "[{}]".format(',\n '.join([str(i) for i in self.time_series.items()]))
|
||||
return printable_str
|
||||
|
||||
def __getitem__(self, n):
|
||||
all_keys = list(self.time_series.keys())
|
||||
all_keys = list(self.time_series)
|
||||
if isinstance(n, int):
|
||||
keys = [all_keys[n]]
|
||||
else:
|
||||
@ -171,7 +188,7 @@ class TimeSeriesCore:
|
||||
return item
|
||||
|
||||
def __len__(self):
|
||||
return len(self.time_series.keys())
|
||||
return len(self.time_series)
|
||||
|
||||
def head(self, n: int = 6):
|
||||
keys = list(self.time_series.keys())
|
||||
|
@ -5,14 +5,15 @@ from typing import List, Union
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from .core import AllFrequencies, Frequency, TimeSeriesCore, _preprocess_match_options
|
||||
from .core import AllFrequencies, TimeSeriesCore, _preprocess_match_options
|
||||
|
||||
|
||||
def create_date_series(
|
||||
start_date: datetime.datetime, end_date: datetime.datetime, frequency: Frequency, eomonth: bool = False
|
||||
start_date: datetime.datetime, end_date: datetime.datetime, frequency: str, eomonth: bool = False
|
||||
) -> List[datetime.datetime]:
|
||||
"""Creates a date series using a frequency"""
|
||||
|
||||
frequency = getattr(AllFrequencies, frequency)
|
||||
if eomonth and frequency.days < AllFrequencies.M.days:
|
||||
raise ValueError(f"eomonth cannot be set to True if frequency is higher than {AllFrequencies.M.name}")
|
||||
|
||||
@ -27,6 +28,7 @@ def create_date_series(
|
||||
date = date.replace(day=31)
|
||||
else:
|
||||
date = date.replace(day=1).replace(month=date.month+1) - relativedelta(days=1)
|
||||
if date <= end_date:
|
||||
dates.append(date)
|
||||
|
||||
return dates
|
||||
@ -59,7 +61,7 @@ class TimeSeries(TimeSeriesCore):
|
||||
"""
|
||||
|
||||
eomonth = True if self.frequency.days >= AllFrequencies.M.days else False
|
||||
dates_to_fill = create_date_series(self.start_date, self.end_date, self.frequency, eomonth)
|
||||
dates_to_fill = create_date_series(self.start_date, self.end_date, self.frequency.symbol, eomonth)
|
||||
|
||||
new_ts = dict()
|
||||
for cur_date in dates_to_fill:
|
||||
@ -177,10 +179,14 @@ class TimeSeries(TimeSeriesCore):
|
||||
) -> List[tuple]:
|
||||
"""Calculates the rolling return"""
|
||||
|
||||
all_dates = create_date_series(from_date, to_date, getattr(AllFrequencies, frequency))
|
||||
dates = set(all_dates)
|
||||
try:
|
||||
frequency = getattr(AllFrequencies, frequency)
|
||||
except AttributeError:
|
||||
raise ValueError(f"Invalid argument for frequency {frequency}")
|
||||
|
||||
dates = create_date_series(from_date, to_date, frequency)
|
||||
if frequency == AllFrequencies.D:
|
||||
dates = all_dates.intersection(self.dates)
|
||||
dates = [i for i in dates if i in self.time_series]
|
||||
|
||||
rolling_returns = []
|
||||
for i in dates:
|
||||
@ -193,8 +199,8 @@ class TimeSeries(TimeSeriesCore):
|
||||
closest=closest,
|
||||
)
|
||||
rolling_returns.append((i, returns))
|
||||
self.rolling_returns = rolling_returns
|
||||
return self.rolling_returns
|
||||
rolling_returns.sort()
|
||||
return rolling_returns
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
23
my_test.py
Normal file
23
my_test.py
Normal file
@ -0,0 +1,23 @@
|
||||
import time
|
||||
|
||||
import pandas
|
||||
|
||||
from fincal.fincal import AllFrequencies, Frequency, TimeSeries, create_date_series
|
||||
|
||||
dfd = pandas.read_csv('test_files/nav_history_daily - Copy.csv')
|
||||
dfm = pandas.read_csv('test_files/nav_history_monthly.csv')
|
||||
dfq = pandas.read_csv('test_files/nav_history_quarterly.csv')
|
||||
|
||||
data_d = [(i.date, i.nav) for i in dfd.itertuples()]
|
||||
data_m = [{'date': i.date, 'value': i.nav} for i in dfm.itertuples()]
|
||||
data_q = {i.date: i.nav for i in dfq.itertuples()}
|
||||
|
||||
tsd = TimeSeries(data_d, frequency='D')
|
||||
tsm = TimeSeries(data_m, frequency='M', date_format='%d-%m-%Y')
|
||||
tsq = TimeSeries(data_q, frequency='Q', date_format='%d-%m-%Y')
|
||||
|
||||
start = time.time()
|
||||
# ts.calculate_rolling_returns(datetime.datetime(2015, 1, 1), datetime.datetime(2022, 2, 1), years=1)
|
||||
# fdata = tsd.ffill()
|
||||
# rr = tsd.calculate_rolling_returns(datetime.datetime(2022, 1, 1), datetime.datetime(2022, 2, 1), years=1)
|
||||
print(time.time() - start)
|
4403
test_files/nav_history_daily - Copy.csv
Normal file
4403
test_files/nav_history_daily - Copy.csv
Normal file
File diff suppressed because it is too large
Load Diff
139
test_files/nav_history_daily.csv
Normal file
139
test_files/nav_history_daily.csv
Normal file
@ -0,0 +1,139 @@
|
||||
amfi_code,date,nav
|
||||
118825,01-11-2021,87.925
|
||||
119528,02-11-2021,378.51
|
||||
118825,02-11-2021,87.885
|
||||
119528,03-11-2021,377.79
|
||||
118825,03-11-2021,87.553
|
||||
119528,08-11-2021,383.13
|
||||
118825,08-11-2021,88.743
|
||||
119528,09-11-2021,383.06
|
||||
118825,09-11-2021,88.793
|
||||
119528,10-11-2021,382.71
|
||||
118825,10-11-2021,88.723
|
||||
118825,10-11-2021,88.78
|
||||
119528,11-11-2021,379.28
|
||||
118825,11-11-2021,88.205
|
||||
119528,12-11-2021,383.94
|
||||
118825,12-11-2021,89.025
|
||||
119528,15-11-2021,383.31
|
||||
118825,15-11-2021,89.182
|
||||
119528,16-11-2021,381.08
|
||||
118825,16-11-2021,88.569
|
||||
119528,17-11-2021,379.17
|
||||
118825,17-11-2021,88.09
|
||||
119528,18-11-2021,375.09
|
||||
118825,18-11-2021,87.202
|
||||
119528,22-11-2021,368.16
|
||||
118825,22-11-2021,85.382
|
||||
119528,23-11-2021,370.64
|
||||
118825,23-11-2021,85.978
|
||||
119528,24-11-2021,369.91
|
||||
118825,24-11-2021,85.635
|
||||
119528,25-11-2021,371.33
|
||||
118825,25-11-2021,86.212
|
||||
119528,26-11-2021,360.66
|
||||
118825,26-11-2021,83.748
|
||||
119528,29-11-2021,360.05
|
||||
118825,29-11-2021,83.523
|
||||
119528,30-11-2021,359.8
|
||||
118825,30-11-2021,83.475
|
||||
119528,01-12-2021,362.35
|
||||
118825,01-12-2021,84.269
|
||||
119528,02-12-2021,366.09
|
||||
118825,02-12-2021,85.105
|
||||
119528,03-12-2021,363.11
|
||||
118825,03-12-2021,84.507
|
||||
119528,06-12-2021,357.21
|
||||
118825,06-12-2021,83.113
|
||||
119528,07-12-2021,362.63
|
||||
118825,07-12-2021,84.429
|
||||
119528,08-12-2021,368.73
|
||||
118825,08-12-2021,85.935
|
||||
119528,09-12-2021,369.49
|
||||
118825,09-12-2021,86.045
|
||||
119528,10-12-2021,369.44
|
||||
118825,10-12-2021,86.058
|
||||
119528,13-12-2021,367.6
|
||||
118825,13-12-2021,85.632
|
||||
119528,14-12-2021,366.36
|
||||
118825,14-12-2021,85.502
|
||||
119528,15-12-2021,364.34
|
||||
118825,15-12-2021,84.989
|
||||
119528,16-12-2021,363.73
|
||||
118825,16-12-2021,84.972
|
||||
119528,17-12-2021,358.17
|
||||
118825,17-12-2021,83.83
|
||||
119528,20-12-2021,349.98
|
||||
118825,20-12-2021,81.817
|
||||
119528,21-12-2021,353.71
|
||||
118825,21-12-2021,82.746
|
||||
119528,22-12-2021,357.93
|
||||
118825,22-12-2021,83.776
|
||||
119528,23-12-2021,360.68
|
||||
118825,23-12-2021,84.297
|
||||
119528,24-12-2021,359.11
|
||||
118825,24-12-2021,83.903
|
||||
119528,27-12-2021,360.71
|
||||
118825,27-12-2021,84.227
|
||||
119528,28-12-2021,363.81
|
||||
118825,28-12-2021,85.044
|
||||
119528,29-12-2021,363.2
|
||||
118825,29-12-2021,85.03
|
||||
119528,30-12-2021,363.31
|
||||
118825,30-12-2021,85.047
|
||||
119528,31-12-2021,366.98
|
||||
118825,31-12-2021,85.759
|
||||
119528,03-01-2022,371.76
|
||||
118825,03-01-2022,87.111
|
||||
119528,04-01-2022,374.22
|
||||
118825,04-01-2022,87.804
|
||||
119528,05-01-2022,376.31
|
||||
118825,05-01-2022,88.162
|
||||
119528,06-01-2022,373.64
|
||||
118825,06-01-2022,87.541
|
||||
119528,07-01-2022,374.68
|
||||
118825,07-01-2022,87.818
|
||||
119528,10-01-2022,378.47
|
||||
118825,10-01-2022,88.622
|
||||
119528,11-01-2022,379.34
|
||||
118825,11-01-2022,88.678
|
||||
119528,12-01-2022,382.86
|
||||
118825,12-01-2022,89.332
|
||||
119528,13-01-2022,383.68
|
||||
118825,13-01-2022,89.553
|
||||
119528,14-01-2022,384.02
|
||||
118825,14-01-2022,89.729
|
||||
119528,17-01-2022,384.36
|
||||
118825,17-01-2022,89.733
|
||||
119528,18-01-2022,380
|
||||
118825,18-01-2022,88.781
|
||||
119528,19-01-2022,377.24
|
||||
118825,19-01-2022,88.059
|
||||
119528,20-01-2022,374.45
|
||||
118825,20-01-2022,87.361
|
||||
119528,21-01-2022,369.86
|
||||
118825,21-01-2022,86.22
|
||||
119528,24-01-2022,361.01
|
||||
118825,24-01-2022,83.907
|
||||
119528,25-01-2022,364.63
|
||||
118825,25-01-2022,84.763
|
||||
119528,27-01-2022,361.95
|
||||
118825,27-01-2022,83.876
|
||||
119528,28-01-2022,361.91
|
||||
118825,28-01-2022,83.829
|
||||
119528,31-01-2022,367.31
|
||||
118825,31-01-2022,85.18
|
||||
119528,04-02-2022,371.01
|
||||
118825,04-02-2022,86.079
|
||||
119528,07-02-2022,365.04
|
||||
118825,07-02-2022,84.867
|
||||
119528,08-02-2022,365.74
|
||||
118825,08-02-2022,84.945
|
||||
119528,09-02-2022,369.85
|
||||
118825,09-02-2022,85.977
|
||||
119528,10-02-2022,372.29
|
||||
118825,10-02-2022,86.5
|
||||
119528,11-02-2022,366.91
|
||||
118825,11-02-2022,85.226
|
||||
119528,14-02-2022,355.47
|
||||
118825,14-02-2022,82.533
|
|
219
test_files/nav_history_monthly - Copy.csv
Normal file
219
test_files/nav_history_monthly - Copy.csv
Normal file
@ -0,0 +1,219 @@
|
||||
"amfi_code","date","nav"
|
||||
118825,2013-01-31,18.913
|
||||
118825,2013-02-28,17.723
|
||||
118825,2013-03-28,17.563
|
||||
118825,2013-04-30,18.272
|
||||
118825,2013-05-31,18.383
|
||||
118825,2013-06-28,17.802
|
||||
118825,2013-07-31,17.588
|
||||
118825,2013-08-30,16.993
|
||||
118825,2013-09-30,17.732
|
||||
118825,2013-10-31,19.665
|
||||
118825,2013-11-29,19.787
|
||||
118825,2013-12-31,20.499
|
||||
118825,2014-01-31,19.994
|
||||
118825,2014-02-28,20.942
|
||||
118825,2014-03-31,22.339
|
||||
118825,2014-04-30,22.599
|
||||
118825,2014-05-30,24.937
|
||||
118825,2014-06-30,27.011
|
||||
118825,2014-07-31,27.219
|
||||
118825,2014-08-28,28.625
|
||||
118825,2014-09-30,29.493
|
||||
118825,2014-10-31,30.685
|
||||
118825,2014-11-28,31.956
|
||||
118825,2014-12-31,31.646
|
||||
118825,2015-01-30,33.653
|
||||
118825,2015-02-27,33.581
|
||||
118825,2015-03-31,33.14
|
||||
118825,2015-04-30,32.181
|
||||
118825,2015-05-29,33.256
|
||||
118825,2015-06-30,33.227
|
||||
118825,2015-07-31,34.697
|
||||
118825,2015-08-31,32.833
|
||||
118825,2015-09-30,32.94
|
||||
118825,2015-10-30,33.071
|
||||
118825,2015-11-30,33.024
|
||||
118825,2015-12-31,33.267
|
||||
118825,2016-01-29,31.389
|
||||
118825,2016-02-29,28.751
|
||||
118825,2016-03-31,32.034
|
||||
118825,2016-04-29,32.848
|
||||
118825,2016-05-31,34.135
|
||||
118825,2016-06-30,35.006
|
||||
118825,2016-07-29,37.148
|
||||
118825,2016-08-31,38.005
|
||||
118825,2016-09-30,37.724
|
||||
118825,2016-10-28,38.722
|
||||
118825,2016-11-30,36.689
|
||||
118825,2016-12-30,36.239
|
||||
118825,2017-01-31,38.195
|
||||
118825,2017-02-28,39.873
|
||||
118825,2017-03-31,41.421
|
||||
118825,2017-04-28,42.525
|
||||
118825,2017-05-31,43.977
|
||||
118825,2017-06-30,43.979
|
||||
118825,2017-07-31,46.554
|
||||
118825,2017-08-31,46.383
|
||||
118825,2017-09-29,46.085
|
||||
118825,2017-10-31,48.668
|
||||
118825,2017-11-30,48.824
|
||||
118825,2017-12-29,50.579
|
||||
118825,2018-01-31,51.799
|
||||
118825,2018-02-28,49.041
|
||||
118825,2018-03-28,46.858
|
||||
118825,2018-04-30,49.636
|
||||
118825,2018-05-31,49.169
|
||||
118825,2018-06-29,48.716
|
||||
118825,2018-07-31,51.455
|
||||
118825,2018-08-31,53.494
|
||||
118825,2018-09-28,49.863
|
||||
118825,2018-10-31,48.538
|
||||
118825,2018-11-30,50.597
|
||||
118825,2018-12-31,50.691
|
||||
118825,2019-01-31,50.517
|
||||
118825,2019-02-28,50.176
|
||||
118825,2019-03-31,54.017
|
||||
118825,2019-04-30,54.402
|
||||
118825,2019-05-31,55.334
|
||||
118825,2019-06-28,55.181
|
||||
118825,2019-07-31,52.388
|
||||
118825,2019-08-30,52.214
|
||||
118825,2019-09-30,54.058
|
||||
118825,2019-10-31,56.514
|
||||
118825,2019-11-29,57.42
|
||||
118825,2019-12-31,57.771
|
||||
118825,2020-01-31,57.135
|
||||
118825,2020-02-28,54.034
|
||||
118825,2020-03-31,41.452
|
||||
118825,2020-04-30,47.326
|
||||
118825,2020-05-29,45.845
|
||||
118825,2020-06-30,49.526
|
||||
118825,2020-07-31,53.306000000000004
|
||||
118825,2020-08-19,55.747
|
||||
118825,2020-10-30,56.387
|
||||
118825,2020-11-27,62.001000000000005
|
||||
118825,2020-12-31,66.415
|
||||
118825,2021-01-29,65.655
|
||||
118825,2021-02-26,70.317
|
||||
118825,2021-03-31,70.69
|
||||
118825,2021-04-30,70.39
|
||||
118825,2021-05-31,74.85
|
||||
118825,2021-06-30,77.109
|
||||
118825,2021-07-30,78.335
|
||||
118825,2021-08-31,83.691
|
||||
118825,2021-09-30,86.128
|
||||
118825,2021-10-29,86.612
|
||||
118825,2021-11-30,83.475
|
||||
118825,2021-12-31,85.759
|
||||
118825,2022-01-31,85.18
|
||||
118825,2022-02-17,84.33
|
||||
119528,2013-01-31,101.36
|
||||
119528,2013-02-28,95.25
|
||||
119528,2013-03-28,94.81
|
||||
119528,2013-04-30,99.75
|
||||
119528,2013-05-31,99.73
|
||||
119528,2013-06-28,97.52
|
||||
119528,2013-07-31,95.37
|
||||
119528,2013-08-30,92.24
|
||||
119528,2013-09-30,97.45
|
||||
119528,2013-10-31,107.03
|
||||
119528,2013-11-29,105.91
|
||||
119528,2013-12-31,109.3
|
||||
119528,2014-01-31,105.09
|
||||
119528,2014-02-28,108.58
|
||||
119528,2014-03-31,117.28
|
||||
119528,2014-04-30,118.06
|
||||
119528,2014-05-30,131.33
|
||||
119528,2014-06-30,139.48
|
||||
119528,2014-07-31,140.49
|
||||
119528,2014-08-28,145.43
|
||||
119528,2014-09-30,147.4
|
||||
119528,2014-10-31,154.46
|
||||
119528,2014-11-28,161.93
|
||||
119528,2014-12-31,159.62
|
||||
119528,2015-01-30,170.46
|
||||
119528,2015-02-27,171.18
|
||||
119528,2015-03-31,166.8
|
||||
119528,2015-04-30,161.95
|
||||
119528,2015-05-29,166.78
|
||||
119528,2015-06-30,166.67
|
||||
119528,2015-07-31,172.33
|
||||
119528,2015-08-31,161.96
|
||||
119528,2015-09-30,162.25
|
||||
119528,2015-10-30,164.16
|
||||
119528,2015-11-30,162.7
|
||||
119528,2015-12-31,162.83
|
||||
119528,2016-01-29,155.87
|
||||
119528,2016-02-29,144.56
|
||||
119528,2016-03-31,159.88
|
||||
119528,2016-04-29,163.54
|
||||
119528,2016-05-31,170.01
|
||||
119528,2016-06-30,174.61
|
||||
119528,2016-07-29,184.36
|
||||
119528,2016-08-31,189.33
|
||||
119528,2016-09-30,187.16
|
||||
119528,2016-10-28,189.29
|
||||
119528,2016-11-30,178.19
|
||||
119528,2016-12-30,176.66
|
||||
119528,2017-01-31,185.76
|
||||
119528,2017-02-28,193.2
|
||||
119528,2017-03-31,200.54
|
||||
119528,2017-04-28,205.25
|
||||
119528,2017-05-31,208.22
|
||||
119528,2017-06-30,209.83
|
||||
119528,2017-07-31,221.15
|
||||
119528,2017-08-31,219.99
|
||||
119528,2017-09-29,217.7
|
||||
119528,2017-10-31,226.94
|
||||
119528,2017-11-30,225.24
|
||||
119528,2017-12-29,233.26
|
||||
119528,2018-01-31,237.57
|
||||
119528,2018-02-28,226.55
|
||||
119528,2018-03-28,219.73
|
||||
119528,2018-04-30,232.04
|
||||
119528,2018-05-31,228.49
|
||||
119528,2018-06-29,225.27
|
||||
119528,2018-07-31,237.11
|
||||
119528,2018-08-31,243.79
|
||||
119528,2018-09-28,223.83
|
||||
119528,2018-10-31,218.61
|
||||
119528,2018-11-30,226.99
|
||||
119528,2018-12-31,228.61
|
||||
119528,2019-01-31,224.26
|
||||
119528,2019-02-28,222.71
|
||||
119528,2019-03-29,240.21
|
||||
119528,2019-04-30,240.01
|
||||
119528,2019-05-31,243.72
|
||||
119528,2019-06-28,241.28
|
||||
119528,2019-07-31,229.54
|
||||
119528,2019-08-30,226.0
|
||||
119528,2019-09-30,234.75
|
||||
119528,2019-10-31,242.11
|
||||
119528,2019-11-29,246.75
|
||||
119528,2019-12-31,247.81
|
||||
119528,2020-01-31,246.14
|
||||
119528,2020-02-28,231.91
|
||||
119528,2020-03-31,175.98
|
||||
119528,2020-04-30,200.77
|
||||
119528,2020-05-29,196.75
|
||||
119528,2020-06-30,210.55
|
||||
119528,2020-07-31,224.93
|
||||
119528,2020-08-19,233.78
|
||||
119528,2020-10-30,235.83
|
||||
119528,2020-11-27,264.04
|
||||
119528,2020-12-31,285.02
|
||||
119528,2021-01-29,280.52
|
||||
119528,2021-02-26,300.56
|
||||
119528,2021-03-31,301.57
|
||||
119528,2021-04-30,301.1
|
||||
119528,2021-05-31,320.98
|
||||
119528,2021-06-30,327.64
|
||||
119528,2021-07-30,336.6
|
||||
119528,2021-08-31,360.75
|
||||
119528,2021-09-30,369.42
|
||||
119528,2021-10-29,372.89
|
||||
119528,2021-11-30,359.8
|
||||
119528,2021-12-31,366.98
|
||||
119528,2022-01-31,367.31
|
||||
119528,2022-02-17,363.56
|
|
11
test_files/nav_history_monthly.csv
Normal file
11
test_files/nav_history_monthly.csv
Normal file
@ -0,0 +1,11 @@
|
||||
amfi_code,date,nav
|
||||
118825,31-03-2021,70.69
|
||||
118825,30-04-2021,70.39
|
||||
118825,31-05-2021,74.85
|
||||
118825,30-07-2021,78.335
|
||||
118825,31-08-2021,83.691
|
||||
118825,30-09-2021,86.128
|
||||
118825,29-10-2021,86.612
|
||||
118825,30-11-2021,83.475
|
||||
118825,31-01-2022,85.18
|
||||
118825,17-02-2022,84.33
|
|
71
test_files/nav_history_quarterly - Copy.csv
Normal file
71
test_files/nav_history_quarterly - Copy.csv
Normal file
@ -0,0 +1,71 @@
|
||||
"amfi_code","date","nav"
|
||||
118825,2013-03-28,17.563
|
||||
118825,2013-06-28,17.802
|
||||
118825,2013-09-30,17.732
|
||||
118825,2013-12-31,20.499
|
||||
118825,2014-03-31,22.339
|
||||
118825,2014-06-30,27.011
|
||||
118825,2014-09-30,29.493
|
||||
118825,2014-12-31,31.646
|
||||
118825,2015-03-31,33.14
|
||||
118825,2015-06-30,33.227
|
||||
118825,2015-09-30,32.94
|
||||
118825,2015-12-31,33.267
|
||||
118825,2016-03-31,32.034
|
||||
118825,2016-06-30,35.006
|
||||
118825,2016-09-30,37.724
|
||||
118825,2016-12-30,36.239
|
||||
118825,2017-03-31,41.421
|
||||
118825,2017-06-30,43.979
|
||||
118825,2017-09-29,46.085
|
||||
118825,2017-12-29,50.579
|
||||
118825,2018-03-28,46.858
|
||||
118825,2018-06-29,48.716
|
||||
118825,2018-09-28,49.863
|
||||
118825,2018-12-31,50.691
|
||||
118825,2019-03-31,54.017
|
||||
118825,2019-06-28,55.181
|
||||
118825,2019-09-30,54.058
|
||||
118825,2019-12-31,57.771
|
||||
118825,2020-03-31,41.452
|
||||
118825,2020-06-30,49.526
|
||||
118825,2020-12-31,66.415
|
||||
118825,2021-03-31,70.69
|
||||
118825,2021-06-30,77.109
|
||||
118825,2021-09-30,86.128
|
||||
118825,2021-12-31,85.759
|
||||
119528,2013-03-28,94.81
|
||||
119528,2013-06-28,97.52
|
||||
119528,2013-09-30,97.45
|
||||
119528,2013-12-31,109.3
|
||||
119528,2014-03-31,117.28
|
||||
119528,2014-06-30,139.48
|
||||
119528,2014-09-30,147.4
|
||||
119528,2014-12-31,159.62
|
||||
119528,2015-03-31,166.8
|
||||
119528,2015-06-30,166.67
|
||||
119528,2015-09-30,162.25
|
||||
119528,2015-12-31,162.83
|
||||
119528,2016-03-31,159.88
|
||||
119528,2016-06-30,174.61
|
||||
119528,2016-09-30,187.16
|
||||
119528,2016-12-30,176.66
|
||||
119528,2017-03-31,200.54
|
||||
119528,2017-06-30,209.83
|
||||
119528,2017-09-29,217.7
|
||||
119528,2017-12-29,233.26
|
||||
119528,2018-03-28,219.73
|
||||
119528,2018-06-29,225.27
|
||||
119528,2018-09-28,223.83
|
||||
119528,2018-12-31,228.61
|
||||
119528,2019-03-29,240.21
|
||||
119528,2019-06-28,241.28
|
||||
119528,2019-09-30,234.75
|
||||
119528,2019-12-31,247.81
|
||||
119528,2020-03-31,175.98
|
||||
119528,2020-06-30,210.55
|
||||
119528,2020-12-31,285.02
|
||||
119528,2021-03-31,301.57
|
||||
119528,2021-06-30,327.64
|
||||
119528,2021-09-30,369.42
|
||||
119528,2021-12-31,366.98
|
|
9
test_files/nav_history_quarterly.csv
Normal file
9
test_files/nav_history_quarterly.csv
Normal file
@ -0,0 +1,9 @@
|
||||
amfi_code,date,nav
|
||||
118825,31-03-2019,54.017
|
||||
118825,28-06-2019,55.181
|
||||
118825,31-12-2019,57.771
|
||||
118825,31-03-2020,41.452
|
||||
118825,30-06-2020,49.526
|
||||
118825,30-06-2021,77.109
|
||||
118825,30-09-2021,86.128
|
||||
118825,31-12-2021,85.759
|
|
@ -1,21 +1,151 @@
|
||||
import datetime
|
||||
import os
|
||||
import random
|
||||
from typing import Literal, Sequence
|
||||
|
||||
import unittest
|
||||
import pytest
|
||||
from fincal.core import Frequency
|
||||
from fincal.fincal import TimeSeries, create_date_series
|
||||
|
||||
from fincal.fincal import TimeSeries
|
||||
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
sample_data_path = os.path.join(THIS_DIR, "data")
|
||||
|
||||
|
||||
class TestFincal(unittest.TestCase):
|
||||
def test_ts(self):
|
||||
data = [
|
||||
('2020-01-01', 23),
|
||||
('2020-01-02', 24),
|
||||
('2020-01-03', 25),
|
||||
('2020-01-06', 26),
|
||||
('2020-01-07', 27),
|
||||
('2020-01-08', 28),
|
||||
('2020-01-10', 29),
|
||||
('2020-01-11', 30)
|
||||
]
|
||||
time_series = TimeSeries(data)
|
||||
time_series.ffill(inplace=True)
|
||||
self.assertEqual(len(time_series.time_series), 11)
|
||||
def create_test_data(
|
||||
frequency: str,
|
||||
eomonth: bool,
|
||||
n: int,
|
||||
gaps: float,
|
||||
month_position: Literal["start", "middle", "end"],
|
||||
date_as_str: bool,
|
||||
as_outer_type: Literal['dict', 'list'] = 'list',
|
||||
as_inner_type: Literal['dict', 'list', 'tuple'] = 'tuple'
|
||||
) -> Sequence[tuple]:
|
||||
start_dates = {
|
||||
"start": datetime.datetime(2016, 1, 1),
|
||||
"middle": datetime.datetime(2016, 1, 15),
|
||||
"end": datetime.datetime(2016, 1, 31),
|
||||
}
|
||||
end_date = datetime.datetime(2021, 12, 31)
|
||||
dates = create_date_series(start_dates[month_position], end_date, frequency=frequency, eomonth=eomonth)
|
||||
dates = dates[:n]
|
||||
if gaps:
|
||||
num_gaps = int(len(dates) * gaps)
|
||||
to_remove = random.sample(dates, num_gaps)
|
||||
for i in to_remove:
|
||||
dates.remove(i)
|
||||
if date_as_str:
|
||||
dates = [i.strftime('%Y-%m-%d') for i in dates]
|
||||
|
||||
values = [random.randint(8000, 90000)/100 for _ in dates]
|
||||
|
||||
data = list(zip(dates, values))
|
||||
if as_outer_type == 'list':
|
||||
if as_inner_type == 'list':
|
||||
data = [list(i) for i in data]
|
||||
elif as_inner_type == 'dict[1]':
|
||||
data = [dict((i,)) for i in data]
|
||||
elif as_inner_type == 'dict[2]':
|
||||
data = [dict(date=i, value=j) for i, j in data]
|
||||
elif as_outer_type == 'dict':
|
||||
data = dict(data)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class TestDateSeries:
|
||||
def test_daily(self):
|
||||
start_date = datetime.datetime(2020, 1, 1)
|
||||
end_date = datetime.datetime(2020, 12, 31)
|
||||
d = create_date_series(start_date, end_date, frequency="D")
|
||||
assert len(d) == 366
|
||||
|
||||
start_date = datetime.datetime(2017, 1, 1)
|
||||
end_date = datetime.datetime(2017, 12, 31)
|
||||
d = create_date_series(start_date, end_date, frequency="D")
|
||||
assert len(d) == 365
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
create_date_series(start_date, end_date, frequency="D", eomonth=True)
|
||||
|
||||
def test_monthly(self):
|
||||
start_date = datetime.datetime(2020, 1, 1)
|
||||
end_date = datetime.datetime(2020, 12, 31)
|
||||
d = create_date_series(start_date, end_date, frequency="M")
|
||||
assert len(d) == 12
|
||||
|
||||
d = create_date_series(start_date, end_date, frequency="M", eomonth=True)
|
||||
assert datetime.datetime(2020, 2, 29) in d
|
||||
|
||||
start_date = datetime.datetime(2020, 1, 31)
|
||||
d = create_date_series(start_date, end_date, frequency="M")
|
||||
assert datetime.datetime(2020, 2, 29) in d
|
||||
assert datetime.datetime(2020, 8, 31) in d
|
||||
assert datetime.datetime(2020, 10, 30) not in d
|
||||
|
||||
start_date = datetime.datetime(2020, 2, 29)
|
||||
d = create_date_series(start_date, end_date, frequency="M")
|
||||
assert len(d) == 11
|
||||
assert datetime.datetime(2020, 2, 29) in d
|
||||
assert datetime.datetime(2020, 8, 31) not in d
|
||||
assert datetime.datetime(2020, 10, 29) in d
|
||||
|
||||
def test_quarterly(self):
|
||||
start_date = datetime.datetime(2018, 1, 1)
|
||||
end_date = datetime.datetime(2020, 12, 31)
|
||||
d = create_date_series(start_date, end_date, frequency="Q")
|
||||
assert len(d) == 12
|
||||
|
||||
d = create_date_series(start_date, end_date, frequency="Q", eomonth=True)
|
||||
assert datetime.datetime(2020, 4, 30) in d
|
||||
|
||||
start_date = datetime.datetime(2020, 1, 31)
|
||||
d = create_date_series(start_date, end_date, frequency="Q")
|
||||
assert len(d) == 4
|
||||
assert datetime.datetime(2020, 2, 29) not in d
|
||||
assert max(d) == datetime.datetime(2020, 10, 31)
|
||||
|
||||
start_date = datetime.datetime(2020, 2, 29)
|
||||
d = create_date_series(start_date, end_date, frequency="Q")
|
||||
assert datetime.datetime(2020, 2, 29) in d
|
||||
assert datetime.datetime(2020, 8, 31) not in d
|
||||
assert datetime.datetime(2020, 11, 29) in d
|
||||
|
||||
d = create_date_series(start_date, end_date, frequency="Q", eomonth=True)
|
||||
assert datetime.datetime(2020, 11, 30) in d
|
||||
|
||||
|
||||
class TestFincal:
|
||||
def test_creation(self):
|
||||
data = create_test_data(frequency='D', eomonth=False, n=50, gaps=0, month_position='start', date_as_str=True)
|
||||
time_series = TimeSeries(data, frequency="D")
|
||||
assert len(time_series) == 50
|
||||
assert isinstance(time_series.frequency, Frequency)
|
||||
assert time_series.frequency.days == 1
|
||||
|
||||
ffill_data = time_series.ffill()
|
||||
assert len(ffill_data) == 50
|
||||
|
||||
data = create_test_data(frequency='D', eomonth=False, n=500, gaps=0.1, month_position='start', date_as_str=True)
|
||||
time_series = TimeSeries(data, frequency="D")
|
||||
assert len(time_series) == 450
|
||||
|
||||
def test_ffill(self):
|
||||
data = create_test_data(frequency='D', eomonth=False, n=500, gaps=0.1, month_position='start', date_as_str=True)
|
||||
time_series = TimeSeries(data, frequency="D")
|
||||
ffill_data = time_series.ffill()
|
||||
assert len(ffill_data) > 498
|
||||
|
||||
ffill_data = time_series.ffill(inplace=True)
|
||||
assert ffill_data is None
|
||||
assert len(time_series) > 498
|
||||
|
||||
def test_slicing(self):
|
||||
data = create_test_data(frequency='D', eomonth=False, n=50, gaps=0, month_position='start', date_as_str=True)
|
||||
time_series = TimeSeries(data, frequency="D")
|
||||
assert time_series[0] is not None
|
||||
assert time_series[:3] is not None
|
||||
assert time_series[5:7] is not None
|
||||
assert isinstance(time_series[0], tuple)
|
||||
assert isinstance(time_series[10:20], list)
|
||||
assert len(time_series[10:20]) == 10
|
||||
|
Loading…
Reference in New Issue
Block a user