DocsBacktestsMargin and auto-decomposition
BacktestsGuide

Margin and auto-decomposition

Margin on a multi-leg position is not the sum of its legs. The engine reads the structures inside a position, nets each recognized structure to its grouped Reg-T requirement, and charges anything it cannot group leg by leg. This page covers how that decomposition works, what coverage means, the residual single-leg charges, and the second model, TIMS portfolio margin, which nets risk across a whole book instead of summing per-structure charges.

Updated Jul 20267 min read

Why decomposition exists

Reg-T strategy-based rules recognize spreads. A defined-risk structure nets to a single grouped requirement instead of the sum of what each leg would cost alone. That is why a four-leg iron condor is charged as one condor, not as four naked legs: the long wings cap the loss on the shorts, and the rule prices the capped loss.

To apply those rules, the engine has to find the structures. It auto-decomposes a position into recognized structures, charges each one by its Reg-T formula, and treats whatever is left as single legs. This computation runs in-house against the position's current marks. It is not a broker margin call or a third-party service, so the number is reproducible from the position and the config alone.

The recognized structures

The recognizer packs seven structures. When one set of legs could belong to more than one structure, it is claimed in priority order, from the tightest defined-risk shape down to the loosest:

Structure
Requirement (per lot, pre-buffer)
Box
max(0, −net premium)
Iron butterfly
max(0, wing width − max(0, net credit))
Iron condor
max(0, max wing width − max(0, net credit))
Long butterfly
max(0, net debit)
Vertical (credit)
max(0, width − current credit)
Vertical (debit)
max(0, current debit)
Calendar
max(0, net debit)
Diagonal
max(0, net debit)

Widths are strike differences scaled by the contract multiplier. Net credit, net debit, and net premium are read from the legs' current mid marks, never fill prices (see margin always marks at mid), so the requirement moves with the position: a credit vertical that has decayed toward zero credit approaches its full width charge, while a long debit structure's requirement tracks the debit still at risk to close. Every quantity here is a dollar figure at the contract multiplier.

Coverage and residuals

Decomposition coverage is how much of a position the recognizer packs into known structures. Anything it cannot group is a residual, charged with a single-leg formula:

Residual
Requirement
Naked short call
option value + max(risk charge, min charge)
Naked short put
option value + max(risk charge, min charge) (min charge is strike-based)
Long option
premium cost = mid × multiplier × | qty | × lots

For the two naked residuals, risk charge = risk_charge_pct × underlying value − out-of-the-money amount and min charge is a percentage floor: short_call_min_pct × underlying value for a call, short_put_min_pct × exercise (strike) value for a put. A long option's requirement is its premium, since its max loss is the debit paid.

Higher coverage means tighter margin. A leg that gets absorbed into a defined-risk group is charged the group's capped loss; the same leg left as a residual is charged its standalone naked or premium requirement, which is larger. The recognizer's job is to leave as little uncovered as possible.

How the packing is solved

The solve is a set-packing problem. Each leg explodes into unit slots, one per contract of size. Every candidate structure match across those slots is enumerated. The solver then selects a non-overlapping set of matches that minimizes total margin, so a slot is only claimed by a group when doing so lowers the requirement versus leaving it as a residual.

Below decomposition_max_candidates (default 1000), the solver is an exhaustive branch-and-bound: it finds the true minimum-margin packing. Above that threshold, it switches to a greedy fallback that takes the best per-slot saving at each step. The fallback is recorded per position, so the count of positions that used it is available for auditing a run.

Recognition runs at one lot and scales by lot count, since N lots of the same legs decompose into N identical copies. The buffer is applied last, once, to the scaled position total. The solve is deterministic: the same position, marks, and config always produce the same decomposition and the same number.

Margin config defaults

These parameters set the Reg-T charges. Every one carries a default; the two index-oriented adjustments are made by the caller, not auto-detected.

Parameter
Default
Effect
risk_charge_pct
0.20 (20%)
Percentage of underlying value charged on a naked short leg. Callers set 0.15 for broad-based indices (SPX, NDX, RUT, VIX).
short_call_min_pct
0.10 (10%)
Floor on the short-call risk charge, as a fraction of underlying value.
short_put_min_pct
0.10 (10%)
Floor on the short-put risk charge, as a fraction of exercise (strike) value.
buffer_pct
0.0
Multiplicative buffer on the position total after decomposition. At 0.0 the post-buffer requirement equals the pre-buffer requirement.
decomposition_max_candidates
1000
Candidate-count threshold. At or below it, the solver runs exhaustive branch-and-bound; above it, the greedy fallback.

Raising risk_charge_pct increases every naked-residual charge and tightens sizing that keys off position margin. Raising buffer_pct marks up the whole position total by that fraction. The solver stays deterministic at any setting.

Initial vs maintenance

Under the Reg-T strategy formulas, initial and maintenance margin are numerically identical at the position total: the formulas do not distinguish them, so both resolve to the same decomposed requirement. Both figures are surfaced separately. Under portfolio margin, a configurable initial uplift can separate the two; see what a TIMS number means.

Margin always marks at mid

Per-minute margin and net liquidation value marks use mid, regardless of the backtest's fill mode. The fill mode governs what you pay to open and close a trade; it does not touch what the account is marked at for margin. So a position filled across the spread still has its margin and NLV computed from mid marks. Fills and marks are separate; see fills, marks and slippage and backtest assumptions and realism.

What the Reg-T model does not do

Two limits define the strategy-based model.

There is no cross-position netting. Netting happens only within one position's own legs. The account requirement is the simple sum of each open position's requirement, so two offsetting positions on the same underlying do not net against each other. A long call in position A does not relieve a short call in position B.

There is no per-broker margin preset. One Reg-T model runs, with the config knobs above. There are no branded profiles that claim to reproduce a specific broker.

Recognizer-coverage completion is ongoing: more structures may be packed over time, which lowers residual charges but never changes the Reg-T formulas themselves. Cross-position netting and a second, user-selected margin model are what portfolio margin below provides.

Portfolio margin (TIMS): the second model

Alongside Reg-T, Backtest.ai offers a portfolio-margin model, selected in config. Where Reg-T sums local per-structure charges, portfolio margin is a risk simulation of the whole book.

All positions on one underlying form a class group. The underlying is shocked across a fixed range for that asset class (wider for single names and ETFs, tighter for broad-based indices). Every position in the group is repriced at each shock point, gains and losses net across the group, and the requirement is the worst modeled net loss across the shock grid, floored at a small per-contract minimum. The account requirement is the sum across groups.

Because losses and gains net economically, a hedged or delta-neutral book requires less than the sum of its legs and can collapse toward the floor. No structure catalogue is needed: the relief comes from the risk model, not from matching a named shape. This is customer portfolio margin under FINRA Rule 4210(g), computed by the TIMS methodology, a named regulatory approach rather than a vendor product.

What a TIMS number means, honestly

The modeled number is regulatory TIMS, computed from public parameters and our pricing inputs. It is a lower bound versus a real broker, because brokers layer proprietary house margin on top of the regulatory minimum. Present it that way: a lower bound with a configurable buffer, not a broker simulator.

The gap is small for diversified, hedged, liquid books and larger for concentrated or short-vol books, where house rules dominate. A conservative overlay preset exists to bracket that gap by widening the modeled severity. It is an approximation, not a match to any one broker's numbers.

Base TIMS produces one requirement, so initial and maintenance are the same figure by default; a configurable initial uplift is what separates them when a user wants broker-style initial margin. The model does not claim to reproduce a specific broker's shock range or a specific broker's requirement.

Selecting and sizing under portfolio margin

The margin model is chosen in config: reg_t is the default, tims selects portfolio margin. Overlays sit on top of whichever model is chosen, including the conservative bundle.

Sizing changes shape under portfolio margin. A candidate trade has no standalone requirement, because its cost depends on the whole book: the same spread can add margin, add nothing, or reduce it, depending on what it hedges. So entries and exits are both what-if-checked. Closing one leg of a hedge can raise the account requirement, because removing a hedge exposes the leg it was covering. That is why exits are checked, not entries alone. Sizing against a margin budget solves for the lot count the book can carry rather than dividing a budget by a fixed per-trade cost.

Was this page helpful?