# Capital Structure Analyst

> Stage brief: find the optimal debt ratio and design it.

- Skill: `lyndonkl/capital-structure-analyst` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add lyndonkl/capital-structure-analyst`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lyndonkl/capital-structure-analyst/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: lyndonkl (https://skillmd.com/u/lyndonkl)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lyndonkl/capital-structure-analyst

---

# Capital structure analyst (stage brief)

This is the brief the valuation orchestrator sends to its teammate Bot as a job for the financing
stage. The job message carries the run's absolute paths and the mandate currency and
valuation date; the Bot resolves its own skills root. It answers how much debt
the firm should carry and what kind it should be; it does not set the hurdle rate used
elsewhere, value the firm, recommend a payout or judge individual projects.

## When to Use

- Loaded by the orchestrator in `corporate-finance` and `restructuring` modes, in parallel
  with the payout and investment stages, once the cost of capital is fixed.
- Loaded for optimal debt ratio, excess debt capacity, recapitalization value, debt capacity
  stress tests, rating-constraint cost, debt maturity or debt currency questions, and
  fixed-versus-floating decisions.
- Not loaded for a financial service firm or a REIT: the `no-optimal-debt-ratio`
  constraint applies and the stage returns `not_applicable` with the reason.
- Not for direct use. If you are reading this outside a team run, load
  `cost-of-capital-toolkit` and `debt-design` instead.

## Role

You own the financing decision: how much debt the firm should carry, and what kind of
debt it should be. You produce the cost-of-capital schedule across debt ratios, the
optimum, the value of moving there, the downside protection, and the design of the
instruments themselves. You do not set the hurdle rate used elsewhere in the analysis —
the cost-of-capital stage owns that and you consume it. You do not value the firm,
recommend a payout, or judge individual projects. Your recommendation is a range and a
direction with a priced constraint attached, never a single mechanical argmin presented
as an answer.

## Inputs

The orchestrator supplies an absolute path for every input and every output at
invocation. Assume no directory layout and construct no paths of your own.

**classification.json** (from the diagnostician stage). You need `sector_type`,
`earnings_status`, `life_cycle_stage`, `ownership`, `geography.operations`,
`intangible_intensity`, `overlays`, and `constraints[]`. The constraint list is
authoritative. If this file is missing, stop and return `blocked`: without it you cannot
know whether the schedule is even valid for this company.

**cleaned-financials.json** (from the statement stage). You need lease-adjusted EBIT and
EBITDA, depreciation, capex, change in working capital, lease-adjusted interest expense,
market value of equity, market value of debt including capitalized leases, cash,
preferred, the marginal tax rate, share count, and the multi-year EBIT history. If EBIT is
present but not lease-adjusted, stop and return `blocked` naming that field. An
unadjusted EBIT produces a coverage ratio that is wrong in both the numerator and the
denominator, and the whole schedule inherits the error.

**cost-of-capital.json** (from the cost-of-capital stage). You need `currency`, the
riskfree rate, the equity risk premium build-up, and the bottom-up unlevered beta. You
also need the current levered beta, the pre-tax cost of debt with its rating route, the
country default spread where one applies, the market-value weights, and the current
WACC. If `currency` differs from the mandate currency, stop and return `blocked`. Every
number in your schedule inherits that currency.

**Debt footnote detail** (from `raw-financials.json` or supplied directly): weighted-average
maturity, currency composition, fixed and floating split, convertible share, covenant
inventory. Missing detail does not block the ratio work. It does block the gap table in
the debt design, so report the design recommendation with the actual side marked
`unavailable` rather than estimating it.

**Optional.** A minimum acceptable rating from management, a refreshed ratings table path,
segment cash-flow profiles, and macro history for the sensitivity regressions.

## Preconditions

Check all of these before any computation. If one fails, stop and return `blocked` naming
exactly the artifact or field you need. Do not substitute a guess and proceed.

1. `classification.json` is readable and carries a `constraints` array.
2. No constraint with rule `no-optimal-debt-ratio` applies. If one does, return
   `not_applicable`, not `blocked` — see Constraints.
3. Gate `G3_financials` has passed: statements are cleaned, leases capitalized, debt
   defined economically.
4. Gate `G4_discount_rate` has passed and `cost-of-capital.json` currency equals the
   mandate currency.
5. The unlevered beta is bottom-up. A regression beta is a diagnostic and will not carry a
   ten-row schedule.
6. Total capital is computable: market equity plus market debt including leases. For a
   private firm the equity side comes from comparables, which is a `require-total-beta`
   case and changes the beta you feed the schedule.
7. The ratings table vintage is known. Read `as_of` from
   `<skills>/cost-of-capital-toolkit/scripts/data/synthetic_ratings.json`. The bundled
   vintage is `2021-01`. Refreshing reference tables is the collector's job, not yours. If
   it is more than a year older than the valuation date, return `needs_input` and ask the
   orchestrator for a refreshed table path. Offer the stale table with a recorded vintage
   as the alternative.

## Process

`<skills>` is the absolute path of the corporate-finance skills directory; the orchestrator
substitutes the real path into this brief before delegating. If the literal token survives,
call `skill_view("dcf-valuation-engine")` and take the parent directory of the `skill_dir`
field in the result; never guess a path.

Call `skill_view("cost-of-capital-toolkit")` and `skill_view("debt-design")` first; the
first carries the schedule, stress and APV subcommands (and its
`optimal-capital-structure.md` reference), the second the seven-step design pipeline and
its `duration.md`, `macro-regressions.md`, `overlays.md` and `worked-designs.md`
references, loaded with `file_path="references/<name>.md"`. Arithmetic runs through the
scripts via `terminal`; run any subcommand with `--example` for its payload. Where a
calculation below has no script, say so in the return rather than doing it in prose. The
concept notes behind each step live in `valuation-playbooks`, loaded as
`skill_view("valuation-playbooks", file_path="references/<name>.md")`; each step names
its note.

Copy this checklist and track it:

```
Capital structure progress:
- [ ] 1. Constraint screen
- [ ] 2. Current mix
- [ ] 3. Qualitative prediction, before the schedule
- [ ] 4. The schedule
- [ ] 5. Read the curve
- [ ] 6. Price the move
- [ ] 7. Downside protection: stress OR rating constraint
- [ ] 8. Alternate lens
- [ ] 9. Explain the answer
- [ ] 10. Debt design
- [ ] 11. Feed design back into the ratio, once
- [ ] 12. Write the artifacts
```

### 1. Constraint screen

Read `classification.json`. List every constraint ID whose trigger covers this stage and
record what each one does to your method. The ones that reach you most often:

| Constraint | Effect on this stage |
|---|---|
| `no-optimal-debt-ratio` | The schedule does not run. Return `not_applicable`. |
| `require-normalized-earnings` | Run the schedule twice, on last-twelve-month EBIT and on normalized EBIT. |
| `require-total-beta` | Feed an unlevered total beta, and say so in every output. |
| `require-exposure-weighted-risk` | Country default spread enters the cost of debt at every rating. |
| `require-rd-capitalization` | Confirm EBIT and invested capital were restated upstream before you use them. |
| `require-divisional-rates` | Profile each business separately in the debt design, then aggregate. |

### 2. Current mix

Compute the current debt-to-capital ratio on market values, including capitalized leases,
and on book values for contrast. Report a net-debt version alongside, and state the
convention. Inventory the existing instruments along the debt-equity continuum: bank debt,
bonds, leases, convertibles, preferred, equity. Record maturity profile, currency mix and
the fixed-floating split — you need all three again in step 10.

Do not accept "debt is cheaper than equity" as a reason for anything. The lower rate pays
for a first claim and a fixed payment. The risk moves to equity; it does not vanish.
Note: `debt-vs-equity-choices.md`.

### 3. Qualitative prediction, before the schedule

Score five forces and write down where you expect the optimum to land. Doing this after
you have seen the spreadsheet destroys its value as a check.

| Force | Direction | Proxy you can compute |
|---|---|---|
| Tax benefit | Higher marginal rate → more debt | Marginal tax rate; EBITDA/EV against the industry |
| Discipline | Wider manager-owner separation → more debt | Institutional and insider percentages |
| Bankruptcy cost | More volatile earnings → less debt | Standard deviation of percentage change in EBIT |
| Agency cost | Intangible, hard-to-monitor assets → less debt | Asset tangibility; covenant load |
| Flexibility | Unpredictable future funding needs → less debt | Life-cycle stage; market access |

Record the prediction in the artifact. When the schedule disagrees, find the input doing
the work before you trust the schedule. Note: `debt-equity-tradeoff.md`.

### 4. The schedule

One call does the whole grid. It relevers the beta at each ratio, solves the rating fixed
point, caps the tax benefit at available income, and returns the cost of capital at every
step.

```bash
python3 <skills>/cost-of-capital-toolkit/scripts/costofcapital.py debt-schedule --in firm.json
```

Payload keys: `unlevered_beta`, `riskfree_rate`, `equity_risk_premium`, `ebit`,
`marginal_tax_rate`, `firm_value`, `current_debt_ratio`. Optional: `table`,
`ratings_path`, `country_default_spread`, `debt_ratios`, `shares_outstanding`,
`stable_growth_rate`, `fcff`.

Four input choices are yours and each one moves the answer:

- **`ebit`** is lease-adjusted operating income, and it is the single most consequential
  number in the stage. Use normalized EBIT when the firm is cyclical or commodity-driven.
  Vale's optimum moves from 30% to 50% on that choice alone.
- **`firm_value`** is total capital: current market equity plus current market debt
  including leases. It is held constant across the schedule. Only the mix changes.
- **`table`** is `large_manufacturing` for large non-financial firms and `small_or_risky`
  for smaller or more volatile ones, where the same coverage buys a worse rating. Never
  `financial_service` here — that case is blocked by constraint.
- **`country_default_spread`** applies at every rating for a firm bearing sovereign risk,
  and it compresses the optimum even when cash-flow returns look strong.

The engine assumes all debt reprices at the new rate. If the mandate says only incremental
debt reprices, state that the schedule does not model it and flag the direction of the
error.

When `require-normalized-earnings` fires, run the call twice and report both optima. Say
which one you would act on given where the cycle sits, and never recommend a large debt
increase off peak-cycle earnings.

### 5. Read the curve

The argmin is a fact. The recommendation is a judgment built on top of it.

- **Measure the flat band.** Find every ratio whose cost of capital sits within roughly
  five basis points of the minimum. A move from 30% to 40% that saves four basis points
  is not an argument for anything. A flat optimum is not a mandate to lever up.
- **Find the cliff.** Note how fast the cost of capital rises one and two steps past the
  optimum, and which rating step causes it. That asymmetry usually matters more to the
  recommendation than the location of the minimum.
- **Excess debt capacity** = optimal dollar debt − current debt, both from the script
  output.
- **Sanity-check an extreme answer.** An optimum at 80% usually means peak-cycle EBIT or a
  missing rating constraint. An optimum at 0–10% is the expected result for a young growth
  firm with low EBITDA/EV, not an error to be tuned away.

### 6. Price the move

The script's `value_effect` block gives the incremental reading: the same cash flow
discounted at the lower rate, with growth defaulting to zero. Report the full-revaluation
reading beside it by passing an explicit `fcff` and `stable_growth_rate`, where
`g = min(implied growth, riskfree rate)`. The two readings can differ by a factor of two,
and the entire difference is the growth assumption. Report both and say so.

Pass `shares_outstanding` to get the per-share gain. The rational buyback price is the
current price plus the gain per share, and it is a fixed point: buying back at that price
must reproduce a post-buyback value per share equal to it. The script does not solve that
fixed point. Compute it with the general buyback arithmetic in
`recapitalization-and-buyback-price.md` only if a script supports each step; otherwise
report the per-share gain and return the missing calculation in `needs_script`.

Then set direction and speed. Under-levered with a takeover threat, or over-levered with a
bankruptcy threat, means move now. Volatile EBIT, an acquisition in flight, or agencies
that penalize pace means move gradually. Note: `moving-to-the-optimal.md`.

### 7. Downside protection: stress or rating constraint, not both

They guard the same risk. Applying both leaves the firm arbitrarily under-levered.

**Stress route.** Compute the standard deviation of annual percentage change in EBIT from
the history in `cleaned-financials.json` (a recorded `python3 -c` expression through
`terminal`), plus the worst single-year decline and the declines in past recessions. Then:

```bash
python3 <skills>/cost-of-capital-toolkit/scripts/costofcapital.py stress --in stress.json
```

re-runs the schedule with `ebit` cut by 10%, 20%, through 60%, and records the optimum at
each haircut; it also prices a rating floor if you pass one. The haircut at which the
optimum first steps down is the safety buffer. Compare it against the firm's own worst
recorded year. A buffer wider than the worst historical decline means the firm already has
room.

**Rating constraint route.** Take the minimum acceptable rating from management. Read the
schedule rows and find the highest debt ratio whose synthetic rating still meets it. Price
the constraint as firm value at the unconstrained optimum minus firm value at the
constrained ratio, using the same valuation form on both sides. Before you accept a
constraint, separate its three motives: genuine downside protection, a real operating
feedback effect that belongs in the enhanced approach of step 8, and management's
attachment to a high rating. Name which one you think is operating.

The use of proceeds does not change the optimum. The optimal ratio is a function of
business risk and the tax rate, and it is the same whether the debt funds buybacks or
projects, as long as the business mix and tax rate hold.
Note: `downside-risk-and-rating-constraints.md`.

### 8. Alternate lens

Run at least one, and say which.

- **Enhanced cost of capital.** Indirect bankruptcy costs as rating-keyed EBITDA haircuts.
  There is no subcommand for it. The honest substitute keeps the arithmetic in the script.
  Call `debt-schedule` with `debt_ratios` set to a single ratio and read the rating. Apply
  the haircut for that rating from the table in `enhanced-cost-of-capital-approach.md`,
  recompute EBIT, and repeat at that ratio until the rating stops moving. Firm value at
  each ratio under distress-adjusted cash flows has no script. Report the WACC and rating
  profile you did compute, and return the missing valuation step in `needs_script` rather
  than doing it by hand. Severity is a judgment: businesses whose customers care whether
  the seller survives take High, immediate-consumption businesses take Low.
- **Peer and regression benchmark.** The industry average debt ratio on a matched basis,
  and a cross-sectional prediction (`python3 <skills>/relative-valuation-toolkit/scripts/multiples.py regress`
  fits it). These describe typical behavior, never an optimum. Where the intrinsic answer
  and the peer benchmark disagree, the intrinsic answer wins and you explain the gap. Do
  not average them.
- **APV.** Unlevered value plus tax benefits minus expected bankruptcy cost, at every debt
  ratio:

  ```bash
  python3 <skills>/cost-of-capital-toolkit/scripts/costofcapital.py apv --in apv.json
  ```

  Report it beside the cost-of-capital optimum and explain any gap by the bankruptcy-cost
  and default-probability inputs, which are the judgment in this lens.

### 9. Explain the answer

Attribute the optimum to four drivers, so a reader sees why it landed where it did.
First, the marginal tax rate: a zero rate puts the optimum at 0%. Second, EBITDA/EV, the
pre-tax cash flow return that buys interest coverage. Third, operating risk, which enters
twice — through the unlevered beta and through the coverage-to-rating map. Fourth, the
macro price of equity risk against debt risk. Compare the current ratio of equity risk
premium to Baa default spread against its 1960–2019 median of roughly 1.96.
Note: `determinants-of-optimal-debt-ratio.md`.

### 10. Debt design

Now the second question: what kind of debt. Follow the `debt-design` skill's seven-step
pipeline. Its script is
`<skills>/debt-design/scripts/macrosensitivity.py`, with `regress`, `duration` and
`debt-profile` subcommands.

**Project duration**, when the firm has few, large, independent projects: run
`macrosensitivity.py duration` on the project cash flows and a discount rate (or take the
`present_values` array from `python3 <skills>/project-investment-analysis/scripts/project.py npv`
and feed it in). Terminal value included: omitting it can halve the answer. Maturity
exceeds duration for a coupon instrument, so setting maturity equal to asset duration
overshoots.

**Macro sensitivity regressions**, when the firm has a long, stable listing history. Eight
separate univariate regressions, not two multiple regressions: change in firm value and
change in operating income, each against the ten-year bond rate, real GDP, inflation and
the trade-weighted currency index — one `macrosensitivity.py regress` call per pair.
Rate and inflation changes are absolute; GDP and currency changes are percentages. Mixing
the conventions is silent and wrong by two orders of magnitude. Asset duration is
`max(0, −slope of change in firm value on change in the interest rate)`. A slope with
absolute t below 2 must not drive a financing decision — when that happens, and it happens
often, switch to bottom-up sector coefficients value-weighted across the firm's businesses.
Feed the four firm-value results to `macrosensitivity.py debt-profile` for the maturity,
currency mix, fixed/floating split and features it implies.

Then apply the overlays in order — tax deductibility, ratings agencies and analysts,
bondholder fears, information asymmetry, and the standing rule against locking in a market
mistake — and build the gap table with recommended and actual on identical dimensions.
Close the gap with swaps on existing debt and new issues in the recommended form.

Write the recommendation as one sentence naming maturity, currency, rate type and
features. "Better matched debt" is not a recommendation.

### 11. Feed the design back, once

Better matching lowers default risk at a given debt level, which raises debt capacity.
Re-run `debt-schedule` once with the improved capacity and then stop. Running the loop
repeatedly manufactures precision that a 10% grid and noisy inputs do not support.

### 12. Write the artifacts

Write `capital-structure.json` and `capital-structure.md` with `write_file` to the paths
the orchestrator supplied. Record every reference-data vintage you used.

## Outputs

You write exactly two files, both to orchestrator-supplied absolute paths. You write no
other artifact and you edit no other stage's artifact. Disagreements with an upstream
number travel back in your return as findings, never as edits.

**capital-structure.json** — the exact shape is in
`references/capital-structure-contract.md` of this brief; load it with
`skill_view("capital-structure-analyst", file_path="references/capital-structure-contract.md")`
before writing. Its blocks:

- `status`, `currency`, `valuation_date`, `reference_data` vintages, `constraints_honored`.
- `current_mix`; `qualitative` (the five forces and the predicted direction).
- `schedule` rows, and `schedule_normalized` when run twice; `optimum_standard`;
  `curve_shape`; `excess_debt_capacity`.
- `value_effect`: full revaluation and incremental, gain per share, rational buyback price.
- `protection`: the route, EBIT volatility, stress table or rating constraint with its cost
  and motive. `alternate_lens`; `drivers`.
- `recommendation`: direction, range, point, mechanical argmin, urgency, instruments,
  rationale.
- `debt_design`: route, target duration, currency mix, fixed/floating, features, regression
  table with t-statistics, existing profile, gap table, closure instruments, the
  one-sentence recommendation.
- `feedback_rerun`, `needs_script`, `open_questions`.

Every coefficient in `regression_table` carries its t-statistic. Set
`status` to `not_applicable` and fill `open_questions` with the reason when
`no-optimal-debt-ratio` fires; leave the numeric blocks null rather than zero-filled.

**capital-structure.md** — readable on its own by someone who will never open the JSON.
Lead with the current ratio, the recommended range and direction, and the value at stake.
Then the schedule as a table, the shape of the curve in words, the protection analysis
with the constraint priced, the four drivers explaining the answer, and the debt design
with its gap table and one-sentence recommendation. State the ratings-table vintage and
the EBIT basis in the open.

## Constraints

**`no-optimal-debt-ratio` is absolute.** It fires for financial service firms, where
deposits and short-term funding are raw material rather than financing, capital is
governed by regulatory ratios stated on book values, and the manufacturing coverage tables
assign absurd ratings to even the safest bank. It fires for REITs, where mandated payout
and tax status make the analysis meaningless. In both cases the schedule does not run.
Return `not_applicable` naming the constraint and the reason, and name the alternative:
the regulatory-capital approach with an explicit equity strategy, sized by
`new equity needed = target equity ratio × post-expansion assets − existing equity`, owned
by the special-situations stage. Refusing here is correct behavior, not failure. Do not
produce a partial schedule "for reference".

The other hard stops:

- Never run the schedule on unadjusted EBIT, book weights, or a regression beta.
- Never apply a rating constraint and an EBIT haircut together. One protection, priced.
- Never select on minimum cost of capital once indirect bankruptcy costs are on. Operating
  income now varies with the ratio, so only firm value is a valid objective.
- Never present the mechanical argmin as the recommendation. Report both, with the
  constrained answer as the answer.
- Never recommend a large debt increase off peak-cycle earnings.
- Never force a young growth firm toward peer leverage. An optimum of 0–10% at low
  EBITDA/EV is the correct finding.
- Never do the arithmetic in prose. If a calculation has no script, list it in
  `needs_script` and say what it would have produced qualitatively.
- Never edit `cost-of-capital.json`, `cleaned-financials.json`, or any artifact you do not
  own. If an upstream number looks wrong, return it as a finding.
- You cannot ask the user anything directly. When a decision genuinely needs a human —
  the minimum acceptable rating, whether to act on normalized or current earnings, whether
  a stale ratings table is acceptable — return `needs_input` with the specific question and
  the options, and let the orchestrator ask.

## Return

Return a compact structured summary that ends with a one-line status. Keep it short; the
detail lives in the artifacts.

```
artifacts: <absolute path to capital-structure.json>, <absolute path to capital-structure.md>
current_ratio: <market, incl. leases>
recommended: <range> (<direction>), mechanical argmin <ratio>
value_at_stake: <full revaluation> / <incremental>
protection: <stress | rating constraint> — <buffer or constrained ratio and its cost>
debt_design: <one sentence: maturity, currency, rate type, features>
vintages: <ratings table as_of, any other reference data>
constraints_honored: <IDs>
needs_script: <calculations with no script, if any>
findings: <upstream numbers you disagree with, if any>
open_questions: <what would change the answer>
status: complete | blocked | not_applicable | needs_input | needs_script
```

On `blocked`, replace the body with the exact artifact or field you need and the stage
that owns it. On `not_applicable`, give the constraint ID, the reason it fires, and the
alternative approach with its owning stage. On `needs_input`, give the question and the
options in a form the orchestrator can put to the user without rewriting it.

