public AssetSwap(bool parAssetSwap, Bond bond, double bondCleanPrice, double nonParRepayment, double gearing, IborIndex iborIndex, double spread = 0.0, DayCounter floatingDayCount = null, Date dealMaturity = null, bool payBondCoupon = false) : base(2) { bond_ = bond; bondCleanPrice_ = bondCleanPrice; nonParRepayment_ = nonParRepayment; spread_ = spread; parSwap_ = parAssetSwap; Schedule tempSch = new Schedule(bond_.settlementDate(), bond_.maturityDate(), iborIndex.tenor(), iborIndex.fixingCalendar(), iborIndex.businessDayConvention(), iborIndex.businessDayConvention(), DateGeneration.Rule.Backward, false); // endOfMonth if (dealMaturity == null) { dealMaturity = bond_.maturityDate(); } Utils.QL_REQUIRE(dealMaturity <= tempSch.dates().Last(), () => "deal maturity " + dealMaturity + " cannot be later than (adjusted) bond maturity " + tempSch.dates().Last()); Utils.QL_REQUIRE(dealMaturity > tempSch.dates()[0], () => "deal maturity " + dealMaturity + " must be later than swap start date " + tempSch.dates()[0]); // the following might become an input parameter BusinessDayConvention paymentAdjustment = BusinessDayConvention.Following; Date finalDate = tempSch.calendar().adjust(dealMaturity, paymentAdjustment); Schedule schedule = tempSch.until(finalDate); // bondCleanPrice must be the (forward) clean price // at the floating schedule start date upfrontDate_ = schedule.startDate(); double dirtyPrice = bondCleanPrice_ + bond_.accruedAmount(upfrontDate_); double notional = bond_.notional(upfrontDate_); /* In the market asset swap, the bond is purchased in return for * payment of the full price. The notional of the floating leg is * then scaled by the full price. */ if (!parSwap_) { notional *= dirtyPrice / 100.0; } if (floatingDayCount == null) { legs_[1] = new IborLeg(schedule, iborIndex) .withSpreads(spread) .withGearings(gearing) .withNotionals(notional) .withPaymentAdjustment(paymentAdjustment); } else { legs_[1] = new IborLeg(schedule, iborIndex) .withSpreads(spread) .withGearings(gearing) .withPaymentDayCounter(floatingDayCount) .withNotionals(notional) .withPaymentAdjustment(paymentAdjustment); } foreach (CashFlow c in legs_[1]) { c.registerWith(update); } List <CashFlow> bondLeg = bond_.cashflows(); // skip bond redemption int i; for (i = 0; i < bondLeg.Count && bondLeg[i].date() <= dealMaturity; ++i) { // whatever might be the choice for the discounting engine // bond flows on upfrontDate_ must be discarded bool upfrontDateBondFlows = false; if (!bondLeg[i].hasOccurred(upfrontDate_, upfrontDateBondFlows)) { legs_[0].Add(bondLeg[i]); } } // if the first skipped cashflow is not the redemption // and it is a coupon then add the accrued coupon if (i < bondLeg.Count - 1) { Coupon c = bondLeg[i] as Coupon; if (c != null) { CashFlow accruedCoupon = new SimpleCashFlow(c.accruedAmount(dealMaturity), finalDate); legs_[0].Add(accruedCoupon); } } // add the nonParRepayment_ CashFlow nonParRepaymentFlow = new SimpleCashFlow(nonParRepayment_, finalDate); legs_[0].Add(nonParRepaymentFlow); Utils.QL_REQUIRE(!legs_[0].empty(), () => "empty bond leg to start with"); // special flows if (parSwap_) { // upfront on the floating leg double upfront = (dirtyPrice - 100.0) / 100.0 * notional; CashFlow upfrontCashFlow = new SimpleCashFlow(upfront, upfrontDate_); legs_[1].Insert(0, upfrontCashFlow); // backpayment on the floating leg // (accounts for non-par redemption, if any) double backPayment = notional; CashFlow backPaymentCashFlow = new SimpleCashFlow(backPayment, finalDate); legs_[1].Add(backPaymentCashFlow); } else { // final notional exchange CashFlow finalCashFlow = new SimpleCashFlow(notional, finalDate); legs_[1].Add(finalCashFlow); } Utils.QL_REQUIRE(!legs_[0].empty(), () => "empty bond leg"); foreach (CashFlow c in legs_[0]) { c.registerWith(update); } if (payBondCoupon) { payer_[0] = -1.0; payer_[1] = +1.0; } else { payer_[0] = +1.0; payer_[1] = -1.0; } }
public AssetSwap(bool parAssetSwap, Bond bond, double bondCleanPrice, double nonParRepayment, double gearing, IborIndex iborIndex, double spread = 0.0, DayCounter floatingDayCount = null, Date dealMaturity = null, bool payBondCoupon = false) : base(2) { bond_ = bond; bondCleanPrice_ = bondCleanPrice; nonParRepayment_ = nonParRepayment; spread_ = spread; parSwap_ = parAssetSwap; Schedule tempSch = new Schedule(bond_.settlementDate(), bond_.maturityDate(), iborIndex.tenor(), iborIndex.fixingCalendar(), iborIndex.businessDayConvention(), iborIndex.businessDayConvention(), DateGeneration.Rule.Backward, false); // endOfMonth if (dealMaturity == null) dealMaturity = bond_.maturityDate(); Utils.QL_REQUIRE( dealMaturity <= tempSch.dates().Last(), () => "deal maturity " + dealMaturity + " cannot be later than (adjusted) bond maturity " + tempSch.dates().Last()); Utils.QL_REQUIRE( dealMaturity > tempSch.dates()[0], () => "deal maturity " + dealMaturity + " must be later than swap start date " + tempSch.dates()[0]); // the following might become an input parameter BusinessDayConvention paymentAdjustment = BusinessDayConvention.Following; Date finalDate = tempSch.calendar().adjust(dealMaturity, paymentAdjustment); Schedule schedule = tempSch.until(finalDate); // bondCleanPrice must be the (forward) clean price // at the floating schedule start date upfrontDate_ = schedule.startDate(); double dirtyPrice = bondCleanPrice_ + bond_.accruedAmount(upfrontDate_); double notional = bond_.notional(upfrontDate_); /* In the market asset swap, the bond is purchased in return for payment of the full price. The notional of the floating leg is then scaled by the full price. */ if (!parSwap_) notional *= dirtyPrice / 100.0; if (floatingDayCount == null) legs_[1] = new IborLeg(schedule, iborIndex) .withSpreads(spread) .withGearings(gearing) .withNotionals(notional) .withPaymentAdjustment(paymentAdjustment); else legs_[1] = new IborLeg(schedule, iborIndex) .withSpreads(spread) .withGearings(gearing) .withPaymentDayCounter(floatingDayCount) .withNotionals(notional) .withPaymentAdjustment(paymentAdjustment); foreach (CashFlow c in legs_[1]) c.registerWith(update); List<CashFlow> bondLeg = bond_.cashflows(); // skip bond redemption int i; for (i = 0; i < bondLeg.Count && bondLeg[i].date() <= dealMaturity; ++i) { // whatever might be the choice for the discounting engine // bond flows on upfrontDate_ must be discarded bool upfrontDateBondFlows = false; if (!bondLeg[i].hasOccurred(upfrontDate_, upfrontDateBondFlows)) legs_[0].Add(bondLeg[i]); } // if the first skipped cashflow is not the redemption // and it is a coupon then add the accrued coupon if (i < bondLeg.Count - 1) { Coupon c = bondLeg[i] as Coupon; if (c != null) { CashFlow accruedCoupon = new SimpleCashFlow(c.accruedAmount(dealMaturity), finalDate); legs_[0].Add(accruedCoupon); } } // add the nonParRepayment_ CashFlow nonParRepaymentFlow = new SimpleCashFlow(nonParRepayment_, finalDate); legs_[0].Add(nonParRepaymentFlow); Utils.QL_REQUIRE( !legs_[0].empty(), () => "empty bond leg to start with" ); // special flows if (parSwap_) { // upfront on the floating leg double upfront = (dirtyPrice - 100.0) / 100.0 * notional; CashFlow upfrontCashFlow = new SimpleCashFlow(upfront, upfrontDate_); legs_[1].Insert(0, upfrontCashFlow); // backpayment on the floating leg // (accounts for non-par redemption, if any) double backPayment = notional; CashFlow backPaymentCashFlow = new SimpleCashFlow(backPayment, finalDate); legs_[1].Add(backPaymentCashFlow); } else { // final notional exchange CashFlow finalCashFlow = new SimpleCashFlow(notional, finalDate); legs_[1].Add(finalCashFlow); } Utils.QL_REQUIRE( !legs_[0].empty(), () => "empty bond leg" ); foreach (CashFlow c in legs_[0]) c.registerWith(update); if (payBondCoupon) { payer_[0] = -1.0; payer_[1] = +1.0; } else { payer_[0] = +1.0; payer_[1] = -1.0; } }