def test_roll(self):
        """
        
            TODO: include assert for each scenario shift
        
        :return: 
        """
        self.sdate = datetime.datetime(2018, 1, 8)
        self.value_date = self.sdate.strftime('%d/%m/%Y')
        self.verbose = 0
        self.is_buy_protection = 0

        # build imm_dates TODO: hide this away internally?
        self.imm_dates = [
            f[1] for f in imm_date_vector(start_date=self.sdate,
                                          tenor_list=self.tenor_list)
        ]

        f = cds_all_in_one(self.trade_date, self.effective_date,
                           self.maturity_date, self.value_date,
                           self.accrual_start_date, self.recovery_rate,
                           self.coupon, self.notional, self.is_buy_protection,
                           self.swap_rates, self.swap_tenors,
                           self.swap_maturity_dates, self.credit_spreads,
                           self.credit_spread_tenors, self.spread_roll_tenors,
                           self.imm_dates, self.scenario_shifts, self.verbose)
    def test_single_factor_shift(self):
        """ method to test roll and roll shocks """
        """

            base case roll test; 
            
            '1D' - moves the roll date one day past maturity 
            '-1D' - moves the stepin date one day closer to maturity
            '-1W' - moves stepin one week closer to maturity
            '-1M' - 1 month closer
            '-6M' - 6 months closer
            '-1Y' - 1 whole year closer
            '-5Y' - 5 whole years closer

        :return: 
        """
        self.sdate = datetime.datetime(2018, 1, 8)
        self.value_date = self.sdate.strftime('%d/%m/%Y')
        self.verbose = 0
        self.is_buy_protection = 0

        # used to generate and shock roll dataset
        # both positive and negative; -1D moves the maturity date one day closer to stepIn
        # whilst 1D pushes the scheduled termination date further out increasing the TTM.
        self.spread_roll_tenors = ['1D', '-1D', '-1W', '-1M', '-6M', '-1Y']
        self.scenario_shifts = [0]

        # build imm_dates TODO: hide this away internally somewhere?
        self.imm_dates = [f[1] for f in imm_date_vector(start_date=self.sdate, tenor_list=self.tenor_list)]

        f = cds_all_in_one(self.trade_date,
                           self.effective_date,
                           self.maturity_date,
                           self.value_date,
                           self.accrual_start_date,
                           self.recovery_rate,
                           self.coupon,
                           self.notional,
                           self.is_buy_protection,
                           self.swap_rates,
                           self.swap_tenors,
                           self.swap_maturity_dates,
                           self.credit_spreads,
                           self.credit_spread_tenors,
                           self.spread_roll_tenors,
                           self.imm_dates,
                           self.scenario_shifts,
                           self.verbose)

        pv_clean = f[0][1]

        # self.spread_roll_tenors zero shift
        self.spread_pv_clean_result = 0.03234206758048001
        # self.spread_roll_tenors zero shift
        self.spread_roll_tenors_results = [0.03234206758048001, 0.03230149347632522,
                                           0.03205803736866009, 0.031124617333038864, 0.024909464222462,
                                           0.017504780143193628]

        for test_value, result_value in zip(list(f[3]), self.spread_roll_tenors_results):
            self.assertAlmostEqual(pv_clean-test_value, self.spread_pv_clean_result-result_value, 4)
    def test_sell_protection(self):
        """ method to test sell protection single name CDS """

        self.sdate = datetime.datetime(2018, 1, 8)
        self.value_date = self.sdate.strftime('%d/%m/%Y')
        self.verbose = 0
        self.is_buy_protection = 0

        # build imm_dates TODO: hide this away internally?
        self.imm_dates = [
            f[1] for f in imm_date_vector(start_date=self.sdate,
                                          tenor_list=self.tenor_list)
        ]

        f = cds_all_in_one(self.trade_date, self.effective_date,
                           self.maturity_date, self.value_date,
                           self.accrual_start_date, self.recovery_rate,
                           self.coupon, self.notional, self.is_buy_protection,
                           self.swap_rates, self.swap_tenors,
                           self.swap_maturity_dates, self.credit_spreads,
                           self.credit_spread_tenors, self.spread_roll_tenors,
                           self.imm_dates, self.scenario_shifts, self.verbose)

        # expand tuple
        pv_dirty, pv_clean, ai, cs01, dv01, duration_in_milliseconds = f[0]
        pvbp6m, pvbp1y, pvbp2y, pvbp3y, pvbp4y, pvbp5y, pvbp7y, pvbp10y = f[1]

        # sell protection +ve npv
        # sell protection +ve npv
        # sell protection -ve cs01
        # sell protection -ve dv01

        self.assertAlmostEqual(1.23099324435, pv_dirty)
        self.assertAlmostEqual(1.19210435546, pv_clean)
        self.assertAlmostEqual(0.0388888888889, ai)
        self.assertAlmostEqual(-14014.5916905, cs01 * 1.0e6)
        self.assertAlmostEqual(-131.61798715, dv01 * 1.0e6)

        #print("cob_date: {0} pv_dirty: {1} pv_clean: {2} ai: {3} cs01: {4} dv01: {5} wall_time: {6}".format(
        #    self.value_date,pv_dirty, pv_clean, ai, cs01 * 1e6, dv01 * 1e6, duration_in_milliseconds))

        six_month_equivalent_notional = -cs01 / pvbp6m
        one_year_equivalent_notional = -cs01 / pvbp1y
        two_year_equivalent_notional = -cs01 / pvbp2y
        three_year_equivalent_notional = -cs01 / pvbp3y
        four_year_equivalent_notional = -cs01 / pvbp4y
        five_year_equivalent_notional = -cs01 / pvbp5y
        seven_year_equivalent_notional = -cs01 / pvbp7y
        ten_year_equivalent_notional = -cs01 / pvbp10y

        self.assertAlmostEqual(307.495318062, six_month_equivalent_notional)
        self.assertAlmostEqual(145.357246478, one_year_equivalent_notional)
        self.assertAlmostEqual(70.8820514668, two_year_equivalent_notional)
        self.assertAlmostEqual(46.8826264701, three_year_equivalent_notional)
        self.assertAlmostEqual(35.1120297467, four_year_equivalent_notional)
        self.assertAlmostEqual(28.1221873429, five_year_equivalent_notional)
        self.assertAlmostEqual(20.2184428985, seven_year_equivalent_notional)
        self.assertAlmostEqual(14.4072625131, ten_year_equivalent_notional)
    def test_1day_roll_buy_protection_cds_shift(self):
        """ method to test roll simple 1 day clean roll pv """
        """
    
            base case roll test;     
            '-1D' - moves the stepin date one day closer to maturity
    
        :return: 
        """
        self.sdate = datetime.datetime(2018, 1, 8)
        self.value_date = self.sdate.strftime('%d/%m/%Y')
        self.verbose = 0
        self.is_buy_protection = 1

        # used to generate and shock roll dataset
        self.spread_roll_tenors = ['-1D', '-1W', '-1M', '-1Y']
        self.scenario_shifts = [0]

        # build imm_dates TODO: hide this away internally somewhere?
        self.imm_dates = [f[1] for f in imm_date_vector(start_date=self.sdate, tenor_list=self.tenor_list)]

        f = cds_all_in_one(self.trade_date,
                           self.effective_date,
                           self.maturity_date,
                           self.value_date,
                           self.accrual_start_date,
                           self.recovery_rate,
                           self.coupon,
                           self.notional,
                           self.is_buy_protection,
                           self.swap_rates,
                           self.swap_tenors,
                           self.swap_maturity_dates,
                           self.credit_spreads,
                           self.credit_spread_tenors,
                           self.spread_roll_tenors,
                           self.imm_dates,
                           self.scenario_shifts,
                           self.verbose)

        pv_clean = f[0][1]

        # self.spread_roll_tenors zero shift
        self.spread_pv_clean_result = -0.03234206758048001
        self.spread_roll_tenors_results = [-0.03230149347632522, -0.03205803736866009,
                                           -0.031124617333038864, -0.017504780143193628]

        for test_value, result_value in zip(list(f[3]), self.spread_roll_tenors_results):
            self.assertAlmostEqual(pv_clean-test_value, self.spread_pv_clean_result-result_value, 4)
    def test_sell_protection(self):

        self.sdate = datetime.datetime(2018, 1, 8)
        self.value_date = self.sdate.strftime('%d/%m/%Y')
        self.verbose = 0
        self.is_buy_protection = 0

        # build imm_dates TODO: hide this away internally?
        self.imm_dates = [
            f[1] for f in imm_date_vector(start_date=self.sdate,
                                          tenor_list=self.tenor_list)
        ]

        f = cds_all_in_one(self.trade_date, self.effective_date,
                           self.maturity_date, self.value_date,
                           self.accrual_start_date, self.recovery_rate,
                           self.coupon, self.notional, self.is_buy_protection,
                           self.swap_rates, self.swap_tenors,
                           self.swap_maturity_dates, self.credit_spreads,
                           self.credit_spread_tenors, self.spread_roll_tenors,
                           self.imm_dates, self.scenario_shifts, self.verbose)

        # expand tuple
        pv_dirty, pv_clean, ai, cs01, dv01, duration_in_milliseconds = f[0]
        pvbp6m, pvbp1y, pvbp2y, pvbp3y, pvbp4y, pvbp5y, pvbp7y, pvbp10y = f[1]

        # sell protection +ve npv
        # sell protection +ve npv
        # sell protection -ve cs01
        # sell protection -ve dv01

        self.assertAlmostEquals(-22.3413350001, pv_dirty)
        self.assertAlmostEquals(-22.380223889, pv_clean)
        self.assertAlmostEquals(0.0388888888889, ai)
        self.assertAlmostEquals(-2612.54681047, cs01 * 1.0e6)
        self.assertAlmostEquals(1452.684883, dv01 * 1.0e6)
    def test_sell_proptection_roll(self):
        """ method to test sell protection single name CDS stressed clean roll pv """
        """
        
            C:\github\CreditDefaultSwapPricer\isda\tests>python TestCdsPricer.py
            .-50 (1.2885563078769746, 1.2867443110180663, 1.275872200629506, 1.2341941013410378, 0.9569132641711624, 0.627070135500295, -0.0)
            -10 (1.2113774003109585, 1.209675487125174, 1.199463788179147, 1.160315827437148, 0.8998078545285471, 0.5897852857014516, -0.0)
            0 (1.1921043554642177, 1.1904299019458315, 1.1803829401914545, 1.141866141475618, 0.8855434271776345, 0.5804691779283648, -0.0)
            10 (1.1728399756302652, 1.1711929573119526, 1.1613105866742064, 1.1234244016646082, 0.8712837666636524, 0.5711551111334601, -0.0)
            20 (1.1535842569100503, 1.1519646493398115, 1.1422467238468759, 1.10499060457683, 0.8570288713937431, 0.5618430848703984, -0.0)
            50 (1.0958690284470913, 1.0943315065849426, 1.085106041654529, 1.0497368354280734, 0.8142927611229521, 0.5339192448040203, -0.0)
            150 (0.9040463035118479, 0.9027808181324095, 0.8951874402865281, 0.8660724744683601, 0.6721481462760723, 0.44097222887852516, -0.0)
            100 (0.9998498397804708, 0.9984486399470907, 0.9900410322567444, 0.9578057553878155, 0.743161067035703, 0.48742028035749574, -0.0)
            ...
            ----------------------------------------------------------------------
            Ran 4 tests in 0.343s
        
        :return: 
        """
        self.sdate = datetime.datetime(2018, 1, 8)
        self.value_date = self.sdate.strftime('%d/%m/%Y')
        self.verbose = 0
        self.is_buy_protection = 0

        self.spread_roll_tenors_results = {
            -50: [
                1.2885563078769746, 1.2867443110180663, 1.275872200629506,
                1.2341941013410378, 0.9569132641711624, 0.627070135500295, -0.0
            ],
            -10: [
                1.2113774003109585, 1.209675487125174, 1.199463788179147,
                1.160315827437148, 0.8998078545285471, 0.5897852857014516, -0.0
            ],
            0: [
                1.1921043554642177, 1.1904299019458315, 1.1803829401914545,
                1.141866141475618, 0.8855434271776345, 0.5804691779283648, -0.0
            ],
            10: [
                1.1728399756302652, 1.1711929573119526, 1.1613105866742064,
                1.1234244016646082, 0.8712837666636524, 0.5711551111334601,
                -0.0
            ],
            20: [
                1.1535842569100503, 1.1519646493398115, 1.1422467238468759,
                1.10499060457683, 0.8570288713937431, 0.5618430848703984, -0.0
            ],
            50: [
                1.0958690284470913, 1.0943315065849426, 1.085106041654529,
                1.0497368354280734, 0.8142927611229521, 0.5339192448040203,
                -0.0
            ],
            100: [
                0.9998498397804708, 0.9984486399470907, 0.9900410322567444,
                0.9578057553878155, 0.743161067035703, 0.48742028035749574,
                -0.0
            ],
            150: [
                0.9040463035118479, 0.9027808181324095, 0.8951874402865281,
                0.8660724744683601, 0.6721481462760723, 0.44097222887852516,
                -0.0
            ]
        }
        # build imm_dates TODO: hide this away internally?
        self.imm_dates = [
            f[1] for f in imm_date_vector(start_date=self.sdate,
                                          tenor_list=self.tenor_list)
        ]

        f = cds_all_in_one(self.trade_date, self.effective_date,
                           self.maturity_date, self.value_date,
                           self.accrual_start_date, self.recovery_rate,
                           self.coupon, self.notional, self.is_buy_protection,
                           self.swap_rates, self.swap_tenors,
                           self.swap_maturity_dates, self.credit_spreads,
                           self.credit_spread_tenors, self.spread_roll_tenors,
                           self.imm_dates, self.scenario_shifts, self.verbose)

        for scenario, test_value_list in enumerate(f[3:]):
            for test_value, result_value in zip(
                    test_value_list, self.spread_roll_tenors_results[
                        self.scenario_shifts[scenario]]):
                self.assertAlmostEqual(test_value, result_value, 4)
    def test_sell_protection_par_spread(self):

        self.sdate = datetime.datetime(2018, 1, 8)
        self.value_date = self.sdate.strftime('%d/%m/%Y')
        self.verbose = 0
        self.is_buy_protection = 0

        # build imm_dates TODO: hide this away internally?
        self.imm_dates = [
            f[1] for f in imm_date_vector(start_date=self.sdate,
                                          tenor_list=self.tenor_list)
        ]

        f = cds_all_in_one(self.trade_date, self.effective_date,
                           self.maturity_date, self.value_date,
                           self.accrual_start_date, self.recovery_rate,
                           self.coupon, self.notional, self.is_buy_protection,
                           self.swap_rates, self.swap_tenors,
                           self.swap_maturity_dates, self.credit_spreads,
                           self.credit_spread_tenors, self.spread_roll_tenors,
                           self.imm_dates, self.scenario_shifts, self.verbose)

        # expand tuple
        pv_dirty, pv_clean, ai, cs01, dv01, duration_in_milliseconds = f[0]
        pvbp6m, pvbp1y, pvbp2y, pvbp3y, pvbp4y, pvbp5y, pvbp7y, pvbp10y = f[1]
        ps_1m, ps_2m, ps_3M, ps_6M, ps_9M, ps_1Y, ps_2Y, ps_3Y, ps_4Y, ps_5Y, ps_6Y, ps_7Y, ps_8Y, ps_9Y, ps_10Y = f[
            2]

        self.assertAlmostEquals(0.00274826727324, ps_1m)
        self.assertAlmostEquals(0.00274883148583, ps_2m)
        self.assertAlmostEquals(0.00274929868985, ps_3M)
        self.assertAlmostEquals(0.00274939866579, ps_6M)
        self.assertAlmostEquals(0.00274936653181, ps_9M)
        self.assertAlmostEquals(0.00274937754343, ps_1Y)
        self.assertAlmostEquals(0.00274932944417, ps_2Y)
        self.assertAlmostEquals(0.00274932454643, ps_3Y)
        self.assertAlmostEquals(0.00274932165857, ps_4Y)
        self.assertAlmostEquals(0.0027493199385, ps_5Y)
        self.assertAlmostEquals(0.00274926894167, ps_6Y)
        self.assertAlmostEquals(0.00274932296072, ps_7Y)
        self.assertAlmostEquals(0.00274925367015, ps_8Y)
        self.assertAlmostEquals(0.00274927195173, ps_9Y)
        self.assertAlmostEquals(0.00274933238284, ps_10Y)

        # sell protection +ve npv
        # sell protection +ve npv
        # sell protection -ve cs01
        # sell protection -ve dv01

        self.assertAlmostEquals(1.23099324435, pv_dirty)
        self.assertAlmostEquals(1.19210435546, pv_clean)
        self.assertAlmostEquals(0.0388888888889, ai)
        self.assertAlmostEquals(-14014.5916905, cs01 * 1.0e6)
        self.assertAlmostEquals(-131.61798715, dv01 * 1.0e6)

        six_month_equivalent_notional = -cs01 / pvbp6m
        one_year_equivalent_notional = -cs01 / pvbp1y
        two_year_equivalent_notional = -cs01 / pvbp2y
        three_year_equivalent_notional = -cs01 / pvbp3y
        four_year_equivalent_notional = -cs01 / pvbp4y
        five_year_equivalent_notional = -cs01 / pvbp5y
        seven_year_equivalent_notional = -cs01 / pvbp7y
        ten_year_equivalent_notional = -cs01 / pvbp10y

        self.assertAlmostEquals(307.495318062, six_month_equivalent_notional)
        self.assertAlmostEquals(145.357246478, one_year_equivalent_notional)
        self.assertAlmostEquals(70.8820514668, two_year_equivalent_notional)
        self.assertAlmostEquals(46.8826264701, three_year_equivalent_notional)
        self.assertAlmostEquals(35.1120297467, four_year_equivalent_notional)
        self.assertAlmostEquals(28.1221873429, five_year_equivalent_notional)
        self.assertAlmostEquals(20.2184428985, seven_year_equivalent_notional)
        self.assertAlmostEquals(14.4072625131, ten_year_equivalent_notional)
    def test_two_factor_shift(self):
        """ method to test roll and roll shocks generate a surface of pv change """
        """
        
            roll and roll shocks generate a surface of pv change;
            for each shock the vector of roll tenors is evaluated
           
           The example below is a typical output from shock vector [-10, 0, 10] which translates internally 
           in the scenario engine; via the formula below; you can see that -10 is translated 
           into a -10/100 or -0.1 * spread_rate value and merged into the original spread_rates. This has the overall
           affect to move the spread_rate lower by 10% ahead of shocking the 
           
           spread_rates[r] + spread_rates[r]  * scenario_tenors[s]/100;
            
            -10 (0.024377398537551408, 0.017571131660956488, 0.007359432714929524, -0.0317885280270698, 
                -0.2922965009356705, -0.602319069762766, -1.1921043554642177)
            0 (0.0050220109323667605, -0.0016744535183860904, -0.01172141527276311, -0.0502382139885996, 
                -0.3065609282865831, -0.6116351775358528, -1.1921043554642177)
            10 (-0.014324638086876014, -0.020911398152265066, -0.030793768790011236, -0.06867995379960933, 
                -0.3208205888005652, -0.6209492443307575, -1.1921043554642177)

            The output above has been generated for 7 separate shocks to the time to maturity. It should be possible 
            to compute an entire surface before and after the maturity date of each CDS instrument using this approach
            which can provide detailed information around the roll of each CDS contract.
        
        :return: 
        """

        self.sdate = datetime.datetime(2018, 1, 8)
        self.value_date = self.sdate.strftime('%d/%m/%Y')
        self.verbose = 0
        self.is_buy_protection = 0

        # used to generate and shock roll dataset
        self.spread_roll_tenors = ['1Y','1D', '-1D', '-1W', '-1M', '-6M', '-1Y']
        self.scenario_shifts = [-10, 0, 10]

        # build imm_dates TODO: hide this away internally somewhere?
        self.imm_dates = [f[1] for f in imm_date_vector(start_date=self.sdate, tenor_list=self.tenor_list)]

        f = cds_all_in_one(self.trade_date,
                           self.effective_date,
                           self.maturity_date,
                           self.value_date,
                           self.accrual_start_date,
                           self.recovery_rate,
                           self.coupon,
                           self.notional,
                           self.is_buy_protection,
                           self.swap_rates,
                           self.swap_tenors,
                           self.swap_maturity_dates,
                           self.credit_spreads,
                           self.credit_spread_tenors,
                           self.spread_roll_tenors,
                           self.imm_dates,
                           self.scenario_shifts,
                           self.verbose)

        pv_clean = f[0][1]

        # results to compare against
        self.spread_roll_tenors_results = {-10: [0.04387513244181401, 0.03306420268550876, 0.03302212083102655,
                                                 0.032769619019361866, 0.031801536455466885,
                                                 0.025356314262256027, 0.01767911535374796]
                                         , 0: [0.042111643439626215, 0.03234206758048001, 0.03230149347632522,
                                               0.03205803736866009, 0.031124617333038864,
                                               0.024909464222462, 0.017504780143193628]
                                         , 10:[0.040349886127636986, 0.03162025310504571, 0.03158118526362845,
                                               0.031346766012800564, 0.030447975828985945,
                                               0.024462724355530034, 0.01733046875873452]}
        # confirm that we have managed to generate the accurate number of scenario details
        #
        # confirm we have the same dataset
        for i, test_value_list in enumerate(f[3:]):
            for test_value, result_value in zip(test_value_list, self.spread_roll_tenors_results[self.scenario_shifts[i]]):
                self.assertAlmostEqual(test_value, result_value, 4)