def test_add(self): s = BusinessDate('20011110') e = BusinessDate('20011112') self.assertEqual(BusinessDate.add_days(s, e.day), BusinessDate('20011122')) self.assertEqual(BusinessDate.add_months(s, 1), BusinessDate('20011210')) self.assertEqual(BusinessDate.add_years(s, 1), BusinessDate('20021110'))
class BusinessDateUnitTests(unittest.TestCase): def setUp(self): self.dec31 = BusinessDate(20151231) self.jan01 = BusinessDate(20160101) self.jan02 = BusinessDate(20160102) self.jan04 = BusinessDate(20160104) self.jan29 = BusinessDate(20160129) self.jan31 = BusinessDate(20160131) self.feb01 = BusinessDate(20160201) self.feb28 = BusinessDate(20160228) self.feb29 = BusinessDate(20160229) self.mar31 = BusinessDate(20160331) self.jun30 = BusinessDate(20160630) self.sep30 = BusinessDate(20160930) self.dates = [self.dec31, self.jan01, self.jan02, self.jan04, self.jan29, self.jan31, self.feb01, self.feb28, self.feb29, self.mar31, self.jun30, self.sep30] def test_constructors(self): self.assertEqual(BusinessDate(date.today()), BusinessDate()) self.assertEqual(self.jan02, BusinessDate('2016-01-02')) self.assertEqual(self.jan02, BusinessDate('01/02/2016')) self.assertEqual(self.jan02, BusinessDate('02.01.2016')) self.assertEqual(self.jan02, BusinessDate(42371)) self.assertEqual(self.jan02, BusinessDate(42371.0)) self.assertEqual(self.jan02, BusinessDate(735965)) self.assertEqual(self.jan02, BusinessDate(735965.0)) self.assertEqual([self.jan01, self.jan02], BusinessDate([20160101, 42371])) def test_to_string(self): self.assertEqual(self.jan02.to_string(), '20160102') self.assertEqual(BusinessDate(42371).to_string(), '20160102') def test_properties(self): self.assertEqual(self.jan01.day, 01) self.assertEqual(self.jan01.month, 01) self.assertEqual(self.jan01.year, 2016) def test_operators(self): self.assertEqual(self.jan01 + '2D', self.jan02 + '1D') self.assertEqual(self.jan01 - '1D', self.jan02 - '2D') self.assertEqual(self.jan02 - '1D' + '1M', self.feb01) self.assertRaises(TypeError, lambda: '1D' + self.jan02) self.assertEqual(self.jan01 - BusinessPeriod('1D'), self.jan02 - '2D') self.assertRaises(TypeError, lambda: BusinessPeriod('1D') + self.jan02) self.assertRaises(TypeError, lambda: BusinessPeriod('1D') - self.jan01) self.assertEqual(self.dec31.add_period(BusinessPeriod('2B', BusinessHolidays())), self.dec31.add_period(BusinessPeriod('2B'), BusinessHolidays([self.jan02]))) def test_validations(self): self.assertTrue(BusinessDate.is_businessdate(18991229)) self.assertTrue(BusinessDate.is_businessdate(20160131)) self.assertTrue(BusinessDate.is_businessdate(20160228)) self.assertTrue(BusinessDate.is_businessdate(20160229)) self.assertFalse(BusinessDate.is_businessdate(20160230)) self.assertFalse(BusinessDate.is_businessdate(20160231)) self.assertTrue(BusinessDate.is_businessdate(20150228)) self.assertFalse(BusinessDate.is_businessdate(20150229)) self.assertFalse(BusinessDate.is_businessdate('xyz')) self.assertFalse(BusinessDate.is_businessdate(-125)) self.assertFalse(BusinessDate.is_businessdate(-20150228)) def test_calculations(self): self.assertEqual(self.jan01.add_days(1), self.jan02) self.assertEqual(self.jan01.add_months(1), self.feb01) self.assertEqual(self.jan01.add_years(1).to_string(), '20170101') self.assertEqual(self.jan01.add_period('2D'), self.jan02 + BusinessPeriod('1D')) self.assertEqual(self.jan02.add_period('-2D'), self.jan01 - BusinessPeriod('1D')) self.assertEqual(self.jan02.add_period('-1b'), self.jan01 - BusinessPeriod('1b')) self.assertNotEqual(BusinessDate(20160630).add_period(BusinessPeriod('2B')), BusinessDate(20160630).add_period(BusinessPeriod('2B', BusinessHolidays(['20160704'])))) self.assertEqual(self.jan01 + '1b', self.jan02 + '1b') def test_cast_to(self): self.assertTrue(isinstance(self.jan01.to_date(), date)) self.assertTrue(isinstance(self.jan01.to_businessdate(), BusinessDate)) self.assertTrue(isinstance(self.jan01.to_businessperiod(), BusinessPeriod)) self.assertTrue(isinstance(self.jan01.to_excel(), int)) self.assertTrue(isinstance(self.jan01.to_ordinal(), int)) self.assertTrue(isinstance(self.jan01.to_string(), str)) self.assertTrue(isinstance(self.jan01.to_ymd(), tuple)) def test_cast_from(self): for d in self.dates: self.assertEqual(BusinessDate.from_date(d.to_date()), d) self.assertEqual(BusinessDate.from_businessdate(d), d) self.assertEqual(BusinessDate.from_excel(d.to_excel()), d) self.assertEqual(BusinessDate.from_ordinal(d.to_ordinal()), d) self.assertEqual(BusinessDate.from_string(d.to_string()), d) self.assertEqual(BusinessDate.from_ymd(*d.to_ymd()), d) def test_day_count(self): # The daycount methods are also tested separately delta = float((self.mar31.to_date() - self.jan01.to_date()).days) total = float(((self.jan01 + '1y').to_date() - self.jan01.to_date()).days) self.assertAlmostEqual(self.jan01.get_30_360(self.mar31), 90. / 360.) self.assertAlmostEqual(self.jan01.get_act_360(self.mar31), delta / 360.) self.assertAlmostEqual(self.jan01.get_act_365(self.mar31), delta / 365.) self.assertAlmostEqual(self.jan01.get_act_36525(self.mar31), delta / 365.25) self.assertAlmostEqual(self.jan01.get_act_act(self.mar31), delta / total) def test_business_day_adjustment(self): self.assertEqual(self.jan01.adjust_follow(), BusinessDate(20160104)) self.assertEqual(self.jan01.adjust_mod_follow(), BusinessDate(20160104)) self.assertEqual(self.jan01.adjust_previous(), BusinessDate(20151231)) self.assertEqual(self.jan01.adjust_mod_previous(), BusinessDate(20160104)) self.assertEqual(self.jan01.adjust_start_of_month(), BusinessDate(20160104)) self.assertEqual(self.jan01.adjust_end_of_month(), BusinessDate(20160129)) self.assertEqual(self.jan01.adjust_imm(), BusinessDate(20160115)) self.assertEqual(self.jan01.adjust_cds_imm(), BusinessDate(20160120)) def test_business_day_is(self): self.assertFalse(self.jan01.is_business_day()) self.assertTrue(BusinessDate.is_leap_year(2016)) self.assertTrue(BusinessDate.is_businessdate('20160101')) self.assertFalse(BusinessDate.is_businessdate('ABC')) self.assertFalse(BusinessDate.is_businessdate('20160230')) self.assertTrue(BusinessDate.is_businessdate('20160229')) self.assertFalse(BusinessDate.is_businessdate('20150229')) def test_is_business_date(self): d = self.dec31 holi = BusinessHolidays() bdate = BusinessDate.from_ymd(2016, 1, 1) is_bday_empty_calendar = bdate.is_business_day(holi) self.assertTrue(is_bday_empty_calendar) is_bday_default_calendar = bdate.is_business_day() self.assertFalse(is_bday_default_calendar) target_a= BusinessDate.from_ymd(2016, 1, 4) a = d.add_business_days(2, holi) self.assertEqual(target_a, a) target_b = BusinessDate.from_ymd(2016, 1, 5) b = d.add_business_days(2) # default holidays contains the target days, i.e. the 1.1.2016 self.assertEqual(target_b, b)
class BusinessDateUnitTests(unittest.TestCase): def setUp(self): self.dec31 = BusinessDate(20151231) self.jan01 = BusinessDate(20160101) self.jan02 = BusinessDate(20160102) self.jan04 = BusinessDate(20160104) self.jan29 = BusinessDate(20160129) self.jan31 = BusinessDate(20160131) self.feb01 = BusinessDate(20160201) self.feb28 = BusinessDate(20160228) self.feb29 = BusinessDate(20160229) self.mar31 = BusinessDate(20160331) self.jun30 = BusinessDate(20160630) self.sep30 = BusinessDate(20160930) self.dates = [ self.dec31, self.jan01, self.jan02, self.jan04, self.jan29, self.jan31, self.feb01, self.feb28, self.feb29, self.mar31, self.jun30, self.sep30 ] def test_constructors(self): self.assertEqual(BusinessDate(date.today()), BusinessDate()) self.assertEqual(self.jan02, BusinessDate('2016-01-02')) self.assertEqual(self.jan02, BusinessDate('01/02/2016')) self.assertEqual(self.jan02, BusinessDate('02.01.2016')) self.assertEqual(self.jan02, BusinessDate(42371)) self.assertEqual(self.jan02, BusinessDate(42371.0)) self.assertEqual(self.jan02, BusinessDate(735965)) self.assertEqual(self.jan02, BusinessDate(735965.0)) self.assertEqual([self.jan01, self.jan02], BusinessDate([20160101, 42371])) def test_to_string(self): self.assertEqual(self.jan02.to_string(), '20160102') self.assertEqual(BusinessDate(42371).to_string(), '20160102') def test_properties(self): self.assertEqual(self.jan01.day, 1) self.assertEqual(self.jan01.month, 1) self.assertEqual(self.jan01.year, 2016) def test_operators(self): self.assertEqual(self.jan01 + '2D', self.jan02 + '1D') self.assertEqual(self.jan01 - '1D', self.jan02 - '2D') self.assertEqual(self.jan02 - '1D' + '1M', self.feb01) self.assertRaises(TypeError, lambda: '1D' + self.jan02) self.assertEqual(self.jan01 - BusinessPeriod('1D'), self.jan02 - '2D') self.assertRaises(TypeError, lambda: BusinessPeriod('1D') + self.jan02) self.assertRaises(TypeError, lambda: BusinessPeriod('1D') - self.jan01) self.assertEqual( self.dec31.add_period(BusinessPeriod('2B', BusinessHolidays())), self.dec31.add_period(BusinessPeriod('2B'), BusinessHolidays([self.jan02]))) def test_validations(self): self.assertTrue(BusinessDate.is_businessdate(18991229)) self.assertTrue(BusinessDate.is_businessdate(20160131)) self.assertTrue(BusinessDate.is_businessdate(20160228)) self.assertTrue(BusinessDate.is_businessdate(20160229)) self.assertFalse(BusinessDate.is_businessdate(20160230)) self.assertFalse(BusinessDate.is_businessdate(20160231)) self.assertTrue(BusinessDate.is_businessdate(20150228)) self.assertFalse(BusinessDate.is_businessdate(20150229)) self.assertFalse(BusinessDate.is_businessdate('xyz')) self.assertFalse(BusinessDate.is_businessdate(-125)) self.assertFalse(BusinessDate.is_businessdate(-20150228)) def test_calculations(self): self.assertEqual(self.jan01.add_days(1), self.jan02) self.assertEqual(self.jan01.add_months(1), self.feb01) self.assertEqual(self.jan01.add_years(1).to_string(), '20170101') self.assertEqual(self.jan01.add_period('2D'), self.jan02 + BusinessPeriod('1D')) self.assertEqual(self.jan02.add_period('-2D'), self.jan01 - BusinessPeriod('1D')) self.assertEqual(self.jan02.add_period('-1b'), self.jan01 - BusinessPeriod('1b')) self.assertNotEqual( BusinessDate(20160630).add_period(BusinessPeriod('2B')), BusinessDate(20160630).add_period( BusinessPeriod('2B', BusinessHolidays(['20160704'])))) self.assertEqual(self.jan01 + '1b', self.jan02 + '1b') rng = list(range(1, 10)) periods = list() for y in rng: for m in rng: for d in rng: periods.append( BusinessPeriod( str(y) + 'y' + str(m) + 'm' + str(d) + 'd')) for d in self.dates: for p in periods: c = d + p self.assertEqual((c - d), BusinessPeriod(p), (c, d, c - d, p)) d = BusinessDate(20160229) self.assertEqual(d + '1y' + '1m', BusinessDate(20170328)) self.assertEqual(d + '1m' + '1y', BusinessDate(20170329)) self.assertEqual(d + '1y1m', BusinessDate(20170329)) def test_cast_to(self): self.assertTrue(isinstance(self.jan01.to_date(), date)) self.assertTrue(isinstance(self.jan01.to_businessdate(), BusinessDate)) self.assertTrue( isinstance(self.jan01.to_businessperiod(), BusinessPeriod)) self.assertTrue(isinstance(self.jan01.to_excel(), int)) self.assertTrue(isinstance(self.jan01.to_ordinal(), int)) self.assertTrue(isinstance(self.jan01.to_string(), str)) self.assertTrue(isinstance(self.jan01.to_ymd(), tuple)) def test_cast_from(self): for d in self.dates: self.assertEqual(BusinessDate.from_date(d.to_date()), d) self.assertEqual(BusinessDate.from_businessdate(d), d) self.assertEqual(BusinessDate.from_excel(d.to_excel()), d) self.assertEqual(BusinessDate.from_ordinal(d.to_ordinal()), d) self.assertEqual(BusinessDate.from_string(d.to_string()), d) self.assertEqual(BusinessDate.from_ymd(*d.to_ymd()), d) def test_day_count( self): # The daycount methods are also tested separately delta = float((self.mar31.to_date() - self.jan01.to_date()).days) total = float( ((self.jan01 + '1y').to_date() - self.jan01.to_date()).days) self.assertAlmostEqual(self.jan01.get_30_360(self.mar31), 90. / 360.) self.assertAlmostEqual(self.jan01.get_act_360(self.mar31), delta / 360.) self.assertAlmostEqual(self.jan01.get_act_365(self.mar31), delta / 365.) self.assertAlmostEqual(self.jan01.get_act_36525(self.mar31), delta / 365.25) self.assertAlmostEqual(self.jan01.get_act_act(self.mar31), delta / total) def test_business_day_adjustment(self): self.assertEqual(self.jan01.adjust_follow(), BusinessDate(20160104)) self.assertEqual(self.jan01.adjust_mod_follow(), BusinessDate(20160104)) self.assertEqual(self.jan01.adjust_previous(), BusinessDate(20151231)) self.assertEqual(self.jan01.adjust_mod_previous(), BusinessDate(20160104)) self.assertEqual(self.jan01.adjust_start_of_month(), BusinessDate(20160104)) self.assertEqual(self.jan01.adjust_end_of_month(), BusinessDate(20160129)) self.assertEqual(self.jan01.adjust_imm(), BusinessDate(20160115)) self.assertEqual(self.jan01.adjust_cds_imm(), BusinessDate(20160120)) def test_business_day_is(self): self.assertFalse(self.jan01.is_business_day()) self.assertTrue(BusinessDate.is_leap_year(2016)) self.assertTrue(BusinessDate.is_businessdate('20160101')) self.assertFalse(BusinessDate.is_businessdate('ABC')) self.assertFalse(BusinessDate.is_businessdate('20160230')) self.assertTrue(BusinessDate.is_businessdate('20160229')) self.assertFalse(BusinessDate.is_businessdate('20150229')) def test_is_business_date(self): d = self.dec31 holi = BusinessHolidays() bdate = BusinessDate.from_ymd(2016, 1, 1) is_bday_empty_calendar = bdate.is_business_day(holi) self.assertTrue(is_bday_empty_calendar) is_bday_default_calendar = bdate.is_business_day() self.assertFalse(is_bday_default_calendar) target_a = BusinessDate.from_ymd(2016, 1, 4) a = d.add_business_days(2, holi) self.assertEqual(target_a, a) target_b = BusinessDate.from_ymd(2016, 1, 5) b = d.add_business_days( 2) # default holidays contains the target days, i.e. the 1.1.2016 self.assertEqual(target_b, b)
class CashFlow(PayOffInterface, Product): """ representation of a single cash flow with all the necessary dates and the index for calculation of the rate """ @property def rate_index(self): """ float rate index of cash flow """ return self._rate_index_ @property def start_date(self): """ start date of accrual period """ return self._start_date_ @property def end_date(self): """ end date of accrual period """ return self._end_date_ @property def reset_date(self): """ reset date , i.e. fixing date, of cash flow index """ return self._reset_date_ @property def pay_date(self): """ cash flow pay date """ return self._pay_date_ @property def year_fraction(self): """ year fraction of accrual period """ return self._year_fraction_ @property def is_pay(self): """ True if leg pays, False if leg receives """ return self._notional_ < 0. @property def is_rec(self): """ False if leg pays, True if leg receives """ return not self.is_pay @property def is_fix(self): """ True if CashFlow has non zero ConstantRate """ return True if self._constant_rate_ else False @property def is_float(self): """ True if CashFlow has zero ConstantRate """ return not self.is_fix def __init__(self, object_name_str=''): super(CashFlow, self).__init__(object_name_str) # index property self._rate_index_ = CashRateIndex() # rate calc properties self._start_date_ = BusinessDate() self._end_date_ = BusinessDate.add_period(self._start_date_, self._rate_index_.tenor) self._accrued_day_count_ = Act360() self._year_fraction_ = self._accrued_day_count_.get_year_fraction( self._start_date_, self._end_date_) self._reset_date_ = self._start_date_ - self._rate_index_._spot_ self._pay_date_ = BusinessDate.add_period(self._end_date_, self._rate_index_._spot_) # payoff properties self._spread_ = 0.00 self._constant_rate_ = 0.0 self._amount_ = 0.00 self._multiplier_ = 1.00 self._cap_ = NO_CAP_VALUE self._floor_ = NO_FLOOR_VALUE self._rebuild_object() def _rebuild_object(self): # check which dates etc are not modified members -> rebuild them if '_reset_date_' not in self._modified_members: self._reset_date_ = self._start_date_ - self._rate_index_._spot_ if '_end_date_' not in self._modified_members: if '_year_fraction_' in self._modified_members: yf = self._year_fraction_ e = self._start_date_.add_days(int(yf * 365.25)) my_yf = self._accrued_day_count_.get_year_fraction( self._start_date_, e) while yf < my_yf: e -= '1d' my_yf = self._accrued_day_count_.get_year_fraction( self._start_date_, e) while yf > my_yf: e += '1d' my_yf = self._accrued_day_count_.get_year_fraction( self._start_date_, e) self._end_date_ = e else: self._end_date_ = BusinessDate.add_period( self._start_date_, self._rate_index_.tenor) self._end_date_ = self._rate_index_._rolling_method_.adjust( self._end_date_) if '_pay_date_' not in self._modified_members: self._pay_date_ = BusinessDate.add_period(self._end_date_, self._rate_index_._spot_) self._pay_date_ = self._rate_index_._rolling_method_.adjust( self._pay_date_) if '_year_fraction_' not in self._modified_members: self._year_fraction_ = self._accrued_day_count_.get_year_fraction( self._start_date_, self._end_date_) # validate if '_year_fraction_' in self._modified_members and '_end_date_' in self._modified_members: yf = self._accrued_day_count_.get_year_fraction( self._start_date_, self._end_date_) # if not float_equal(yf, self._year_fraction_, 0.00000001): if not abs(yf - self._year_fraction_) < 0.00000001: s = self.__class__.__name__ # raise ValueError('If %s.YearFraction and %s.EndDate is given they must meet.' % (s, s)) if self._end_date_ < self._start_date_: s = self.__class__.__name__, self._end_date_, self.__class__.__name__, self._start_date_ raise ValueError( '%s.EndDate (%s) must scheduled after %s.StartDate (%s).' % s) if self._year_fraction_ > 0 and self._pay_date_ <= self._reset_date_: s = self.__class__.__name__, self._pay_date_, self.__class__.__name__, self._reset_date_ raise ValueError( '%s.PayDate (%s) must scheduled after %s.SetDate (%s).' % s) return self def get_expected_payoff(self, value_date=BusinessDate(), index_model_dict={}): """ calculates expected payoff at paydate :param BusinessDate value_date: :param IndexModel index_model_dict: :return: float, the pv of the cash flow """ idx_model = index_model_dict.get(self._rate_index_.object_name, self._rate_index_.index_model) rate = 0.0 is_fix_rate = True is_fixed_cash_flow = False fwd = None fixing_value = None time = None vol = None effective_floor_strike = None effective_cap_strike = None pay_rec = 1.0 if self.is_rec else -1.0 payoff = 0.0 rate_ccy = self.currency if self._pay_date_ > value_date: if self._constant_rate_: rate += self._constant_rate_ else: is_fix_rate = False # fixme: be consistent, use is_fixed_flow? rate_ccy = self._rate_index_.currency if self._rate_index_.has_fixing(self._reset_date_): is_fixed_cash_flow = True fixing_value = self._rate_index_.get_fixing( self._reset_date_) rate = fixing_value else: fwd = idx_model.get_value(self._rate_index_, self._reset_date_, self._pay_date_) rate = fwd if self._floor_ != NO_FLOOR_VALUE: effective_floor_strike = ( self._floor_ - self._spread_) / self._multiplier_ if not is_fixed_cash_flow: value, vol, time, fwd = idx_model.get_option_payoff_value( self._rate_index_, Put(), effective_floor_strike, self._reset_date_) else: value = max(effective_floor_strike - rate, 0) rate += value if self._cap_ != NO_CAP_VALUE: effective_cap_strike = (self._cap_ - self._spread_) / self._multiplier_ if not is_fixed_cash_flow: value, vol, time, fwd = idx_model.get_option_payoff_value( self._rate_index_, Call(), effective_cap_strike, self._reset_date_) else: value = max(rate - effective_cap_strike, 0) rate -= value rate *= self._multiplier_ rate += self._spread_ fx_index = self._get_fx_index(self.currency) fx = fx_index.get_value(value_date) if fx_index else 1.0 payoff = fx * rate * self._year_fraction_ * self._notional_ + self._amount_ Record(pay_rec="REC" if pay_rec == 1.0 else "PAY", year_fraction=self.year_fraction, cf_name=self.object_name, notional=self._notional_, currency=self.currency, rate_ccy=rate_ccy, is_fix_rate=is_fix_rate, forward=fwd, is_fixed_cash_flow=is_fixed_cash_flow, fixing_value=fixing_value, vol=vol, expiry_time=time, spread=self._spread_, reset_date=self._reset_date_, start_date=self._start_date_, end_date=self._end_date_, pay_date=self._pay_date_, constant_rate=self._constant_rate_, amount=self._amount_, effective_floor_strike=effective_floor_strike, effective_cap_strike=effective_cap_strike, index=self._rate_index_.object_name, expected_payoff=payoff, floor=None if self._floor_ == NO_FLOOR_VALUE else self._floor_, cap=None if self._cap_ == NO_CAP_VALUE else self._cap_) # pylint: disable=unexpected-keyword-arg return payoff @Record.Prefix() def get_present_value(self, value_date=BusinessDate(), index_model_dict={}): """ :param BusinessDate value_date: :param IndexModel index_model_dict: :return: float, the pv of the cash flow """ payoff = self.get_expected_payoff(value_date, index_model_dict) df_index = self._get_discount_index(self.currency) pv_let = self._get_discounted_value(value_date, payoff, df_index) return pv_let