Bond vs Swap Cashflows

Bond vs Swap Fixed Leg: The Devil in the Cashflow Details

When pricing asset swaps or building fixed income analytics, it’s easy to assume that a swap’s fixed leg and a bond with the same coupon rate will produce identical cashflows. In practice, subtle convention differences mean the cashflows diverge slightly - and these details matter for accurate pricing.

The Key Differences

1. Business Day Convention

  • Bonds: Typically use FOLLOWING - if a payment date falls on a non-business day, move to the next business day (even if it crosses into the next month)
  • Swaps: Typically use MODIFIED FOLLOWING - move to the next business day UNLESS that crosses into a new month, then move backwards to the previous business day

Why this matters: If March 30 falls on a weekend and April 1 is the next business day:

  • Bond with FOLLOWING: Pays April 1 (crosses month boundary)
  • Swap with MODIFIED FOLLOWING: Pays March 29 (stays in original month)

This creates different payment dates and different accrual periods between instruments that theoretically should match.

2. Day Count Conventions

  • Bonds: In Sweden often 30E/360 or 30/360 (assumes 30-day months). Intl often Act/Act.
  • Swaps: Fixed leg commonly uses 30/360, but the specific variant matters (30/360 vs 30E/360 vs ACT/360)

3. Stub Period Handling

  • Bonds: First/last coupon may be irregular; accrual follows bond indenture rules
  • Swaps: Stub calculations can differ (short first, long first, etc.) based on swap template

4. Interest Accrual Rules

  • Bonds: IR_MATURITY_NOADJ - accrual to unadjusted maturity
  • Swaps: Can use IR_MATURITY_NOADJ or IR_MATURITY_ADJ - affects final coupon

Real Example: Swedish Government Bond Asset Swap

Here’s actual output from pricing a Swedish 3.5% 2039 bond (SE1053) traded on 2026-02-10 with a matching fixed-for-floating swap:

Bond Calc Date Bond Pay Date Bond Flow Swap Date Swap Flow Notes
2026-03-30 2026-03-30 35,000.00 2026-03-30 -4,666.67 Stub period from trade date
2027-03-30 2027-03-30 35,000.00 2027-03-30 -35,000.00 Perfect match
2028-03-30 2028-03-30 35,000.00 2028-03-30 -35,000.00 Perfect match
2029-03-30 2029-04-03 35,000.00 2029-03-29 -34,902.78 Different adjusted dates!
2030-03-30 2030-04-01 35,000.00 2030-03-29 -35,000.00 Different pay dates, same accrual
2031-03-30 2031-03-31 35,000.00 2031-03-31 -35,097.22 Same pay date, different accrual
2032-03-30 2032-03-30 35,000.00 2032-03-30 -35,000.00 Perfect match
2033-03-30 2033-03-30 35,000.00 2033-03-30 -35,000.00 Perfect match
2034-03-30 2034-03-30 35,000.00 2034-03-30 -35,000.00 Perfect match
2035-03-30 2035-03-30 35,000.00 2035-03-30 -35,000.00 Perfect match
2036-03-30 2036-03-31 35,000.00 2036-03-31 -35,000.00 Same adjustment
2037-03-30 2037-03-30 35,000.00 2037-03-30 -35,000.00 Perfect match
2038-03-30 2038-03-30 35,000.00 2038-03-30 -35,000.00 Perfect match
2039-03-30 2039-03-30 1,035,000.00 2039-03-30 -35,000.00 Final principal payment

What’s Happening Here?

First payment (2026-03-30):

  • Bond: Full 35,000 coupon (bond accrues from issue date 2000-03-30). Typical in Sweden. Bonds generally can have either full copupon or a proportional first cpn.
  • Swap: Short stub from trade date 2026-02-10 → 2026-03-30 = ~48 days
  • Stub calculation: 35,000 × (48/360) ≈ 4,667

2029-04-03 vs 2029-03-29 - The Critical Divergence:

  • Calculation date: 2029-03-30 (falls on Friday before Easter weekend)
  • Bond (FOLLOWING): Adjusts to 2029-04-03 (next business day, crosses into April)
  • Swap (MODIFIED FOLLOWING): Adjusts to 2029-03-29 (can’t cross month, goes backward to Thursday)
  • Different payment dates = different accrual periods = -34,902.78 vs 35,000.00
  • This is the classic FOLLOWING vs MODIFIED FOLLOWING divergence

2030-04-01 vs 2030-03-29:

  • Calculation date: 2030-03-30 (Saturday)
  • Bond: 2030-04-01 (FOLLOWING)
  • Swap: 2030-03-29 (MODIFIED FOLLOWING - can’t cross month boundary)
  • Despite different payment dates, cashflows match at 35,000 (accrual periods align)

2031-03-31 - Same Adjusted Date, Different Accruals:

  • Both instruments adjust to 2031-03-31
  • But different accrual calculation rules create -35,097.22 vs 35,000.00 difference
  • Shows that even matching payment dates don’t guarantee matching cashflows!

The Code

// Setup: Swedish market curves
vector(number) years = [1,2,3,4,5,8];
vector(number) ois_rates = [2.0,2.100,2.364,2.38,2.514,2.88]/100;
vector(number) disc_factors = exp(-ois_rates .* years);
vector(number) fwd_rates = [2.04,2.28,2.44,2.6,2.75,3.07]/100;

disc_func df = disc_func_interp(years, disc_factors, ip_linear(), rate_type.RT_CONT);
fwd_func fwd = fwd_func_interp(years, fwd_rates, ip_linear());

// Bond definition - Swedish government bond conventions
INSTR_TMPL.bond_def_tmpl bond_swe_govt = new INSTR_TMPL.bond_def_tmpl(
    "SWE_BOND", 
    bond_yld_method.ISMA, 
    1,                      // Annual frequency
    DC_30E_360,             // 30E/360 day count
    false, 
    "BD4",                  // FOLLOWING business day convention
    first_cpn_type.REG, 
    last_per_yld_method.SIMPLE, 
    next_per_yld_method.EFFECTIVE,
    odd_last_cpn_type.REG,
    "BD2",
    CAL_SE,                 // Swedish calendar
    "SEK",
    quote_style.YIELD_PCT
);

// Create the bond
bond b = bond(
    instr_def_bond(bond_swe_govt), 
    "SE1053", 
    #2000-03-30,           // Issue date
    null, null, null, 
    #2039-03-30,           // Maturity
    0.035,                 // 3.5% coupon
    trade_d,               // Trade date = "today" (2026-02-10)
    null, null, null, null
);

// Swap definition - matching tenor and rate
INSTR_TMPL.swap_fixflt_def_tmpl fixflt_swe = new INSTR_TMPL.swap_fixflt_def_tmpl(
    "SWE_FIXFLT",
    "BD2", null<string>, null<string>, 
    1,                      // Annual fixed leg
    DC_30E_360,             // Same day count as bond
    interest_rule.IR_MATURITY_NOADJ,
    "SEK", CAL_SE, 
    BD_MOD_FOLLOWING,       // MODIFIED FOLLOWING - key difference from bond!
    false, 
    4, 4,                   // Quarterly floating leg
    DC_ACT_360,             // ACT/360 for floating
    interest_rule.IR_MATURITY_NOADJ,
    "SEK", CAL_SE, 
    BD_MOD_FOLLOWING,       // MODIFIED FOLLOWING on floating leg too
    false,
    null<flt_comp_avg_type>, 
    null<flt_sprd_comp_method>,
    null<flt_avg_method>,
    null<notional_exchg_style>,
    null<flt_stub_fwd_style>,
    INSTR_TMPL_IR_INDEX.stibor_3m
);

// Create swap with same maturity and coupon as bond
swap_fixflt swp = swap_fixflt_plain(
    fixflt_swe, 
    "Myswap", 
    trade_d,               // Same trade date
    null, null, 
    b.maturity(),          // Match bond maturity
    b.coupon_rate(),       // Match bond coupon (3.5%)
    -1,                    // Pay fixed (receive floating)
    1E6,                   // 1M SEK notional
    true, 
    df, fwd, null
);

// Extract cashflows
vector(date) b_calc_dates = b.cash_flow_dates(true, false);
vector(date) b_pay_dates = b.cash_flow_dates(true, true);
vector(number) b_flows = b.cash_flows(1E6, true);

vector(date) swp_dates;
vector(number) swp_flows;
swp.payment_data_fix_leg(true, false, swp_dates, cpn_rate, swp_flows, 
                         pv_cpn_cashflow, fee, pv_fee, notional);

Why This Matters

  1. Asset Swap Pricing: The spread adjustment needs to account for these cashflow mismatches
  2. Hedging: A naïve 1:1 hedge will have small cashflow timing/amount mismatches
  3. Risk Management: Your DV01 calculations differ slightly between bond and swap
  4. P&L Attribution: Unexplained P&L often comes from these convention differences

Practical Implications

When building an asset swap:

  • Don’t assume fixed leg = bond cashflows
  • Do align day count conventions where possible
  • Do track the adjustment spread that compensates for convention differences
  • Do test edge cases: leap years, business day adjustments, stub periods

The difference between theory (perfect cashflow match) and practice (convention-driven mismatches) is exactly where quantitative finance gets interesting!

test flows extended.qlw (29.7 KB)

1 Like