
Fixed Income Structured
- 387 installs
- 161 repo stars
- Updated July 18, 2026
- joellewis/finance_skills
fixed-income-structured is a finance skill that decodes structured fixed-income products such as ABS, MBS, and bespoke notes for developers and analysts modeling cash flows, explaining risks, or validating suitability in
About
fixed-income-structured is a finance analysis skill from joellewis/finance_skills for structured fixed-income products including ABS, MBS, and bespoke notes. The skill helps developers and analysts decode tranche structures, cash-flow waterfalls, prepayment and default risk, and suitability checks used in wealth management and institutional portfolios. Reach for fixed-income-structured when building pricing tools, risk dashboards, or compliance workflows that must explain structured note mechanics accurately. It targets financial modeling and validation logic rather than generic bond yield formulas for plain-vanilla government debt.
- Cash-flow waterfall logic
- Prepayment and extension risk
- Credit enhancement layers
- Rating and liquidity caveats
- Scenario stress framing
Fixed Income Structured by the numbers
- 387 all-time installs (skills.sh)
- +15 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #281 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/joellewis/finance_skills --skill fixed-income-structuredAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 387 |
|---|---|
| repo stars | ★ 161 |
| Last updated | July 18, 2026 |
| Repository | joellewis/finance_skills ↗ |
How do you model structured fixed-income cash flows?
Decode structured fixed-income products such as ABS, MBS, and bespoke notes when modeling cash flows, explaining risks, or validating suitability in wealth and institutional workflows.
Who is it for?
Developers and analysts building wealth or institutional tools who must decode ABS, MBS, and bespoke note structures and risks.
Skip if: Retail stock trading apps, crypto DeFi protocols, or teams handling only plain government or corporate bonds.
When should I use this skill?
A developer models cash flows, explains risks, or validates suitability for ABS, MBS, or bespoke structured fixed-income products.
What you get
Structured product breakdown with cash-flow model assumptions, risk factors, and suitability validation notes for ABS, MBS, or bespoke notes.
- cash-flow model assumptions
- risk breakdown
- suitability validation notes
Files
Fixed Income — Structured Products
Core Concepts
MBS Pass-Throughs
A pool of mortgages whose cash flows (principal, interest, prepayments) are passed through to investors on a pro-rata basis. Agency MBS (Ginnie Mae, Fannie Mae, Freddie Mac) carry a government or GSE guarantee against credit losses, isolating prepayment risk as the primary concern. Non-agency MBS lack this guarantee and carry both credit and prepayment risk.
Prepayment Risk
Borrowers can refinance when rates drop, returning principal early. This creates negative convexity — when rates fall, MBS prices rise less than comparable Treasuries because prepayments accelerate and shorten the bond's effective life. Prepayment risk has two faces:
Contraction risk: Rates fall, prepayments accelerate, duration shortens. Investors receive principal back when reinvestment rates are lower.
Extension risk: Rates rise, prepayments slow, duration extends. Investors are locked into below-market coupons for longer than expected.
PSA Prepayment Model
The Public Securities Association model provides a benchmark prepayment speed:
100% PSA = ramp from 0% CPR to 6% CPR linearly over the first 30 months, then constant at 6% CPR thereafter.
At 150% PSA, all speeds are multiplied by 1.5 (e.g., the plateau is 9% CPR). At 200% PSA, the plateau is 12% CPR.
CPR and SMM
CPR (Conditional Prepayment Rate): Annualized prepayment rate as a percentage of the remaining pool balance.
SMM (Single Monthly Mortality): Monthly prepayment rate.
SMM = 1 - (1 - CPR)^(1/12)
Weighted Average Life (WAL)
WAL = sum(t × Principal_t) / Total Principal. Unlike maturity, WAL accounts for the timing of principal repayments (both scheduled and prepayments). WAL is shorter than maturity for amortizing securities and is sensitive to prepayment assumptions.
CMO Tranches
Collateralized Mortgage Obligations redistribute MBS cash flows into tranches with different risk profiles:
Sequential pay: Principal flows to the first tranche until retired, then the second, etc. Earlier tranches have shorter duration, later tranches have longer duration.
PAC (Planned Amortization Class): Provides a predictable principal schedule within a band of prepayment speeds (e.g., 100-250% PSA). Stability comes at the expense of companion/support tranches that absorb prepayment variability.
Support/Companion tranches: Absorb excess or deficit prepayments to protect PAC tranches. Highly volatile duration.
ABS (Asset-Backed Securities)
Securitized pools of non-mortgage assets:
- Auto loans: amortizing, relatively predictable cash flows
- Credit cards: revolving, with a revolving period followed by a controlled amortization period
- Student loans: longer duration, income-driven repayment creates uncertainty
CLOs (Collateralized Loan Obligations)
Tranched portfolios of leveraged loans (typically 150-250 loans). AAA tranches benefit from significant subordination (30-40% of the structure below them). Equity tranches receive residual cash flows after all senior tranches are paid. Waterfall tests (overcollateralization and interest coverage tests) redirect cash flows to protect senior tranches when the portfolio deteriorates.
Waterfall Structure
Cash flows are distributed by seniority: senior tranches receive interest and principal first, mezzanine next, equity last. If pool performance deteriorates, lower tranches absorb losses first (subordination protects senior tranches). Overcollateralization (OC) tests and interest coverage (IC) tests trigger cash flow diversions when breached.
OAS for Structured Products
OAS is essential for MBS because it captures prepayment optionality. Standard modified duration is inappropriate for MBS — use effective duration (computed via OAS models) or empirical duration. Monte Carlo simulation of interest rate paths and corresponding prepayment responses is the standard valuation approach for MBS.
Key Formulas
| Formula | Expression | Use Case |
|---|---|---|
| SMM from CPR | SMM = 1 - (1-CPR)^(1/12) | Monthly prepayment rate |
| CPR from SMM | CPR = 1 - (1-SMM)^12 | Annualize monthly rate |
| PSA CPR (month t, t<=30) | CPR = 6% × (t/30) × PSA/100 | Ramping prepayment model |
| PSA CPR (month t, t>30) | CPR = 6% × PSA/100 | Plateau prepayment model |
| WAL | sum(t × Principal_t) / Total Principal | Average principal timing |
| OAS Price | P = E[sum CF_t(path) / (1+s_t+OAS)^t] | MBS valuation |
Worked Examples
Example 1: Convert PSA to CPR
Given: 150% PSA, month 20 Calculate: CPR and SMM in month 20 Solution: At 100% PSA, month 20: CPR = 6% × (20/30) = 4.0% At 150% PSA: CPR = 4.0% × 1.5 = 6.0% SMM = 1 - (1 - 0.06)^(1/12) = 1 - (0.94)^(0.0833) = 1 - 0.99486 = 0.00514 = 0.514%
In month 20 at 150% PSA, approximately 0.514% of the remaining pool balance prepays each month, equivalent to 6.0% annualized.
Example 2: CLO Tranche Analysis
Given: A CLO with $500M total assets. AAA tranche = $325M (65%), AA = $50M (10%), A = $37.5M (7.5%), BBB = $25M (5%), BB = $12.5M (2.5%), Equity = $50M (10%). Calculate: Subordination level for the AAA tranche Solution: Subordination below AAA = AA + A + BBB + BB + Equity = $50M + $37.5M + $25M + $12.5M + $50M = $175M Subordination % = $175M / $500M = 35%
The AAA tranche has 35% subordination — the portfolio would need to lose more than 35% of its value before AAA investors suffer any principal loss. This substantial credit enhancement is why CLO AAA tranches have historically experienced zero defaults.
Common Pitfalls
- Ignoring negative convexity of MBS — MBS underperform Treasuries in both rallies (contraction) and selloffs (extension)
- Using modified duration for MBS — use effective/OAS duration instead, as cash flows change with rates
- Assuming constant prepayment speeds — speeds vary with rates, seasonality, borrower demographics, and housing turnover
- Not understanding that waterfall mechanics affect tranche risk differently — senior and subordinate tranches of the same deal have very different risk profiles
Cross-References
- fixed-income-sovereign: the Treasury curve and duration/convexity concepts
- fixed-income-corporate: credit spread concepts applied to non-agency MBS and CLOs
- real-assets: real estate market fundamentals underlying MBS
- asset-allocation: structured products in multi-asset portfolios
Running the Script
uv run scripts/fixed_income_structured.py # run the demo (uses PEP 723 inline deps)
uv run scripts/fixed_income_structured.py --verify # check demo outputs against the worked examples (exit 1 on mismatch)
python3 scripts/fixed_income_structured.py # alternative (requires: pip install numpy)The demo prints the calculations covered above; its values match the worked examples in this skill. Run --help for a list of the classes and functions. For programmatic use, import the module rather than running it — the demo only executes under python fixed_income_structured.py.
# /// script
# dependencies = ["numpy"]
# requires-python = ">=3.11"
# ///
"""
Fixed Income — Structured Products
====================================
Weighted average life (WAL), CPR/SMM conversion, PSA prepayment model,
mortgage cash flow projection, and average life sensitivity to prepayment
speed.
Part of Layer 2 (Asset Classes) in the finance skills framework.
"""
import argparse
import sys
import numpy as np
class PrepaymentModel:
"""CPR/SMM conversion and PSA prepayment speed calculations.
Provides static methods for prepayment rate conversions and the
PSA benchmark prepayment model.
"""
@staticmethod
def smm_from_cpr(cpr: float) -> float:
"""Convert CPR (annual) to SMM (monthly).
Parameters
----------
cpr : float
Conditional Prepayment Rate as a decimal (e.g., 0.06 = 6%).
Returns
-------
float
SMM = 1 - (1 - CPR)^(1/12)
"""
return float(1.0 - (1.0 - cpr) ** (1.0 / 12.0))
@staticmethod
def cpr_from_smm(smm: float) -> float:
"""Convert SMM (monthly) to CPR (annual).
Parameters
----------
smm : float
Single Monthly Mortality rate as a decimal.
Returns
-------
float
CPR = 1 - (1 - SMM)^12
"""
return float(1.0 - (1.0 - smm) ** 12.0)
@staticmethod
def psa_cpr(month: int, psa_speed: float = 100.0) -> float:
"""Compute the CPR for a given month under the PSA model.
100% PSA ramps linearly from 0% CPR to 6% CPR over the first
30 months, then plateaus at 6% CPR. Other PSA speeds scale
proportionally.
Parameters
----------
month : int
Loan age in months (1-indexed).
psa_speed : float, optional
PSA speed as a percentage (e.g., 150 = 150% PSA). Default is 100.
Returns
-------
float
CPR for the given month as a decimal.
"""
if month <= 0:
raise ValueError("Month must be >= 1.")
psa_factor = psa_speed / 100.0
if month <= 30:
base_cpr = 0.06 * (month / 30.0)
else:
base_cpr = 0.06
return float(base_cpr * psa_factor)
@staticmethod
def psa_smm(month: int, psa_speed: float = 100.0) -> float:
"""Compute SMM for a given month under the PSA model.
Parameters
----------
month : int
Loan age in months.
psa_speed : float, optional
PSA speed as a percentage. Default is 100.
Returns
-------
float
SMM for the given month.
"""
cpr = PrepaymentModel.psa_cpr(month, psa_speed)
return PrepaymentModel.smm_from_cpr(cpr)
@staticmethod
def psa_cpr_schedule(
n_months: int,
psa_speed: float = 100.0,
) -> np.ndarray:
"""Generate a full CPR schedule under the PSA model.
Parameters
----------
n_months : int
Number of months in the schedule.
psa_speed : float, optional
PSA speed. Default is 100.
Returns
-------
np.ndarray
Array of CPR values for months 1 through n_months.
"""
months = np.arange(1, n_months + 1)
cprs = np.where(
months <= 30,
0.06 * (months / 30.0) * (psa_speed / 100.0),
0.06 * (psa_speed / 100.0),
)
return cprs
class MortgageCashFlow:
"""Project monthly cash flows for a mortgage pass-through security.
Parameters
----------
original_balance : float
Original pool balance.
mortgage_rate : float
Annual mortgage rate as a decimal (e.g., 0.06 = 6%).
original_term : int
Original loan term in months (e.g., 360 for 30-year).
current_age : int, optional
Current age of the pool in months. Default is 0 (new pool).
passthrough_rate : float or None, optional
Annual pass-through rate to investors. If None, uses mortgage_rate.
The difference is the servicing fee.
"""
def __init__(
self,
original_balance: float,
mortgage_rate: float,
original_term: int = 360,
current_age: int = 0,
passthrough_rate: float | None = None,
):
self.original_balance = original_balance
self.mortgage_rate = mortgage_rate
self.monthly_rate = mortgage_rate / 12.0
self.original_term = original_term
self.current_age = current_age
self.remaining_term = original_term - current_age
self.passthrough_rate = passthrough_rate if passthrough_rate is not None else mortgage_rate
self.monthly_passthrough = self.passthrough_rate / 12.0
def _scheduled_payment(self, balance: float, remaining_months: int) -> float:
"""Compute the scheduled monthly mortgage payment.
Parameters
----------
balance : float
Current outstanding balance.
remaining_months : int
Months remaining on the loan.
Returns
-------
float
Level monthly payment (principal + interest).
"""
r = self.monthly_rate
if r == 0:
return balance / remaining_months
return float(balance * r * (1.0 + r) ** remaining_months /
((1.0 + r) ** remaining_months - 1.0))
def project(self, psa_speed: float = 100.0) -> dict:
"""Project monthly cash flows under a given PSA speed.
Parameters
----------
psa_speed : float, optional
PSA prepayment speed. Default is 100.
Returns
-------
dict
Dictionary with arrays:
- 'month': month numbers
- 'beginning_balance': balance at start of month
- 'scheduled_payment': total scheduled payment
- 'interest': interest portion of scheduled payment
- 'scheduled_principal': scheduled principal repayment
- 'prepayment': prepayment amount
- 'total_principal': scheduled + prepaid principal
- 'total_cash_flow': interest + total principal (to investor)
- 'ending_balance': balance at end of month
- 'smm': single monthly mortality rate
"""
n = self.remaining_term
months = np.zeros(n, dtype=np.int64)
beg_balance = np.zeros(n)
sched_payment = np.zeros(n)
interest = np.zeros(n)
sched_principal = np.zeros(n)
prepayment = np.zeros(n)
total_principal = np.zeros(n)
total_cf = np.zeros(n)
end_balance = np.zeros(n)
smm_arr = np.zeros(n)
balance = self.original_balance
for i in range(n):
month_number = self.current_age + i + 1
months[i] = month_number
beg_balance[i] = balance
remaining = n - i
# Scheduled payment
pmt = self._scheduled_payment(balance, remaining)
sched_payment[i] = pmt
# Interest
int_payment = balance * self.monthly_rate
interest[i] = int_payment
# Scheduled principal
sp = pmt - int_payment
sched_principal[i] = sp
# Prepayment (on balance after scheduled principal)
smm = PrepaymentModel.psa_smm(month_number, psa_speed)
smm_arr[i] = smm
pp = (balance - sp) * smm
prepayment[i] = pp
# Totals
tp = sp + pp
total_principal[i] = tp
# Cash flow to investor uses passthrough rate for interest
investor_interest = balance * self.monthly_passthrough
total_cf[i] = investor_interest + tp
# Ending balance
balance = balance - tp
if balance < 0.01:
balance = 0.0
end_balance[i] = balance
if balance == 0.0:
# Truncate arrays
months = months[: i + 1]
beg_balance = beg_balance[: i + 1]
sched_payment = sched_payment[: i + 1]
interest = interest[: i + 1]
sched_principal = sched_principal[: i + 1]
prepayment = prepayment[: i + 1]
total_principal = total_principal[: i + 1]
total_cf = total_cf[: i + 1]
end_balance = end_balance[: i + 1]
smm_arr = smm_arr[: i + 1]
break
return {
"month": months,
"beginning_balance": beg_balance,
"scheduled_payment": sched_payment,
"interest": interest,
"scheduled_principal": sched_principal,
"prepayment": prepayment,
"total_principal": total_principal,
"total_cash_flow": total_cf,
"ending_balance": end_balance,
"smm": smm_arr,
}
class StructuredAnalytics:
"""Weighted average life and sensitivity analysis for structured products."""
@staticmethod
def weighted_average_life(
principal_payments: np.ndarray,
months: np.ndarray,
) -> float:
"""Compute weighted average life (WAL) in years.
Parameters
----------
principal_payments : np.ndarray
Array of total principal payments (scheduled + prepaid) per period.
months : np.ndarray
Array of month numbers corresponding to each payment.
Returns
-------
float
WAL = sum(t * Principal_t) / sum(Principal_t), converted to years.
"""
total_principal = np.sum(principal_payments)
if total_principal == 0:
return 0.0
# Convert months to years for the weighting
years = months / 12.0
return float(np.sum(years * principal_payments) / total_principal)
@staticmethod
def wal_sensitivity(
original_balance: float,
mortgage_rate: float,
original_term: int,
psa_speeds: np.ndarray,
current_age: int = 0,
) -> dict:
"""Compute WAL across a range of PSA speeds.
Parameters
----------
original_balance : float
Original pool balance.
mortgage_rate : float
Annual mortgage rate as a decimal.
original_term : int
Original loan term in months.
psa_speeds : np.ndarray
Array of PSA speeds to evaluate (e.g., [50, 100, 150, 200, 300]).
current_age : int, optional
Current pool age in months. Default is 0.
Returns
-------
dict
Dictionary with 'psa_speeds' and 'wal_years' arrays.
"""
speeds = np.asarray(psa_speeds, dtype=np.float64)
wals = np.zeros_like(speeds)
for i, speed in enumerate(speeds):
pool = MortgageCashFlow(
original_balance=original_balance,
mortgage_rate=mortgage_rate,
original_term=original_term,
current_age=current_age,
)
cf = pool.project(psa_speed=speed)
wals[i] = StructuredAnalytics.weighted_average_life(
cf["total_principal"], cf["month"]
)
return {
"psa_speeds": speeds.tolist(),
"wal_years": wals.tolist(),
}
def _demo() -> None:
# ----------------------------------------------------------------
# Demo: Structured products analysis
# ----------------------------------------------------------------
print("=" * 60)
print("Fixed Income Structured Products — Demo")
print("=" * 60)
# ----- CPR/SMM Conversion -----
print("\n--- CPR/SMM Conversion ---")
cpr = 0.06
smm = PrepaymentModel.smm_from_cpr(cpr)
cpr_back = PrepaymentModel.cpr_from_smm(smm)
print(f"\n CPR = {cpr*100:.2f}%")
print(f" SMM = {smm*100:.4f}%")
print(f" Round-trip CPR = {cpr_back*100:.2f}%")
# ----- PSA Model -----
print("\n--- PSA Prepayment Model ---")
print(f"\n {'Month':>6s} {'100% PSA':>10s} {'150% PSA':>10s} {'200% PSA':>10s}")
print(f" {'-'*6} {'-'*10} {'-'*10} {'-'*10}")
for month in [1, 5, 10, 15, 20, 25, 30, 36, 60, 120]:
cpr100 = PrepaymentModel.psa_cpr(month, 100)
cpr150 = PrepaymentModel.psa_cpr(month, 150)
cpr200 = PrepaymentModel.psa_cpr(month, 200)
print(f" {month:6d} {cpr100*100:9.2f}% {cpr150*100:9.2f}% {cpr200*100:9.2f}%")
# Verify worked example from SKILL.md: 150% PSA month 20
cpr_ex = PrepaymentModel.psa_cpr(20, 150)
smm_ex = PrepaymentModel.psa_smm(20, 150)
print(f"\n Worked example: 150% PSA, month 20")
print(f" CPR = {cpr_ex*100:.1f}%")
print(f" SMM = {smm_ex*100:.3f}%")
# ----- Mortgage Cash Flow Projection -----
print("\n--- Mortgage Cash Flow Projection ---")
pool = MortgageCashFlow(
original_balance=1_000_000.0,
mortgage_rate=0.06,
original_term=360,
passthrough_rate=0.055,
)
cf = pool.project(psa_speed=150)
# Show first 12 months and last 3 months
print(f"\n $1M Pool, 6.0% WAC, 5.5% pass-through, 150% PSA")
print(f" {'Month':>6s} {'Beg Bal':>12s} {'Interest':>10s} {'Sched Prin':>12s} {'Prepay':>10s} {'End Bal':>12s}")
print(f" {'-'*6} {'-'*12} {'-'*10} {'-'*12} {'-'*10} {'-'*12}")
for i in list(range(min(12, len(cf["month"])))) + list(range(max(0, len(cf["month"]) - 3), len(cf["month"]))):
if i == 12 and len(cf["month"]) > 15:
print(f" {'...':>6s} {'...':>12s} {'...':>10s} {'...':>12s} {'...':>10s} {'...':>12s}")
print(
f" {cf['month'][i]:6d}"
f" ${cf['beginning_balance'][i]:11,.2f}"
f" ${cf['interest'][i]:9,.2f}"
f" ${cf['scheduled_principal'][i]:11,.2f}"
f" ${cf['prepayment'][i]:9,.2f}"
f" ${cf['ending_balance'][i]:11,.2f}"
)
total_principal = np.sum(cf["total_principal"])
total_interest = np.sum(cf["interest"])
print(f"\n Total principal returned: ${total_principal:,.2f}")
print(f" Total interest paid: ${total_interest:,.2f}")
print(f" Pool life: {len(cf['month'])} months ({len(cf['month'])/12:.1f} years)")
# ----- Weighted Average Life -----
print("\n--- Weighted Average Life ---")
wal = StructuredAnalytics.weighted_average_life(
cf["total_principal"], cf["month"]
)
print(f"\n WAL at 150% PSA: {wal:.2f} years")
# ----- WAL Sensitivity -----
print("\n--- WAL Sensitivity to Prepayment Speed ---")
speeds = np.array([0, 50, 75, 100, 125, 150, 200, 250, 300, 400, 500])
sensitivity = StructuredAnalytics.wal_sensitivity(
original_balance=1_000_000.0,
mortgage_rate=0.06,
original_term=360,
psa_speeds=speeds,
)
print(f"\n {'PSA Speed':>10s} {'WAL (years)':>12s}")
print(f" {'-'*10} {'-'*12}")
for speed, w in zip(sensitivity["psa_speeds"], sensitivity["wal_years"]):
print(f" {speed:9.0f}% {w:11.2f}")
print("\n" + "=" * 60)
print("Demo complete.")
print("=" * 60)
def _check(failures: list, name: str, actual: float, expected: float, tol: float) -> None:
"""Record a verification check result."""
ok = abs(actual - expected) <= tol
status = "PASS" if ok else "FAIL"
print(f" [{status}] {name}: actual={actual:.6g}, expected={expected:.6g}, tol={tol:.2g}")
if not ok:
failures.append(name)
def _verify() -> None:
"""Verify key outputs against the SKILL.md worked examples."""
failures: list = []
# SKILL.md Example 1: 150% PSA, month 20
_check(failures, "Ex1 CPR (month 20, 150% PSA)", PrepaymentModel.psa_cpr(20, 150), 0.06, 1e-12)
_check(failures, "Ex1 SMM (month 20, 150% PSA)", PrepaymentModel.psa_smm(20, 150), 0.00514, 1e-5)
# CPR/SMM round trip
smm = PrepaymentModel.smm_from_cpr(0.06)
_check(failures, "CPR->SMM->CPR round trip", PrepaymentModel.cpr_from_smm(smm), 0.06, 1e-12)
# PSA plateau: 100% PSA after month 30 is 6% CPR
_check(failures, "100% PSA plateau", PrepaymentModel.psa_cpr(60, 100), 0.06, 1e-12)
# WAL sensitivity is monotonically decreasing in PSA speed
sens = StructuredAnalytics.wal_sensitivity(1_000_000.0, 0.06, 360,
np.array([50.0, 150.0, 300.0]))
wals = sens["wal_years"]
_check(failures, "WAL decreases with faster prepayment",
1.0 if wals[0] > wals[1] > wals[2] else 0.0, 1.0, 0)
if failures:
print(f"\n{len(failures)} check(s) FAILED: {', '.join(failures)}")
sys.exit(1)
print("\nAll checks passed.")
def main() -> None:
parser = argparse.ArgumentParser(
description=__doc__.strip().splitlines()[2] if __doc__ else "",
epilog=(
"Provides: PrepaymentModel, MortgageCashFlow, StructuredAnalytics. "
"For programmatic use, import this module (fixed_income_structured) instead of running it. "
"Bare run executes a demo whose printed values match the SKILL.md worked examples; "
"--verify asserts those values and exits nonzero on mismatch."
),
)
parser.add_argument(
"--verify",
action="store_true",
help="run the verification checks against the SKILL.md worked-example values",
)
args = parser.parse_args()
if args.verify:
_verify()
else:
_demo()
if __name__ == "__main__":
main()
Related skills
FAQ
What products does fixed-income-structured cover?
The fixed-income-structured skill decodes ABS, MBS, and bespoke structured notes for cash-flow modeling, risk explanation, and suitability validation in wealth management and institutional finance workflows.
Is fixed-income-structured for developers or analysts?
fixed-income-structured serves both developers building finance tools and analysts modeling structured products. The skill focuses on accurate mechanics and risk factors rather than generic plain-vanilla bond math.