# Bond vs Swap Cashflows

**URL:** <https://quantlab.discourse.group/t/bond-vs-swap-cashflows/122>\
**Category:** Qlang Financial Libraries\
**Created:** [February 10, 2026, 7:06am UTC](https://quantlab.discourse.group/t/bond-vs-swap-cashflows/122 "2026-02-10T07:06:32Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![QuantBob](https://yyz1.discourse-cdn.com/flex003/user_avatar/quantlab.discourse.group/quantbob/32/9_2.png) [@QuantBob](https://quantlab.discourse.group/u/QuantBob)\
**Post date:** [February 10, 2026, 7:06am UTC](https://quantlab.discourse.group/t/bond-vs-swap-cashflows/122/1 "2026-02-10T07:06:32Z")

</div>

# 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

```cpp
// 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](https://quantlab.discourse.group/uploads/short-url/2edXCnXDg4DMG3v0CHpUk9sThon.qlw) (29.7 KB)
