v2.0.0 to v3.0.0

125 added, 24 removed. Audit A to A.

---
name: broker-margin-interest-accrual-tracking
- description: Institutional-grade margin interest and short borrow fee tracker. Uses
- progressive blended rate schedules (e.g. IBKR style), accounts for 360-day vs 365-day
- conventions, applies weekend T+1/T+2 compounding rules, and deducts cost of leverage
- and borrow from net P&L.
+ description: Use when accounting for the financing cost of leveraged or short positions —
+ margin loan interest on tiered blended rate schedules, short borrow fees on collateral
+ value, 360 vs 365 day-count conventions, calendar-day accrual across weekends and
+ holidays — and deducting that cost from gross P&L to get a net figure a backtest can be
+ trusted on.
domain: algorithmic-trading
subdomain: broker-integration
tags:
- broker-integration
- margin-interest
- accrual-tracking
- borrowing-cost
- pnl-accounting
- leverage-cost
- institutional
brokers_frameworks:
- Margin Cost Tracker
- Python PnL Accounting
- Interactive Brokers
- version: "2.0.0"
+ version: "3.0.0"
author: algo-trading-skills-contributors
license: Apache-2.0
---
## When to Use
- Invoke this skill when performing highly accurate institutional P&L accounting for algorithmic strategies that use leverage, hold overnight positions, or engage in short selling. Standard retail backtests ignore financing costs, leading to massive overestimations of Sharpe and net return—especially in elevated interest rate environments (5-8% APR). This skill calculates exact daily interest and short borrow accruals using institutional progressive tiered schedules and accurate day count conventions.
+ Invoke this skill when computing net P&L for a strategy that borrows — one that runs
+ leverage, carries positions overnight, or sells short. Backtests that ignore financing
+ overstate returns and Sharpe by the entire cost of the leverage that produced them, and
+ the overstatement scales with the benchmark rate: a book carrying a permanent debit at a
+ 5% margin rate gives up 5% a year before it makes a single trading decision.
+ Use it to answer three questions with numbers rather than estimates: what does the debit
+ balance cost under the broker's **tiered** schedule, what does the borrow cost on the
+ short book, and what does gross P&L become once both are subtracted.
+
+ ## When NOT to Use
+
+ - **As a substitute for the broker's own interest statement.** This computes an accrual
+ from balances you supply. Reconcile it against the monthly posting before trusting it;
+ a systematic gap usually means the collateral basis or the day-count is wrong.
+ - **To model multi-year compounding.** Brokers accrue daily and post monthly (IBKR on
+ the third business day of the following month), so interest starts earning interest
+ only once posted. This module sums simple daily charges over the window. Negligible
+ over weeks, material over years at high rates.
+ - **To get the net cost of a short.** It reports the **gross** borrow fee. The economic
+ cost is the fee less the rebate earned on short sale proceeds, which this does not
+ model — so its short-side number is an upper bound, and for general-collateral names a
+ loose one.
+ - **For intraday leverage.** Only end-of-day balances accrue overnight financing. A
+ position opened and closed inside the session contributes nothing here.
+ - **As a live rate source.** Broker tiers are quoted as a spread over a benchmark and
+ reprice with monetary policy. The bundled defaults are a dated illustration.
+
## Prerequisites
- - Broker margin interest schedule (blended progressive tiers).
- - Short stock hard-to-borrow (HTB) rates.
- - Day-count conventions (typically 360 days for US broker margin and borrow fees, 365 days for some international).
- - Accurate daily EOD cash and short market values.
+ - Your broker's **current** margin schedule. IBKR publishes tiers as benchmark + spread
+ (Fed Funds for USD), so absolute APRs drift — pull today's table, or build one with
+ `tiers_from_benchmark(benchmark_apr, spreads)`.
+ - The correct day-count for the financing currency: 360 for USD and most currencies at
+ IBKR, 365 for exceptions such as GBP.
+ - Per-security short borrow rates, which are re-struck daily and can move hundreds of
+ basis points overnight.
+ - End-of-day debit balance and gross short market value per date — not an average.
+ - The exchange holiday calendar, if you want the ledger's accrual blocks to line up with
+ settlement days.
## Workflow
- 1. **Configure Rate Schedules**: Set up the progressive tiers representing your broker's lending rates (e.g., 6.83% for first $100k, 6.33% for next $900k, etc.).
- 2. **Track Daily Debit Balance & Short Market Value**: Determine end-of-day balances subject to financing.
- 3. **Apply Blended Rate Calculation**: Calculate the effective APR for the total balance by filling each tier progressively.
- 4. **Compute Daily Cost with Weekend Logic**: Apply daily rates (using `rate / 360`). For positions held over Friday night, apply 3 days of interest (covering Saturday and Sunday).
- 5. **Adjust P&L**: Subtract total financing costs (margin interest + borrow fees) from Gross P&L to derive exact Adjusted Net P&L.
+ 1. **Configure the schedule, and let it reject bad input.** Build tiers with
+ `tiers_from_benchmark` or explicit `MarginRateTier` values. The constructor validates
+ that brackets start at zero, are contiguous, and that the **top tier is open-ended**.
+ That last check is not pedantry: a schedule capped at $100k silently prices everything
+ above the cap at 0%, so a $200k balance reports 2.5% instead of 5% — half the true
+ cost, with no error.
+ 2. **Feed dates, not day counts.** Call `accrue_daily_balances(balances, through_date)`
+ with one `EodBalance` per observation. It derives the day count from the dates, so it
+ cannot be handed a trading-day count by mistake. Use the scalar
+ `calculate_interest_accrual(start_date, holding_days, ...)` only for a constant
+ balance, and read `holding_days` as **calendar** days.
+
+ 3. **Know why weekends cost three days.** Not because settlement is slow — because the
+ balance still exists on Saturday and Sunday and interest is computed on the daily
+ balance. The consequence is the part people get wrong in both directions: the total
+ depends *only* on the number of calendar days in the window. Batching Sat/Sun into
+ Friday's ledger row changes granularity, never the total. Applying a weekend
+ multiplier on top of a calendar-day count double-charges; feeding a trading-day count
+ under-charges by roughly 2/7.
+
+ 4. **Register holidays for ledger alignment, not for extra cost.** `add_holidays()` makes
+ the Friday before a holiday Monday carry one four-day block rather than a three-day
+ block plus a row dated on a day the market was shut. The total is identical either
+ way; what changes is whether the ledger reconciles line-by-line against the broker's.
+
+ 5. **Charge the borrow fee on collateral, not market value.** IBKR computes it as
+ `value x rate / 360` where value is **102% of the prior day's settlement price,
+ rounded up to the next whole dollar**, times shares. Set `short_collateral_markup=1.02`
+ to approximate it, or pass an exactly computed `EodBalance.short_collateral_usd` to
+ match a statement. The default of 1.0 charges on raw market value and understates.
+
+ 6. **Subtract both from gross P&L.** `adjusted_net_pnl_usd` is gross less margin interest
+ less borrow fees. Report it alongside gross, never instead of it — the gap between the
+ two is the number that tells you whether the leverage was worth carrying.
+
> Full procedure: see `references/workflows.md`.
- > Standards reference: see `references/standards.md`.
+ > Sourced day-count, collateral and settlement conventions: see `references/standards.md`.
> Printable pre-flight checklist: see `assets/checklist.md`.
## Common Pitfalls
- - **Ignoring Blended Rates**: Assuming the lowest tier's rate applies to the entire balance, whereas brokers charge progressively.
- - **Wrong Day-Count**: Using 365 days instead of the industry standard 360 days for US dollar financing, understating costs by ~1.4%.
- - **Missing Weekend Compounding**: Forgetting that overnight margin held Friday incurs 3 days of interest.
- - **Conflating Borrow Fees with Margin**: Shorting a stock incurs a borrow fee (based on the short market value) AND if it causes a cash debit, incurs margin interest as well.
+ - **Counting trading days.** A position held from Monday's close to the following
+ Monday's close costs seven days of financing, not five. Over a year of continuous
+ carry this understates the cost by roughly 30% — about 2/7 for a weekday count, more
+ once holidays are dropped as well.
+ - **Double-counting the weekend.** The mirror-image error: taking a correct calendar-day
+ count and then multiplying Fridays by three. Weekend batching is presentation.
+ - **Assuming the lowest tier's rate applies to the whole balance.** Blended schedules
+ price each slice in its own bracket, so the effective rate falls as the balance grows —
+ and a flat-rate assumption misprices in whichever direction the schedule bends.
+ - **A tier schedule with a capped top bracket.** Anything above the cap is priced at
+ zero, and the shortfall grows with the size of the loan. This now raises
+ `RateScheduleError` rather than returning a plausible-looking number.
+ - **Using 365 where the broker uses 360.** It understates the charge by ~1.4% — small per
+ day, systematic forever, and it will never reconcile against a statement.
+ - **Charging borrow on market value.** Collateral is marked at 102% and rounded up, so
+ the fee is at least 2% higher than a market-value calculation suggests.
+ - **Netting a credit cash balance against borrow fees.** A credit balance earns interest
+ under a separate, tiered, threshold-gated schedule this module does not model; treating
+ it as negative margin interest invents income.
+ - **Letting NaN through.** A single NaN balance turns net P&L into NaN silently, and a
+ financing cost that defaults to zero is indistinguishable from an unlevered strategy —
+ it flatters every metric derived from it. Unusable input raises `FinancingDataError`.
+ - **Hard-coding last year's APRs.** Rates are benchmark-linked; a schedule copied from a
+ 2023 screenshot was roughly 170bp too high by 2026.
+ - **Conflating borrow fees with margin interest.** Shorting incurs a borrow fee on the
+ short market value *and*, if the account's cash goes into deficit, margin interest on
+ that deficit. They are separate charges with separate bases.
## Verification
- - Simulate a $150k margin balance across the weekend and verify the blended APR and 3-day accrual logic.
- - Run `python scripts/test_margin_interest.py` and confirm all tests pass 100%.
-
+ - Run the unit suite and confirm every test passes:
+ `python -m unittest discover -s skills/broker-margin-interest-accrual-tracking/scripts`
+ - Accrue a constant $100k debit at 5% from a Monday for 7 calendar days and confirm
+ $97.22 (`100000 * 0.05 / 360 * 7`), with the Friday row carrying `days_accrued == 3`.
+ - Run the same balance for 14 days starting on a Monday and on a Friday and confirm the
+ totals are identical — if they differ, weekend handling is double-counting.
+ - Register a holiday Monday and confirm the preceding Friday yields a single four-day
+ block and no accrual row is dated on the holiday.
+ - Construct a tracker whose top tier is finite and confirm `RateScheduleError`; pass NaN
+ as a balance and confirm `FinancingDataError`. A number coming back from either is a
+ fail-open bug.
+ - Feed a two-day schedule of $50k then $200k and confirm the effective APR is recomputed
+ per day (5% then 4.5%) rather than averaged.
+ - Reconcile a month of output against the broker's posted interest before relying on it.
## Related Skills
- Documentation for Related Skills.
+ - `short-selling-borrow-cost-and-availability-modeling`
+ - `broker-account-margin-call-handling`
+ - `multi-currency-pnl-and-fx-conversion`
+ - `backtesting-ml-models-against-transaction-costs`
+ - `execution-realistic-simulation`