def _act_360_fraction(self, date): '''Implement daycount method Act/360 ''' date = BankDate(date) if date and date > self._valuation_date: return self._valuation_date.nbr_of_days(date) / to_decimal(360) else: return to_decimal(0)
def _360_360_fraction(self, date): '''Implement daycount method 360/360 ''' day1 = BankDate(date) day2 = self._valuation_date if day1 and day1 > day2: number_of_days = 360 * (day1.year - day2.year) \ + 30 * (day1.month - day2.month) \ + (day1.day - day2.day) number_of_days = number_of_days % 360 return to_decimal(number_of_days) / to_decimal(360) else: return to_decimal(0)
def modified_convexity(self, spread): ''' :param spread: A rate based generel spread. Default is 0.0 ie no spread :type spread: A float between 0 and 1 :return: modified_convexity for the DateFlow and time valuation parameters given at instantiation *Formula for macauley_convexity:* .. math:: macauley \\textunderscore convexity &= \\frac {1} {NPV \\left( spread \\right)} \\cdot \\frac {\\partial ^{2} NPV \\left( spread \\right)} {\\partial spread ^{2}}\\\\ &= \\frac {1}{NPV \\left( spread \\right)}\\cdot npv\\_d2 .. note:: Actually when no discountcurve is used the spread can be used to implement the discountcurve with constant discountfactor for each future period of fixed length. ''' if self.__nonzero__(): spread = to_decimal(spread) npv = self.npv_value(spread) if spread and npv: return self.npv_d2(spread) / npv
def _act_act_isda_fraction(self, date): '''Implement daycount method Act/Act ISDA ''' date = BankDate(date) if date and date > self._valuation_date: date_year = date.year days_in_year = to_decimal(366 if isleap(date.year) else 365) days_in_valuation_year = to_decimal( 366 if isleap(self._valuation_date.year) else 365) if self._valuation_date.year == date.year: return self._valuation_date.nbr_of_days(date) \ / days_in_valuation_year else: mid_day = BankDate('%s-01-01' % date_year) return self._valuation_date.nbr_of_days(mid_day) \ / days_in_valuation_year \ + mid_day.nbr_of_days(date) / days_in_year else: return to_decimal(0)
def npv_spread(self, npv_0, precision=1e-16, max_iteration=30): ''' :param npv_0: The present value of the cashflow :type npv_0: Float or decimal :param max_iteration: The precision/tolerance for the result. Default is 1e-16 :type max_iteration: Float or decimal :param max_iter: The maximal number of iterations. Default is 30 :type max_iter: A positive integer :return: The par rate of the discounted cashflow ''' npv_0 = to_decimal(npv_0) if npv_0: # TODO: Valuate for a solution spread_func = Solver(self.npv_value, self.npv_d1, precision, max_iteration) return spread_func(npv_0)
def npv_d2(self, spread): ''' :param spread: A rate based generel spread. :type spread: A float between 0 and 1 :return: Second order derivative for the DateFlow and time valuation parameters given at instantiation *Formula for npv_d2:* .. math:: npv \\textunderscore d2 = \\frac{\\partial ^{2} NPV \\left( spread \\right)}{\\partial spread ^{2}} ''' spread = to_decimal(spread) discounts = -self.future_times() * (-self.future_times() - 1) \ * (1 + spread) ** (-self.future_times() - 2) # discounted_values is called as a function to get vector # multiplication return self.discounted_values()(discounts)
def npv_value(self, spread): ''' :param spread: A rate based generel spread. :type spread: A float between 0 and 1 :param used_discountcurve: Used discountcurve. Default is None ie. no discounting :type used_discountcurve: A discountcurve or None :return: npv_value for the DateFlow and time valuation parameters given at instantiation *Short hand notation for npv_value in the documentation:* .. math:: NPV ( spread ) = npv \\textunderscore value ( spread ) .. note:: Actually when no discountcurve is used the spread can be used to implement the discountcurve with constant discountfactor for each future period of fixed length. ''' # discounted_values is called as a function to get vector # multiplication spread = to_decimal(spread) return self.discounted_values()((1 + spread)**-self.future_times())
class ClassicRiskMeasures: __metaclass__ = ABCMeta basispoint = to_decimal('0.0001') @abstractmethod def npv_value(self, spread): '''npv_value has to be defined before using ClassicRiskMeasures ''' raise NotImplementedError @abstractmethod def npv_d1(self, spread): '''npv_d1 has to be defined before using ClassicRiskMeasures ''' raise NotImplementedError @abstractmethod def npv_d2(self, spread): '''npv_d2 has to be defined before using ClassicRiskMeasures ''' raise NotImplementedError @vector_function(1, True) def modified_duration(self, spread): ''' :param spread: A rate based generel spread. :type spread: A float between 0 and 1 :return: modified_duration for the DateFlow and time valuation parameters given at instantiation *Formula for modified_duration:* .. math:: modified \\textunderscore duration &= \\frac {1} {NPV \\left( spread \\right)} \\cdot \\frac {\\partial NPV \\left( spread \\right)}{\\partial spread}\\\\ &= \\frac{1}{NPV \\left( spread \\right)} \\cdot npv \\textunderscore d1 .. note:: Actually when no discountcurve is used the spread can be used to implement the discountcurve with constant discountfactor for each future period of fixed length. ''' if self.__nonzero__(): npv = self.npv_value(spread) if spread and npv: return -self.npv_d1(spread) / npv @vector_function(1, True) def macauley_duration(self, spread): ''' :param spread: A rate based generel spread. Default is 0.0 ie no spread :type spread: A float between 0 and 1 :return: macauley_duration for the DateFlow and time valuation parameters given at instantiation *Formula for macauley_duration:* .. math:: macauley \\textunderscore duration &= \\frac{1} {NPV \\left( spread \\right) \\cdot \\left( 1 + spread \\right)} \\cdot \\frac {\\partial NPV \\left( spread \\right)} {\\partial spread}\\\\ &= \\frac {1}{NPV \\left( spread \\right) \\cdot \\left( 1 + spread \\right)} \\cdot npv \\textunderscore d1 .. note:: Actually when no discountcurve is used the spread can be used to implement the discountcurve with constant discountfactor for each future period of fixed length. ''' if self.__nonzero__(): npv = self.npv_value(spread) if spread and npv: return -self.npv_d1(spread) / npv * (1 + spread) @vector_function(1, True) def modified_convexity(self, spread): ''' :param spread: A rate based generel spread. Default is 0.0 ie no spread :type spread: A float between 0 and 1 :return: modified_convexity for the DateFlow and time valuation parameters given at instantiation *Formula for macauley_convexity:* .. math:: macauley \\textunderscore convexity &= \\frac {1} {NPV \\left( spread \\right)} \\cdot \\frac {\\partial ^{2} NPV \\left( spread \\right)} {\\partial spread ^{2}}\\\\ &= \\frac {1}{NPV \\left( spread \\right)}\\cdot npv\\_d2 .. note:: Actually when no discountcurve is used the spread can be used to implement the discountcurve with constant discountfactor for each future period of fixed length. ''' if self.__nonzero__(): spread = to_decimal(spread) npv = self.npv_value(spread) if spread and npv: return self.npv_d2(spread) / npv @vector_function(1, True) def macauley_convexity(self, spread): ''' :param spread: A rate based generel spread. :type spread: A float between 0 and 1 :return: macauley_convexity for the DateFlow and time valuation parameters given at instantiation *Formula for macauley_convexity:* .. math:: macauley \\textunderscore convexity &= \\frac {1} {NPV \\left( spread \\right) \\cdot \\left( 1 + spread \\right)^{2}} \\cdot \\frac {\\partial ^{2} NPV \\left( spread \\right)} {\\partial spread ^{2}}\\\\ &= \\frac {1}{NPV \\left( spread \\right) \\cdot \\left( 1 + spread \\right) ^{2}} \\cdot npv \\textunderscore d2 .. note:: Actually when no discountcurve is used the spread can be used to implement the discountcurve with constant discountfactor for each future period of fixed length. ''' if self.__nonzero__(): npv = self.npv_value(spread) if spread and npv: return self.npv_d2(spread) / npv * (1 + spread)**2 @vector_function(1, True) def pv01(self, spread): ''' :param spread: A rate based generel spread. :type spread: A float between 0 and 1 :return: pv01 for the DateFlow and time valuation parameters given at instantiation *Formula for pv01:* .. math:: pv01 &= \\frac {\\partial NPV \\left( spread \\right)} {\\partial spread}\\cdot0.0001\\\\ &= npv \\textunderscore d1 \\left( spread \\right) \\cdot 0.0001 This definition has the advantage that it can handle DateFlows with NPV near zero. .. note:: Actually when no discountcurve is used the spread can be used to implement the discountcurve with constant discountfactor for each future period of fixed length. ''' if self.__nonzero__(): if spread: return -ClassicRiskMeasures.basispoint * self.npv_d1(spread) @vector_function(1, True) def pvbp(self, spread): ''' :param spread: A rate based generel spread. :type spread: A float between 0 and 1 :return: pvbp for the DateFlow and time valuation parameters given at instantiation *Formula for pvbp:* .. math:: pvbp = NPV \\left( spread \\right) - NPV \\left( spread + 0.0001 \\right) This definition has the advantage that it can handle DateFlows with NPV near zero. .. note:: Actually when no discountcurve is used the spread can be used to implement the discountcurve with constant discountfactor for each future period of fixed length. ''' if self.__nonzero__(): if spread: return self.npv_value(spread) \ - self.npv_value(spread + ClassicRiskMeasures.basispoint)