def infer_period(self): logger.debug('Attempting to infer period/frequency of cashflows.') # no_of_dates = len - 1 because delta is being computed, i.e. # lose one date. dates, no_of_dates = self.sample.keys(), (len(self.sample.keys()) - 1) first_pass = True mean_delta = 0 if no_of_dates < 2: logger.debug( 'Cannot infer period from sample size less than or equal to 1') self.period = None self.frequency = None else: for date in dates: if first_pass: tomorrows_date = dater.parse(date) first_pass = False else: todays_date = dater.parse(date) delta = (tomorrows_date - todays_date).days / 365 mean_delta += delta / no_of_dates tomorrows_date = todays_date self.period = mean_delta self.frequency = 1 / self.period logger.debug(f'Inferred period = {self.period} yrs') logger.debug(f'Inferred frequency = {self.frequency}')
def test_date_validation(ticker, asset_type, start_date_str, end_date_str, expected_start_str, expected_end_str): start_date = dater.parse(start_date_str) end_date = dater.parse(end_date_str) validated_start, validated_end = errors.validate_dates( start_date, end_date, asset_type) assert(dater.to_string(validated_start) == expected_start_str and \ dater.to_string(validated_end) == expected_end_str)
def test_standardized_sample_of_returns(ticker, start_date, end_date): with HTTMock(mock.mock_prices): these_returns = standardize(statistics.get_sample_of_returns(ticker=ticker, start_date=dater.parse(start_date), end_date=dater.parse(end_date))) if get_asset_type(ticker) == keys['ASSETS']['CRYPTO']: no_of_days = dater.days_between(start_date, end_date) else: no_of_days = dater.business_days_between(start_date, end_date) assert isinstance(these_returns, list) assert all(isinstance(this_return, float) for this_return in these_returns) # subtract one because differencing price history loses one sample assert len(these_returns) == no_of_days - 1
def plot_correlation_series(tickers: list, series: dict, show: bool = True, savefile: str = None) -> Union[FigureCanvas, None]: start, end = list(series.keys())[-1], list(series.keys())[0] title = f'({tickers[0]}, {tickers[1]}) correlation time series' subtitle = f'{start} to {end}, rolling {settings.DEFAULT_ANALYSIS_PERIOD}-day estimate' canvas = FigureCanvas(Figure()) figure = canvas.figure axes = figure.subplots() locator = mdates.AutoDateLocator() formatter = mdates.AutoDateFormatter(locator) correl_history, dates = [], [] for date in series: dates.append(dater.parse(date)) correl_history.append(series[date]) axes.plot(dates, correl_history) axes.grid() axes.xaxis.set_major_locator(locator) axes.xaxis.set_major_formatter(formatter) axes.set_ylabel('Correlation') axes.set_xlabel('Dates') axes.set_title(subtitle, fontsize=12) figure.suptitle(title, fontsize=18) figure.autofmt_xdate() return _show_or_save(canvas=canvas, show=show, savefile=savefile)
def generate_time_series_for_sample(self): self.time_series = [] dates, no_of_dates = self.sample.keys(), len(self.sample.keys()) if no_of_dates == 0: logger.debug( 'Cannot generate a time series for a sample size of 0.') self.time_series = None else: first_date = dater.parse(list(dates)[-1]) for date in dates: this_date = dater.parse(date) delta = (this_date - first_date).days time_in_years = delta / 365 self.time_series.append(time_in_years)
def calculate_net_present_value(self): """ Returns the net present value of the cash flow by using the `get_growth_function` method to project future cash flows and then discounting those projections back to the present by the value of the `discount_rate`. Call this method after constructing/initializing a `Cashflow` object to retrieve its NPV. Raises ------ 1. **scrilla.errors.InputValidationError** If not enough information is present in the instance of the `Cashflow` object to project future cash flows, this error will be thrown. Returns ------- ``float`` : NPV of cash flow. """ if self.period is None: raise errors.InputValidationError( "No period detected for cashflows. Not enough information to calculate net present value.") time_to_first_payment = 0 if self.period is None: raise errors.InputValidationError( 'Not enough information to calculate net present value of cash flow.') if self.period == FREQ_ANNUAL: time_to_first_payment = dater.get_time_to_next_year() elif self.period == FREQ_QUARTER: time_to_first_payment = dater.get_time_to_next_quarter() elif self.period == FREQ_MONTH: time_to_first_payment = dater.get_time_to_next_month() elif self.period == FREQ_DAY: time_to_first_payment = FREQ_DAY else: dates = self.sample.keys() latest_date = dater.parse(list(dates)[0]) time_to_first_payment = dater.get_time_to_next_period( starting_date=latest_date, period=self.period) net_present_value, i, current_time = 0, 0, 0 calculating = True while calculating: previous_value = net_present_value current_time = time_to_first_payment + i * self.period net_present_value += self.get_growth_function(current_time) / ( (1 + self.discount_rate)**current_time) if net_present_value - previous_value < constants.constants['NPV_DELTA_TOLERANCE']: calculating = False i += 1 return net_present_value
def calculate_time_to_today(self): first_date = dater.parse(list(self.sample.keys())[-1]) today = datetime.date.today() return ((today - first_date).days/365)
def test_dates_between(start_date, end_date, length): date_range = dater.dates_between(start_date, end_date) assert(len(date_range) == length) assert(dater.parse(start_date) in date_range) assert(dater.parse(end_date) in date_range)
def test_decrement_date_by_business_days(start_date, days, result): test_date = dater.decrement_date_by_business_days(start_date, days) assert(test_date == dater.parse(result))
import os from scrilla.util import dater from scrilla.static import constants TEST_DIR = os.path.dirname(os.path.abspath(__file__)) MOCK_DIR = os.path.join(TEST_DIR, 'data') END_STR = "2021-11-19" END = dater.parse(END_STR) START_STR = "2021-07-14" START = dater.parse(START_STR) def is_within_tolerance(func): return (abs(func()) < 10**(-constants.constants['ACCURACY']))