docs: account risk and capital management primer - #419
Conversation
Add docs/account-risk.md: vocabulary, a layered risk model, market-type margin rules, and invariant tests for account-level risk. Each layer is mapped to what Trader implements today, what the Account Margin Admission milestone (#409, #410-#417) adds, and what remains future work. The primer records the real cause of the 4.6x SPY backtest: an unbounded fixed-fraction sizer, an empty risk engine in the backtest composition root, and a simulator with no margin policy. It follows the accepted ADRs: exact numerics (ADR-004), sizing kept separate from admission (ADR-006), strict approve/reject with no resizing (ADR-029), and live guards kept separate from trade risk. Closes #418 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The documentation contains multiple inaccuracies and ambiguous status or milestone statements that should be corrected before approval.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 6
Open (6)
Distinguish reference prices from aggregate exposure marks · New Clarify buying power versus maximum additional notional · New Scope ADR-034 exemption to position and exposure rules · New Do not claim all rules reject currency mismatches · New Make aggregate margin rates extensible per instrument · New Define invariant for over-limit fills and mark drift · New
What changed in this PR
Adds a Trader-specific account risk and capital management primer and roadmap.
Changes:
- Documents the sizing, risk, broker, and journal pipeline.
- Explains margin concepts, parameters, and milestone work.
- Defines proposed invariant tests and corrects prior architecture assumptions.
| File | Description |
|---|---|
docs/account-risk.md |
Adds the account risk primer and roadmap. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| |---|---|---| | ||
| | **Equity / NAV** | Cash + unrealized P&L of open positions. The base for all percentages. | `account.Snapshot.Equity()` | | ||
| | **Balance** | Cash only. Don't size off this when positions are open. | `Snapshot.CashBalances()` | | ||
| | **Notional (exposure)** | Units × price × multiplier, in account currency. | computed by rules from `Input.ReferencePrice` | |
| | **Gross exposure** | Σ \|notional\| across positions. Longs and shorts both add. | [#411] | | ||
| | **Net exposure** | Σ signed notional. Longs minus shorts. | [future] (`internal/portfolio` groups positions per instrument across accounts, without valuing them) | | ||
| | **Leverage** | Gross exposure ÷ equity. | per-position today; account-wide [#413] | | ||
| | **Buying power** | Maximum additional notional the account can open now. | `Snapshot.BuyingPower()` — placeholder in sim until [#412] | |
There was a problem hiding this comment.
Fixed in 1c70be6: buying power is now defined as a money amount of funds available, not a notional. How much notional it supports depends on the multiplier and margin policy.
| Every rule must also always admit a de-risking proposal, so an account that is | ||
| already over a limit is never trapped (ADR-034). |
There was a problem hiding this comment.
Fixed in 1c70be6: the exemption is now scoped to the position/exposure-limit rules ADR-034 covers, plus the planned initial-margin rule. It notes that stateful or operational rules may legitimately reject de-risking proposals.
| - For FX, per-unit risk must be in account currency (pip value). Today every | ||
| sizer and rule rejects a listing whose settlement currency differs from the | ||
| account currency rather than converting. |
There was a problem hiding this comment.
Confirmed: only the sizers, PerTradeLossRule, MaxInstrumentExposureRule, and MaxPositionLeverageRule check settlement currency. Fixed in 1c70be6 here and in section 6.
| prospective gross notional × initial_margin_ratio ≤ equity | ||
| ``` |
|
|
||
| | # | Invariant | Status | | ||
| |---|---|---| | ||
| | 1 | At ratio 1.0, no bar ends with gross notional above the admitted limit as a result of a fill | [#414] | |
There was a problem hiding this comment.
Fixed in 1c70be6: the invariant is now stated per exposure-increasing fill, against the equity used to admit it, with post-entry mark drift explicitly excluded (no maintenance margin in v1).
Refs #418 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rustyeddy
left a comment
There was a problem hiding this comment.
Reviewed PR #419 at head 53cb97c84834f60e4c6a7554bf58560dcee9dbf0.
Overall, this is a useful document and the framing is much better than the earlier draft: it correctly separates sizing, admission, broker enforcement, account state, and reporting, and it makes clear that ADRs remain authoritative.
I agree with most of Copilot's six comments, but they are mostly precision fixes rather than architectural blockers. I found one additional mismatch with the decisions we just made around #417.
1. Important: remove the implication that #417 will move Buy & Hold to full-notional sizing
The document currently says:
A capping sizer ... is an open question in #410 / #417
and later lists:
full-notional sizer headroom | TBD in #417
That no longer matches the intended Buy & Hold baseline.
We have now explicitly settled the Buy & Hold strategy contract as:
instrument
quantity
buy_date
sell_date # optional
The strategy is quantity-driven. It should not be rewritten around FullNotionalSizer or a capping sizer simply to fit the margin milestone.
For the baseline experiment, we choose a quantity that is financeable under the account policy:
quantity * execution price <= available buying power
with suitable headroom if needed for the expected fill mechanics.
Also, the backtest end date is not an implicit sell date: if sell_date is omitted, the final position remains open and Trader values it at the final mark.
Please update the primer so #417 is described as baseline/example sizing guidance, not planned adoption of full-notional sizing by Buy & Hold.
2. Copilot's reference-price vs aggregate-mark comment is valid
The vocabulary row:
Notional ... computed by rules from Input.ReferencePrice
is too broad once account-wide margin exists.
The proposal/resulting position may use the proposal reference/fill price, while other existing positions use current snapshot marks.
The primer should distinguish those two valuation inputs explicitly.
3. Copilot's BuyingPower wording is valid
Defining BuyingPower as:
Maximum additional notional the account can open now
is too strong/general.
It is better described as the account's available financing capacity according to the account/broker model. Translating that into additional units/notional depends on price, multiplier, and margin policy.
That also fits the existing full-notional sizer behavior better.
4. Scope the de-risking exemption
This sentence:
Every rule must also always admit a de-risking proposal
overstates ADR-034.
For the margin/exposure/position-limit family we are discussing, yes: reductions must not be trapped.
But that should not become a universal rule contract for all future risk/operational gates. I agree with narrowing the wording.
5. Narrow the cross-currency statement
Likewise, this:
Today every sizer and rule rejects a listing whose settlement currency differs...
is broader than the implementation.
The correct statement is closer to:
Sizers and monetary-valued rules that require account-currency valuation currently reject cross-currency inputs rather than converting them.
Quantity/count-only rules need not inspect currency.
6. Per-instrument margin extensibility: document it, but do not expand v1 scope
I partially agree with Copilot here.
The primer should say that the shared aggregate calculation must not make future per-instrument rates impossible. A later listing/instrument-specific margin policy should be able to provide each position's requirement without changing the aggregate model.
However, I would not use this documentation PR to strengthen #411 into requiring mixed per-instrument rates in v1. We already discussed keeping this milestone intentionally narrow:
one account
one account currency
one configured initial_margin_ratio
Design for the extension; don't implement it yet.
7. The invariant wording should explicitly allow post-fill market drift
I agree with Copilot's final comment.
The important invariant is not:
gross notional * ratio <= current equity on every bar
because v1 intentionally has no maintenance-margin liquidation.
It is:
No fill may leave the account above the admission limit as a result of that fill. A later breach caused solely by market/equity drift is allowed in v1; subsequent exposure-increasing fills remain blocked while de-risking remains allowed.
That wording will prevent #414 tests from accidentally becoming maintenance-margin tests.
8. One small sequencing/documentation adjustment
The sequencing section currently lists #414 before #415, which is fine numerically, but I would preserve the dependency nuance we agreed on:
#412
├─> #413 -> #414
└─> #415
#415 can proceed in parallel with #413/#414 once #410/#411/#412 are settled.
Not a blocker, just worth saying clearly.
Conclusion
I would make the documentation corrections above before merging, particularly the #417 / Buy & Hold sizing mismatch because that otherwise records a direction we have explicitly decided against.
After those edits, I think this primer will be a useful companion to ADR-066 rather than competing with it.
- Notional: distinguish the proposal's reference price from the current marks used for aggregate gross exposure. - Buying power: define it as funds available (money), not notional. - Limit the de-risking exemption to the position/exposure rules ADR-034 covers. - Correct the cross-currency claims: count/quantity rules do not check currency. - State that v1 applies the account ratio to each position, so a later per-instrument rate only changes that input. - Define the fill invariant against admission-time equity, and exclude post-entry mark drift. Refs #418 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rustyeddy
left a comment
There was a problem hiding this comment.
Final re-review of PR #419 at head 1c70be6621f5898e49d34f99d60442bac61fa2af.
Most of the previous review items are fixed:
- proposal reference price vs aggregate current marks is clarified;
- BuyingPower is correctly described as a money amount rather than notional;
- the de-risking exemption is scoped appropriately;
- the cross-currency wording is now accurate;
- post-entry mark drift is explicitly excluded from the v1 margin invariant;
- sequencing now notes that #415 can proceed in parallel after #412.
There is one remaining mismatch from my prior review:
#417 is still described as full-notional/capping-sizer work
The document still says:
A capping sizer ... whether to add one is an open question in #410 / #417.
and the planned-parameters table still includes:
full-notional sizer headroom | TBD in #417
That no longer matches the Buy & Hold baseline contract we explicitly settled.
Buy & Hold should remain:
instrument
quantity
buy_date
sell_date # optional
The strategy is quantity-driven. #417 should cover choosing/documenting a financeable baseline quantity and margin/sizing guidance, not changing Buy & Hold to use FullNotionalSizer or adding sizer headroom for it.
Please remove the #417 linkage from the capping-sizer paragraph and replace the planned-parameter row with wording consistent with the actual baseline contract, for example:
Buy & Hold baseline quantity | explicit config | choose a quantity that fits available buying power with suitable fill headroom
or simply omit that row and leave the detailed guidance to #417.
Once that is corrected, I do not see another substantive blocker in this documentation PR.
Current state: not quite merge-ready; expected merge-ready after this final documentation correction.
- Take #417 out of the capping/full-notional sizer wording. Buy & Hold takes an explicit quantity; #417 is guidance on choosing a quantity the account can finance. - Say that per-instrument margin rates are designed for but not implemented in v1. - Invariant: while over the limit because of price drift, further exposure increases stay blocked and de-risking stays allowed. - Show the #410-#417 dependency graph explicitly. Refs #418 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Addressed the remaining item from the re-review in 17af7c9:
Issue bodies updated to match:
|
rustyeddy
left a comment
There was a problem hiding this comment.
Final review of PR #419 at head 17af7c9a58814398c724ea4e68f6ca353f1cc001.
The last remaining issue from my prior review is now corrected.
The primer now explicitly states that Buy & Hold remains quantity-driven:
instrument
quantity
buy_date
sell_date # optional
and correctly separates responsibilities:
- strategy chooses quantity and timing;
- margin/risk decides whether that quantity is admissible;
- simulator determines the actual fill;
- reporting values the result;
- run end is not an implicit sell, so an open position remains open and is valued at the final mark.
It also now makes clear that a capping sizer is future work and is not part of this milestone or #417.
I re-checked the other previously raised items as well:
- proposal reference price vs existing-position marks is clear;
- BuyingPower wording is corrected;
- de-risking exemption is scoped correctly;
- cross-currency wording matches current implementation;
- per-instrument margin extensibility is documented without expanding v1 scope;
- the invariant allows post-entry mark/equity drift and only prohibits fills that themselves create an over-limit state;
- sequencing notes #415 can proceed in parallel once the shared foundation is in place.
I do not see another substantive issue in the current documentation.
PR #419 is merge-ready from my review.
GitHub currently shows no combined-status entries for this head, so this assessment is based on the PR contents and review state rather than an external CI status signal.

What changed
Adds
docs/account-risk.md, a primer and roadmap for account-level risk:Every item is marked as implemented [today], planned in the Account Margin Admission milestone ([#410]–[#417]), or [future].
Why
The draft of this document was written against a different codebase:
RunPortfolio,portfolio_runner.go, andAccountManager, none of which exist here.This version keeps the useful general material and corrects everything specific to Trader. The real cause of the 4.6x run: an unbounded fixed-fraction sizer,
risk.NewEngine()composed with no rules incmd/trader/backtest, and a simulator with no margin policy.How it was tested
Documentation only. I checked every referenced type, rule, file, and ADR against the current tree.
Documentation changed
docs/account-risk.md(new)Closes #418
🤖 Generated with Claude Code