← Tax Agent

Every scripted case

All 301 cases that run on every build, grouped by what the client is trying to achieve. 71 run live over JSON-RPC against the MCP servers, 98 pin figures in the rule packs, and 132 are optimizer invariants recomputed from the snapshot.

Nothing executes on this page. It is a listing: every case links to the file that defines it. To run them, clone the repository and start the console — python3 scripts/serve_dashboard.py — where the 71 console cases spawn a real server and speak the protocol.

Harvest losses (38) · Raise cash (23) · Cap realized gains (29) · Rebalance (10) · Coordinate a household (14) · Municipal bonds (26) · Know what a gain costs (31) · Business owner and entity (25) · Transfer wealth (18) · Fix a filing problem (16) · Expat and cross-border (14) · What is deductible (9) · Trust the data (12) · Entitlement, scope and replay (6) · Conservation, restrictions and determinism (30)

Harvest losses 38

Tax-managed equity above all, then any taxable account in a down market. Very common, and the most mishandled: the rule reaches across accounts, across spouses, and into retirement plans where the loss is destroyed rather than deferred.

CaseWhat it pinsSuiteDefined in
portfolio-lotsWhat is held and what it carries. Proposes nothing: this is the tool for 'where do we stand', not 'what should we do'.runs liveuse_cases.py
portfolio-harvestA proposal, the losses it realises, and the lots it held back — one for wash-sale risk, one for unknown basis.runs liveuse_cases.py
portfolio-planned-purchaseScreening a planned buy before it happens turns a disallowed loss into a decision the advisor still gets to make.runs liveuse_cases.py
holding-period-anniversary-is-short-termThe period begins the day after acquisition, so a sale on the one-year anniversary is short-term. Bought 2025-09-30, sold 2026-09-30. ruleinvestment_rules.yaml
holding-period-one-day-later-is-long-termOne day past the anniversary crosses into long-term.ruleinvestment_rules.yaml
wash-sale-window-includes-day-thirtyA purchase exactly 30 days after the sale is inside the window; the loss on the matched shares is disallowed. ruleinvestment_rules.yaml
wash-sale-window-excludes-day-thirty-oneOne day outside the 61-day window and the loss stands in full.ruleinvestment_rules.yaml
wash-sale-partial-quantity-allocates-pro-rata40 replacement shares against a 100-share loss disallow 40%.ruleinvestment_rules.yaml
replacement-in-a-roth-permanently-disallowsRev. Rul. 2008-5: a replacement bought in an IRA or Roth kills the loss outright, with no basis adjustment available anywhere. ruleinvestment_rules.yaml
replacement-in-a-taxable-account-adjusts-basisThe same facts in a taxable account defer the loss instead.ruleinvestment_rules.yaml
different-ticker-is-not-matched-and-not-clearedA different identifier is never matched, and the screen says the equivalence question was not decided rather than implying safety. ruleinvestment_rules.yaml
dividend-reinvestment-is-a-replacement-purchaseA reinvested dividend is an acquisition and can wash a loss.ruleinvestment_rules.yaml
select-prefers-losses-before-gainsWith a tight gross budget, only losses can be taken.conformanceselection.yaml
select-takes-the-only-eligible-lotWhen three lots are unusable the fourth is the only possible trade.conformanceselection.yaml
select-extra-trades-past-the-target-are-lossesHarvesting may continue past the cash target, because taking a loss is itself the objective. What it may not do is realize a gain nobody needed: any trade beyond the target must consume no budget. conformanceselection.yaml
select-no-gain-realized-when-losses-sufficeCash available from losses should not be raised by realizing a gain.conformanceselection.yaml
select-all-lots-when-target-needs-themA target equal to the whole portfolio requires every eligible lot.conformanceselection.yaml
select-restricted-lot-forces-the-alternativeRestricting the larger loss forces the smaller one to be used.conformanceselection.yaml
select-washed-lot-forces-the-alternativeA washed lot is withheld, so the other loss lot carries the target.conformanceselection.yaml
select-nothing-to-do-is-a-valid-answerA zero target with gains only means no trade is the right answer.conformanceselection.yaml
select-harvest-without-a-cash-targetWith no cash objective the losses are still worth harvesting.conformanceselection.yaml
select-gain-lot-only-when-requiredA target beyond the losses requires reaching into the gains.conformanceselection.yaml
wash-existing-replacement-blocks-harvestA repurchase already inside the window makes that loss lot unharvestable.conformancewash_sale.yaml
wash-other-loss-lot-remains-availableOnly the washed lot is withheld; the clean one is still sold.conformancewash_sale.yaml
wash-reason-is-reportedA withheld harvest is reported, not silently omitted.conformancewash_sale.yaml
wash-roth-replacement-blocks-harvestA replacement in a Roth would permanently disallow the loss, so it is withheld.conformancewash_sale.yaml
wash-planned-purchase-blocks-harvestA buy a manager intends but has not placed is invisible to history and must still block.conformancewash_sale.yaml
wash-planned-purchase-names-the-conflictA cross-unit conflict is labelled as such, not as ordinary wash-sale risk.conformancewash_sale.yaml
wash-planned-purchase-outside-windowA planned buy beyond 30 days after the sale does not block the harvest.conformancewash_sale.yaml
wash-planned-purchase-of-another-securityA planned buy of a different identifier is not a replacement.conformancewash_sale.yaml
wash-does-not-block-gain-lotsWash-sale rules concern losses; a gain lot is unaffected by a repurchase.conformancewash_sale.yaml
wash-blocked-lot-never-appears-in-tradesWhatever the cash pressure, a washed lot must not be sold.conformancewash_sale.yaml
wash-withholding-can-make-a-target-infeasibleWithholding a washed lot may put the target out of reach, which is reported honestly.conformancewash_sale.yaml
wash-clean-portfolio-harvests-freelyWith no replacement anywhere, both loss lots are available.conformancewash_sale.yaml
wash-multiple-planned-purchasesSeveral planned buys each block their own security.conformancewash_sale.yaml
wash-partial-planned-quantity-still-blocksA planned buy smaller than the lot still matches part of it and withholds the harvest.conformancewash_sale.yaml
wash-traditional-ira-replacement-blocks-harvestRev. Rul. 2008-5 applies to a traditional IRA exactly as to a Roth: the loss is permanently disallowed with no basis adjustment, so the lot is never harvested.conformancewash_sale.yaml
wash-qualified-plan-replacement-blocks-harvestThe same permanent disallowance through an employer plan. All three retirement registrations are treated alike by the rule pack, and now all three are exercised.conformancewash_sale.yaml

Raise cash 23

Every segment. The objective is proceeds by a date, with the gain budget as the constraint rather than the goal.

CaseWhat it pinsSuiteDefined in
portfolio-infeasibleThe shortfall is reported. The constraint is never quietly relaxed to manufacture a result that looks like success.runs liveuse_cases.py
cash-met-exactly-from-lossesLosses alone can satisfy a target without touching the budget.conformancecash_target.yaml
cash-requires-a-gainWhen losses are insufficient, reaching the target means realizing a gain.conformancecash_target.yaml
cash-unreachable-is-infeasibleA target beyond the whole portfolio is infeasible, not partially served as success.conformancecash_target.yaml
cash-unreachable-within-budgetCash exists in the portfolio but only behind gains the budget forbids.conformancecash_target.yaml
cash-shortfall-is-reportedAn infeasible run says how far short it fell.conformancecash_target.yaml
cash-infeasibility-gives-a-reasonThe diagnostics must say why, not merely that it failed.conformancecash_target.yaml
cash-zero-needs-no-tradeA zero target must not provoke a gain nobody asked for.conformancecash_target.yaml
cash-small-target-from-one-lotA small target should not require liquidating the whole portfolio.conformancecash_target.yaml
cash-whole-portfolio-exactlyA target equal to the entire portfolio value is reachable.conformancecash_target.yaml
cash-one-cent-beyond-the-portfolioA cent more than everything is infeasible.conformancecash_target.yaml
cash-never-relaxes-the-budget-to-succeedAn adapter may not breach a hard budget to report success.conformancecash_target.yaml
cash-withdrawal-account-namedA named withdrawal account does not change conservation or the budget.conformancecash_target.yaml
infeasible-reports-statusAn unreachable target yields the infeasible status, not a partial success.conformanceinfeasibility.yaml
infeasible-still-conservesEven when failing, the partial trade list must remain valid.conformanceinfeasibility.yaml
infeasible-does-not-breach-the-budgetFalling short is preferable to exceeding a hard budget.conformanceinfeasibility.yaml
infeasible-does-not-breach-restrictionsA restriction is not relaxed to close a shortfall.conformanceinfeasibility.yaml
infeasible-reports-the-shortfallThe gap between target and achievable cash is quantified.conformanceinfeasibility.yaml
infeasible-explains-itselfA reason accompanies the status so a person can act on it.conformanceinfeasibility.yaml
infeasible-empty-portfolioNo holdings and a positive target is infeasible, not an exception.conformanceinfeasibility.yaml
infeasible-everything-restrictedA fully restricted portfolio cannot raise a cent.conformanceinfeasibility.yaml
infeasible-zero-budget-blocks-the-only-cashWhen all cash sits behind gains a zero budget makes the target unreachable.conformanceinfeasibility.yaml
infeasible-feasible-when-target-loweredThe same portfolio succeeds once the target is within reach, proving the refusal was about the constraint.conformanceinfeasibility.yaml

Cap realized gains 29

UHNW and HNW accounts under a signed gain budget. Twelve of these are the optimality gate: the difference between a plan that works and a plan that is provably best.

CaseWhat it pinsSuiteDefined in
portfolio-missing-netting'Keep gains under $15,000' reads two ways and they give different answers, so the schema requires the caller to say which.runs liveuse_cases.py
budget-gross-blocks-the-gain-lotUnder gross netting a 5,000 gain cannot fit a 1,000 budget, however much loss is harvested.conformancegain_budget.yaml
budget-net-allows-offsetUnder net netting the harvested loss creates room for the gain.conformancegain_budget.yaml
budget-zero-gross-permits-losses-onlyA zero gross budget still allows loss harvesting, which consumes none of it.conformancegain_budget.yaml
budget-zero-net-permits-offsettingA zero net budget permits a gain exactly offset by a loss.conformancegain_budget.yaml
budget-external-realized-reduces-roomGains already realized elsewhere consume the same allowance.conformancegain_budget.yaml
budget-external-exhausts-itWhen prior gains have used the whole budget, no further gain fits.conformancegain_budget.yaml
budget-exactly-met-is-within-itA gain equal to the budget is within it; the limit is not strict.conformancegain_budget.yaml
budget-one-cent-shortA budget a cent below the gain must exclude that lot.conformancegain_budget.yaml
budget-small-gain-fitsThe smaller gain lot fits a budget the larger one cannot.conformancegain_budget.yaml
budget-generous-allows-everythingA budget larger than every gain imposes no restriction.conformancegain_budget.yaml
budget-losses-do-not-consume-grossHarvesting losses under gross netting leaves the budget untouched.conformancegain_budget.yaml
budget-net-can-go-negativeA net figure below zero is a harvested loss, not a breach.conformancegain_budget.yaml
budget-soft-limit-is-still-reportedA budget marked soft may be exceeded, but the figures must still recompute.conformancegain_budget.yaml
budget-with-no-cash-targetA budget alone still binds when there is no cash objective.conformancegain_budget.yaml
budget-efficiency-mattersTwo lots raise cash at very different budget cost per dollar.conformancegain_budget.yaml
budget-period-is-recordedA budget carries the period it applies to, which the run must respect.conformancegain_budget.yaml
opt-spends-budget-on-the-better-lotA 5,000 gross budget buys 15,000 of cash from the large gain lot or 5,000 from the small one. Greedy ordering takes the small lot first and then cannot afford the large one, missing the target entirely. conformanceoptimality.yaml
opt-chooses-cheaper-cash-per-budget-dollarlot_cheap yields 15,000 of cash for 1,000 of gain; lot_dear yields 5,000 for 4,000. A 1,000 budget should buy the cheap lot rather than fail. conformanceoptimality.yaml
opt-no-dominated-tradeWhere one lot is better on both cash raised and budget consumed, the dominated lot must never be preferred. conformanceoptimality.yaml
opt-maximises-cash-within-the-budgetWith a 1,000 gross budget the best reachable cash is both loss lots plus the small gain lot: 14,200 + 5,000 = 19,200. conformanceoptimality.yaml
opt-uses-net-offsetting-to-reach-furtherUnder net netting the 6,800 of harvested losses offset the gains, so the whole portfolio is reachable within a zero net budget. conformanceoptimality.yaml
opt-declines-an-unnecessary-gainWhere losses alone reach the target, an optimizer realizes no gain at all even though the budget would have allowed one. conformanceoptimality.yaml
opt-minimum-trades-for-the-targetA target reachable from a single lot should not be met by liquidating two, where transaction count forms part of the objective. conformanceoptimality.yaml
opt-respects-restrictions-while-optimisingOptimality never licenses breaking a restriction in order to find a better answer; the restricted lot stays untouched. conformanceoptimality.yaml
opt-reports-optimal-statusAn engine that proves optimality reports it, and must set the optimality_claimed diagnostic to match the status it gives. conformanceoptimality.yaml
opt-reports-a-gap-when-it-stops-earlyAn optimizer halted by a time limit reports a bound and a gap rather than claiming an optimum it never proved. conformanceoptimality.yaml
opt-infeasible-is-proven-not-assumedAn optimizer declaring infeasibility should identify the conflicting constraints rather than merely failing to find an answer. conformanceoptimality.yaml
opt-household-joint-beats-sequentialA joint household optimizer serves both accounts from one budget with no ordering at all, which is the entire reason to build one. conformanceoptimality.yaml

Rebalance 10

Tax-managed equity and any model-driven book. Exact per overweight security, and it never sells a security below its target.

CaseWhat it pinsSuiteDefined in
portfolio-rebalanceCloses drift within a gain budget, never selling a security below its target. With chunky lots it under-trades rather than overshooting, and reports the residual.runs liveuse_cases.py
portfolio-rebalance-needs-targetsRefused rather than inferred. Deriving a target from the current holdings would make the portfolio its own benchmark.runs liveuse_cases.py
rebal-closes-drift-toward-targetAAA is far above its 50% target and CCC is absent. Selling the affordable AAA lot and buying the shortfall moves the portfolio toward target. conformancerebalance.yaml
rebal-never-sells-past-targetEven with an unlimited budget, no security is sold below its target weight. Overshooting would close drift on one side and open it on the other. conformancerebalance.yaml
rebal-respects-the-gain-budgetA tight gross budget limits which overweight lots can be sold, so drift closes only partly. The budget is never breached to reach the target. conformancerebalance.yaml
rebal-zero-budget-still-trades-what-costs-nothingA zero gross budget blocks gain lots but not loss or break-even lots, which consume no gross capacity. conformancerebalance.yaml
rebal-tolerance-suppresses-small-tradesA wide tolerance band leaves every drift inside it, so nothing is traded and the portfolio is left alone. conformancerebalance.yaml
rebal-conserves-positionsNo lot may be sold in a quantity it does not hold, and no lot may appear twice, whatever the objective. conformancerebalance.yaml
rebal-trims-a-combination-that-would-overshootTotal value is 18,200, so a 50% AAA target leaves AAA 2,900 overweight. Either 6,000 lot alone already exceeds that, so the engine sells neither rather than overshoot. This is the only portfolio shape that exercises the trim, and without it a deliberately broken trim still passed the suite. conformancerebalance.yaml
rebal-sells-one-of-two-when-only-one-fitsA 30% AAA target leaves AAA 6,540 overweight. One 6,000 lot fits inside that and a second would overshoot by more than 5,000, so exactly one is sold. This is the case the trim has to get right. conformancerebalance.yaml

Coordinate a household 14

UHNW families, UMAs with several managers, spouses at different custodians. Budgets belong to taxpayers and are never pooled.

CaseWhat it pinsSuiteDefined in
household-coordinatedBudget is reserved per tax unit as each account is served, so no two runs spend the same allowance.runs liveuse_cases.py
household-uma-washSame account, same taxpayer, two managers. Neither did anything wrong and the loss would still be disallowed, so it is caught first.runs liveuse_cases.py
household-gapSequential allocation depends on the order units are served, so the run reports an LP bound on what any allocation could have raised.runs liveuse_cases.py
household-joint-optimalNo ordering at all. Where the search exhausts, the run proves no other selection raises more cash within the budget — which the sequential coordinator can never say.runs liveuse_cases.py
household-joint-infeasible-is-provenBecause each tax unit was searched exhaustively, the shortfall is a proof rather than a failure to find a plan.runs liveuse_cases.py
household-capabilitiesTwo engines on one server: the joint optimizer proves optimality where its search exhausts, the coordinator never does. The server states the difference rather than letting a coordinated result read as an optimal one.runs liveuse_cases.py
scope-household-refused-by-account-engineAn account-scoped engine must refuse household scope rather than looping over accounts.conformancescope.yaml
scope-unconfigured-adapter-refusesAn adapter with no pinned engine build refuses rather than guessing.conformancescope.yaml
scope-coordinated-household-is-servedThe coordinator accepts the scope it declares and produces a candidate.conformancescope.yaml
scope-coordination-never-claims-optimalityA coordinated result must not describe itself as optimal.conformancescope.yaml
scope-coordination-declares-order-dependenceOrder dependence is stated, because it changes the answer.conformancescope.yaml
scope-coordination-budget-by-tax-unitBudgets are accounted per taxpayer, never pooled across a household.conformancescope.yaml
scope-coordination-respects-the-budgetCoordination does not loosen the shared budget it is coordinating.conformancescope.yaml
scope-coordination-conserves-positionsThe combined trade list across units must still be valid as a whole.conformancescope.yaml

Municipal bonds 26

UHNW and HNW in high-tax states, and anyone comparing a muni to a Treasury. The standard formula uses the federal rate alone and understates the equivalent yield by over a point.

CaseWhat it pinsSuiteDefined in
muni-naive-understates7.01% against the 5.56% the federal-only formula gives. State tax and the 3.8% surtax are both avoided, and both are routinely left out.runs liveuse_cases.py
muni-out-of-stateFederally exempt, state taxable. 5.91% instead of 7.01% — a different proposition that looks identical on a screen.runs liveuse_cases.py
muni-private-activity-in-amtThe interest is a preference item, so nothing is avoided and the advantage is zero. The answer reverses rather than shades.runs liveuse_cases.py
muni-2009-windowBonds issued in 2009 and 2010 are excepted from the preference, so the advantage survives AMT. A narrow window with a total effect.runs liveuse_cases.py
muni-discount-is-ordinaryA $50 discount against a $25 threshold: accrued market discount is ordinary income on disposition. The interest stays exempt; the discount does not.runs liveuse_cases.py
muni-discount-de-minimis$20 against the same $25 threshold is treated as zero, so the gain on redemption is capital. The margin is what a buyer can act on.runs liveuse_cases.py
muni-discount-short-maturityThe threshold is a quarter point per year, so two years allows only $5. Maturity drives this more than price does.runs liveuse_cases.py
muni-premium-no-lossAmortization is mandatory with no deduction, and basis falls to the redemption price. The expectation of a loss at maturity is the thing to correct before they buy.runs liveuse_cases.py
muni-rejects-percentage-rateRefused rather than producing a yield nobody could sanity-check.runs liveuse_cases.py
muni-capabilitiesWhether a bond is a private activity bond, what any state charges, and credit or call risk. Munis are also outside every portfolio engine.runs liveuse_cases.py
muni-naive-formula-understates-for-a-high-earnerA 3.5% in-state muni for a 37% federal, 9.3% state, surtax-paying holder. The rate avoided is 0.37 + 0.038 + 0.093 = 0.501, so the equivalent yield is 0.035/0.499 = 7.014%. Federal alone gives 0.035/0.63 = 5.556%, which understates by nearly a point and a half. rulemunicipal.yaml
muni-out-of-state-bond-avoids-lessThe same bond bought out of state is federally exempt and state taxable, so only 0.37 + 0.038 = 0.408 is avoided and the equivalent yield falls to 0.035/0.592 = 5.912%. rulemunicipal.yaml
muni-surtax-is-part-of-what-is-avoidedWithout the surtax the same in-state bond avoids 0.463 and yields an equivalent 6.518%. The 3.8 points are worth half a point of yield. rulemunicipal.yaml
muni-private-activity-in-amt-has-no-advantageA private activity bond held by a taxpayer in AMT: the interest is a preference item under section 57(a)(5), so nothing is avoided and the equivalent yield is the stated 3.5%. rulemunicipal.yaml
muni-private-activity-outside-amt-keeps-the-advantageThe same bond for a taxpayer not in AMT is exempt as normal. The result carries a warning rather than a different number. rulemunicipal.yaml
muni-discount-above-the-threshold-is-ordinary-income1,000 redemption, 10 complete years: the threshold is 1000 x 0.0025 x 10 = 25. A 50 discount is above it, so accrued market discount is ordinary income on disposition, not capital gain. rulemunicipal.yaml
muni-small-discount-stays-capitalThe same bond bought at 980: a 20 discount against a 25 threshold is de minimis, treated as zero, and the gain on redemption is capital. rulemunicipal.yaml
muni-a-short-bond-tolerates-far-less-discountTwo complete years gives a threshold of only 1000 x 0.0025 x 2 = 5, so the same 20 discount that was de minimis over ten years is ordinary income over two. Maturity drives this more than price does. rulemunicipal.yaml
muni-a-discount-exactly-at-the-threshold-is-not-de-minimisThe statute says "less than", so a 25 discount against a 25 threshold fails the test. The boundary matters because it is where advice changes. rulemunicipal.yaml
muni-bought-at-par-has-no-discountNo discount at all, so no character question arises and nothing accrues. rulemunicipal.yaml
muni-premium-buys-no-deduction1,050 for a bond redeeming at 1,000 over ten years: 50 of premium, 5 a year, and section 171(a)(2) allows no deduction for any of it. rulemunicipal.yaml
muni-premium-leaves-no-loss-at-maturityBasis falls by the amortization each year, so at maturity it equals the 1,000 redemption price and there is no loss to claim. The holder who expected one has already had the benefit. rulemunicipal.yaml
muni-no-premium-when-bought-at-a-discountBought below par, so there is no premium to amortize. The discount has its own treatment and is not computed by this calculation. rulemunicipal.yaml
muni-exempt-interest-still-reaches-social-securityThe pack records that exempt interest is added back under section 86(b)(2)(B) when deciding how much Social Security is taxable. No engine computes that, so the result carries it as a note rather than a figure. rulemunicipal.yaml
muni-private-activity-issued-in-2009-escapes-the-preferenceSection 57(a)(5)(C)(vi) excepts private activity bonds issued in 2009 and 2010. A 2009 bond held by a taxpayer in AMT keeps its exemption, so the full 50.1% is still avoided where an otherwise identical 2008 bond would avoid nothing. rulemunicipal.yaml
muni-private-activity-issued-in-2008-does-notOne year earlier and the preference applies, so the same taxpayer in AMT gets no advantage at all. The window is narrow and the difference total. rulemunicipal.yaml

Know what a gain costs 31

Every segment, and where a wrong answer is most likely to be believed, because the output looks like a return even though it is not one.

CaseWhat it pinsSuiteDefined in
federal-estimateOrdinary tax band by band, then the gain stacked on top of taxable income so it lands in the right preferential band.runs liveuse_cases.py
federal-gain-splitsThe gain is split across bands rather than taxed wholly at the rate its top dollar reaches. This is the error the legacy code made.runs liveuse_cases.py
federal-niit-thresholdAt $260,000 MAGI only $10,000 is over the threshold, so the 3.8% applies to that, not to the whole $45,000 of investment income.runs liveuse_cases.py
federal-amt-bitesTentative minimum tax against regular tax, with the exemption phasing out at 50 cents per dollar over the threshold.runs liveuse_cases.py
federal-amt-keeps-ratesA gain inside AMTI keeps its preferential rate. Taxing it at 26/28% would overstate the liability substantially.runs liveuse_cases.py
federal-loss-limitThe annual limit and what carries forward, rather than deducting the whole loss against ordinary income.runs liveuse_cases.py
federal-rejects-floatMoney is decimal. A value that would silently lose a fraction of a cent is rejected at the boundary rather than rounded quietly.runs liveuse_cases.py
federal-capabilitiesThe boundary stated by the server itself: no return, no credits, no state tax, and AMT and section 199A never folded together.runs liveuse_cases.py
amt-below-phaseout-full-exemptionAMTI 600,000 MFJ is below the 1,000,000 threshold, so the full 140,200 exemption survives. Base 459,800: 26% x 244,500 = 63,570 plus 28% x 215,300 = 60,284, giving 123,854. ruleamt.yaml
amt-not-owed-when-regular-tax-is-higherAMT is the excess over regular tax, so a higher regular tax means none.ruleamt.yaml
amt-exemption-phases-out-at-fifty-percentAMTI 1,200,000 is 200,000 over the MFJ threshold. At 50 cents on the dollar that removes 100,000 of the 140,200 exemption, leaving 40,200. ruleamt.yaml
amt-exemption-fully-eliminatedThe loss is capped at the exemption; it cannot go negative.ruleamt.yaml
amt-single-thresholds-are-halfA single filer starts losing the exemption at 500,000.ruleamt.yaml
amt-preserves-preferential-rates-on-gainsCapital gain keeps its preferential rate inside AMT. Base 459,800 less the 400,000 gain leaves 59,800 of ordinary base at 26% = 15,548. The gain stacked on 59,800 puts 39,100 in the 0% band and 360,900 at 15% = 54,135. Tentative minimum tax 69,683. Taxing the whole base at 26/28% would have given 123,854 — an overstatement of more than 54,000. ruleamt.yaml
amt-low-income-owes-nothingAMTI below the exemption leaves no base at all.ruleamt.yaml
ordinary-mfj-three-bandsMFJ taxable income of 152,800 spans three bands. 10% x 24,800 = 2,480; 12% x 76,000 = 9,120; 22% x 52,000 = 11,440. rulefederal_income.yaml
ordinary-single-top-bandA single filer above 640,600 reaches the 37% band.rulefederal_income.yaml
ordinary-hoh-differs-from-singleHOH bands are wider at the bottom than single. 10% x 17,700 = 1,770 plus 12% x 42,300 = 5,076, giving 6,846. rulefederal_income.yaml
ordinary-mfs-top-band-is-half-of-mfjMFS reaches 37% at 384,350, half the MFJ figure.rulefederal_income.yaml
standard-deduction-mfj185,000 less the 32,200 MFJ standard deduction leaves 152,800.rulefederal_income.yaml
standard-deduction-overridden-by-itemizingA supplied itemized figure replaces the standard deduction.rulefederal_income.yaml
capital-gain-straddles-zero-and-fifteenThe classic error: taxing the whole gain at the rate its top dollar reaches. Ordinary income of 90,000 leaves 8,900 of the MFJ 0% band; the remaining 91,100 is taxed at 15% for 13,665, not 15,000. rulefederal_income.yaml
capital-gain-entirely-in-zero-bandA modest gain with no other income is untaxed.rulefederal_income.yaml
capital-gain-reaches-twenty-percent13,700 of room below 613,700 at 15%, then 86,300 at 20%.rulefederal_income.yaml
niit-excess-bindsMAGI 260,000 is 10,000 over the MFJ threshold; NII is larger.rulefederal_income.yaml
niit-nii-bindsWell over the threshold, so the smaller NII is the base.rulefederal_income.yaml
niit-below-thresholdNo surtax below the threshold, however large the NII.rulefederal_income.yaml
niit-mfs-threshold-is-125000The MFS threshold is half the MFJ figure and is not indexed.rulefederal_income.yaml
capital-loss-limited-with-carryforward3,000 deductible now, the remaining 9,000 carries forward.rulefederal_income.yaml
capital-loss-mfs-limit-halvedMFS is limited to 1,500, half the amount available to other statuses.rulefederal_income.yaml
capital-loss-smaller-than-the-limitA loss under the cap is fully deductible with nothing carried.rulefederal_income.yaml

Business owner and entity 25

Founders, practice owners, and anyone with pass-through income — at UHNW that is most clients.

CaseWhat it pinsSuiteDefined in
federal-qbi-phase-inAbove the threshold the wage limit applies in proportion to how far into the phase-in range the taxpayer is, not all at once.runs liveuse_cases.py
federal-qbi-sstbThe same figures as a consultancy rather than a manufacturer: the applicable percentage falls to zero and the deduction goes with it.runs liveuse_cases.py
biz-179-phaseoutThe dollar limit falls one-for-one with spending above the threshold, so deferring a purchase across the year end can restore it.runs liveuse_cases.py
biz-179-eliminatedPast a point there is no section 179 deduction at all, which is the opposite of what buying more usually does.runs liveuse_cases.py
biz-179-income-limitedCapped at business income, with the excess carried forward rather than lost. Two different limits that are easily confused.runs liveuse_cases.py
biz-entity-close-call$368,000 against $370,000 — close enough that payroll or state tax, neither of which is included, would decide it.runs liveuse_cases.py
biz-entity-retainedOnly the corporate layer is paid this year. The second layer is deferred, not avoided, and the comparison flips.runs liveuse_cases.py
biz-rejects-rate-over-oneRefused rather than taxing the income at 3,700%. The fraction is range-checked at the boundary.runs liveuse_cases.py
section-179-under-the-threshold-is-unreduced3,000,000 of spending is below the 4,090,000 threshold, so the full 2,560,000 dollar limit survives and the deduction is capped by it. rulebusiness_compliance_salt.yaml
section-179-phases-out-dollar-for-dollar4,500,000 of spending is 410,000 over the threshold, so the limit falls by the same 410,000 to 2,150,000. The reduction is one-for-one, not a percentage. rulebusiness_compliance_salt.yaml
section-179-disappears-entirely-with-enough-spendingSpending of 6,700,000 is 2,610,000 over the threshold, more than the 2,560,000 limit itself, so no section 179 deduction is available at all. rulebusiness_compliance_salt.yaml
section-179-business-income-limit-carries-forward500,000 elected against 300,000 of business income: the deduction is capped at income and the remaining 200,000 carries forward rather than being lost. rulebusiness_compliance_salt.yaml
entity-double-layer-against-a-top-rate-owner1,000,000 at 21% is 210,000 of corporate tax, leaving 790,000. Distributed in full at 20% adds 158,000, for 368,000 against 370,000 passed through at 37%. The corporation is ahead by 2,000, which is a narrower margin than people expect. rulebusiness_compliance_salt.yaml
entity-retaining-earnings-defers-the-second-layerThe same income with nothing distributed costs only the 210,000 corporate layer this year, well under the 370,000 pass-through figure. The second layer is deferred, not avoided. rulebusiness_compliance_salt.yaml
entity-passthrough-deduction-reverses-the-comparisonA 200,000 section 199A deduction drops the pass-through base to 800,000, taxed at 37% for 296,000, against the same 368,000 corporate total. rulebusiness_compliance_salt.yaml
qbi-below-threshold-no-limitsBelow the threshold neither the wage limit nor the service-business restriction applies. 20% of 200,000 is 40,000. ruleqbi.yaml
qbi-sstb-unrestricted-below-thresholdA service business below the threshold is treated like any other.ruleqbi.yaml
qbi-wage-limit-above-the-rangeFully phased in, the wage limit binds: 50% of 50,000 wages is 25,000, below the 40,000 tentative deduction. ruleqbi.yaml
qbi-ubia-alternative-beats-wages-alone25% of wages plus 2.5% of property can exceed half the wages where a business is capital heavy: 5,000 + 50,000 = 55,000 beats 10,000. ruleqbi.yaml
qbi-wage-limit-phases-in-not-switches-onHalfway through the 150,000 MFJ range only half the excess over the limit is removed. Tentative 40,000, limit 25,000, ratio 0.5, so the reduction is 7,500 and the deduction is 32,500. ruleqbi.yaml
qbi-sstb-fully-phased-outA specified service business above the range gets nothing on that income, and the new minimum deduction is not applied because no QBI is taken into account. That interaction is unsettled and is flagged for review. ruleqbi.yaml
qbi-sstb-partial-phase-outAt 51% through the range the applicable percentage is 49%. QBI, wages and property are all reduced, then the wage limit phases in on top. ruleqbi.yaml
qbi-capped-by-taxable-income-less-gainsThe overall cap is 20% of taxable income less net capital gain. With 150,000 of the 200,000 taxable income being gain, the cap is 10,000. ruleqbi.yaml
qbi-minimum-deduction-for-small-active-businessAbove the range with no wages the limit would be zero, but at least 1,000 of active QBI secures the 400 minimum. ruleqbi.yaml
qbi-single-threshold-is-half-of-mfjA single filer is fully phased in by 276,750.ruleqbi.yaml

Transfer wealth 18

UHNW almost exclusively, plus any trustee filing a 1041. Lifetime gifts are added back to the estate, not subtracted from it.

CaseWhat it pinsSuiteDefined in
estate-within-exclusionNo federal estate tax at $12m against a $15m exclusion. A return may still be due, and several states tax far lower estates.runs liveuse_cases.py
estate-gifts-added-back$2m of prior gifts does not simply shrink the estate: it is added to it before the rate schedule, so it pushes the estate into higher brackets.runs liveuse_cases.py
estate-portabilityDoubles the shelter to $30m. Taken as given: portability needs a timely return on the first death, which this server cannot check.runs liveuse_cases.py
estate-gift-splitting$19,000 times three recipients times two donors shelters $114,000. Splitting needs both spouses to consent on a return.runs liveuse_cases.py
estate-noncitizen-spouseThe unlimited marital deduction does not apply, so a separate and much larger annual figure governs instead of the ordinary $19,000.runs liveuse_cases.py
estate-splitting-refused-for-spouse-giftThe non-citizen spouse figure applies to one donor and one spouse. Multiplying it would invent an exclusion that does not exist.runs liveuse_cases.py
estate-fiduciary-compressionThe compression that drives most distribution planning: an individual reaches 37% in the hundreds of thousands, a trust at $16,000.runs liveuse_cases.py
estate-within-the-exclusion-owes-nothingA 12,000,000 estate sits under the 15,000,000 exclusion, so the credit covers the whole tentative tax. ruleestate_gift.yaml
estate-lifetime-gifts-are-added-back-not-subtractedTaxable estate 19,500,000 plus 2,000,000 of lifetime gifts is a 21,500,000 base. Tentative 345,800 + 40% x 20,500,000 = 8,545,800. Credit on the 15,000,000 exclusion is 345,800 + 40% x 14,000,000 = 5,945,800. Tax is the difference, 2,600,000. ruleestate_gift.yaml
estate-portability-doubles-the-shelterA deceased spouse's unused exclusion of 15,000,000 raises the available exclusion to 30,000,000, which covers a 28,000,000 estate entirely. ruleestate_gift.yaml
estate-lowest-bracket-is-reached-only-without-the-creditWith no exclusion consumed by gifts, a 15,010,000 estate has 10,000 above the exclusion. Tentative on 15,010,000 is 345,800 + 40% x 14,010,000 = 5,949,800; credit is 5,945,800; tax is 4,000, which is the 40% top rate applied to the 10,000 of excess. ruleestate_gift.yaml
gift-single-donor-single-recipientOne donor to one recipient: 19,000 of a 25,000 gift is covered by the annual exclusion and 6,000 is a taxable gift. ruleestate_gift.yaml
gift-splitting-doubles-the-exclusionTwo donors, one recipient, so 38,000 of a 50,000 gift is covered.ruleestate_gift.yaml
gift-to-three-children-from-two-parents19,000 x 3 recipients x 2 donors is 114,000, covering a 100,000 gift.ruleestate_gift.yaml
gift-to-noncitizen-spouse-uses-its-own-figureThe unlimited marital deduction does not apply, so a separate and much larger annual figure of 194,000 governs. ruleestate_gift.yaml
fiduciary-reaches-the-top-rate-at-sixteen-thousand20,000 of ordinary income: 10% x 3,300 = 330, 24% x 8,400 = 2,016, 35% x 4,300 = 1,505, 37% x 4,000 = 1,480. Total 5,331. ruleestate_gift.yaml
fiduciary-gain-stacks-on-ordinary-incomeWith 20,000 of ordinary income the stack is already past the 16,250 fifteen-percent ceiling, so the whole 10,000 gain is taxed at 20%. ruleestate_gift.yaml
fiduciary-small-gain-falls-in-the-zero-bandWith 1,000 of ordinary income a 2,000 gain stacks to 3,000, still inside the 3,300 zero-rate ceiling, so no tax on the gain at all. ruleestate_gift.yaml

Fix a filing problem 16

Every segment, and the group most often needed by a prospect rather than a client: someone arrives already late.

CaseWhat it pinsSuiteDefined in
comp-file-vs-payFailure to file runs at ten times the failure-to-pay rate, with the section 6651(c) offset applied so the combined rate is 5%, not 5.5%.runs liveuse_cases.py
comp-filed-not-paid$200 instead of $2,000. This is why 'file even if you cannot pay' is the standard advice, and the numbers make the case.runs liveuse_cases.py
comp-minimum-penaltyThe percentage addition would be $360, so the statutory floor applies instead. Small balances are not proportionally cheap.runs liveuse_cases.py
comp-partnership-penalty$8,320 for eight partners and four months. The penalty is per owner per month and does not depend on tax owed.runs liveuse_cases.py
comp-safe-harbour-jump110% of last year's known tax beats 90% of this year's guess. The prior-year route is what protects a year like this.runs liveuse_cases.py
comp-safe-harbour-fallThe current-year route governs when income drops. Taking the prior-year route out of habit ties up cash for a year.runs liveuse_cases.py
comp-rejects-negative-daysRefused rather than producing a negative penalty.runs liveuse_cases.py
penalty-late-filing-is-ten-times-late-payment95 days is four months. Failure-to-file runs at 5% less the 0.5% failure-to-pay rate, so 4.5% x 4 x 10,000 = 1,800, plus 0.5% x 4 x 10,000 = 200 for failure to pay. Total 2,000. rulebusiness_compliance_salt.yaml
penalty-filing-on-time-without-paying-costs-far-lessThe same 10,000 unpaid for the same four months, but filed on time, costs 200 rather than 2,000. This is why filing without paying is the advice. rulebusiness_compliance_salt.yaml
penalty-one-day-late-is-a-whole-monthAny part of a month counts as a whole month, so a single day late already carries 4.5% + 0.5% of the unpaid tax. rulebusiness_compliance_salt.yaml
penalty-minimum-applies-after-sixty-daysWith only 2,000 unpaid and 95 days late, the computed failure-to-file addition is 4.5% x 4 x 2,000 = 360, below the 535 floor, so the floor applies instead. rulebusiness_compliance_salt.yaml
penalty-partnership-does-not-depend-on-tax-owedEight partners, four months late: 260 x 8 x 4 = 8,320, owed even by a partnership with no taxable income. rulebusiness_compliance_salt.yaml
penalty-entity-is-capped-at-twelve-monthsTwenty months late is charged as twelve: 260 x 3 x 12 = 9,360. rulebusiness_compliance_salt.yaml
safe-harbour-prior-year-route-protects-a-jump-in-incomePrior AGI of 400,000 is over the 150,000 threshold, so the prior-year route is 110% x 80,000 = 88,000. The current-year route is 90% x 200,000 = 180,000. The smaller governs, so 88,000 protects the year. rulebusiness_compliance_salt.yaml
safe-harbour-lower-income-uses-one-hundred-percentPrior AGI of 120,000 is under the threshold, so the prior-year route is 100% of last year's 20,000 rather than 110%. rulebusiness_compliance_salt.yaml
safe-harbour-current-year-route-wins-when-income-fallsIncome fell, so 90% of this year's 20,000 is 18,000, below 110% of last year's 90,000. Paying to last year's figure would massively overpay. rulebusiness_compliance_salt.yaml

Expat and cross-border 14

Clients working abroad, and anyone receiving money from family overseas.

CaseWhat it pinsSuiteDefined in
intl-feie-with-housingThe housing amount is the excess over a 16% base, capped at 30%, not a deduction for everything spent on rent.runs liveuse_cases.py
intl-feie-prorated183 days prorates both the exclusion and the housing figures. A day count that is off by one changes the answer.runs liveuse_cases.py
intl-feie-housing-below-baseThe base is a floor, not a starting allowance. Spending under it produces no housing amount at all.runs liveuse_cases.py
intl-foreign-gift-individualReportable, and not income. The threshold for individuals is the flat statutory $100,000, which is not indexed.runs liveuse_cases.py
intl-foreign-gift-entityA far lower, inflation-adjusted threshold applies, so $25,000 is reportable where it would not be from an individual.runs liveuse_cases.py
intl-rejects-bad-day-count400 days in a year is not a typo the server will absorb.runs liveuse_cases.py
feie-full-year-exclusion-caps-the-income180,000 of foreign earned income against the 132,900 exclusion for a full qualifying year leaves 47,100 before any housing amount. ruleinternational.yaml
feie-is-prorated-for-a-partial-qualifying-period183 qualifying days is 183/365 of the year, so the exclusion is 132,900 x 183/365 = 66,632.05 rather than the full amount. ruleinternational.yaml
feie-housing-is-the-excess-over-a-base-and-is-cappedBase is 16% of 132,900 = 21,264 and the limit is 30% = 39,870. With 45,000 of expenses the limit binds, so the housing amount is 39,870 - 21,264 = 18,606. Total excluded is 132,900 + 18,606 = 151,506. ruleinternational.yaml
feie-housing-below-the-base-gives-nothingExpenses of 15,000 are below the 21,264 base, so there is no housing amount at all. The base is a floor, not a starting allowance. ruleinternational.yaml
feie-cannot-exclude-more-than-was-earnedIncome of 50,000 is below the exclusion, so only 50,000 is excluded and nothing remains for a housing amount to attach to. ruleinternational.yaml
foreign-gift-from-an-individual-uses-the-statutory-figureGifts from foreign individuals and estates use the flat 100,000 statutory threshold, which section 6039F does not index, so 150,000 is reportable. ruleinternational.yaml
foreign-gift-from-a-corporation-uses-the-indexed-figureGifts from foreign corporations and partnerships use the much lower inflation-adjusted threshold of 20,573, so 25,000 is reportable where the same amount from an individual would not be. ruleinternational.yaml
foreign-gift-below-the-threshold-is-not-reportable90,000 from a foreign individual is under the 100,000 threshold. It is still not income either way; the question here is disclosure only. ruleinternational.yaml

What is deductible 9

HNW and UHNW itemizers in high-tax states. The phase-down is a slope, not a cliff.

CaseWhat it pinsSuiteDefined in
salt-full-cap$40,400 of a $60,000 bill is deductible; the rest is simply lost and does not carry forward.runs liveuse_cases.py
salt-phasing-downA slope, not a cliff: 30 cents per dollar of MAGI above the threshold. Income in this band costs more than its own marginal rate.runs liveuse_cases.py
salt-at-the-floorThe cap stops falling at $10,000 however high income goes, so the penalty on extra income ends there too.runs liveuse_cases.py
salt-separate-filerCap, threshold and floor are all halved, which is easy to miss when comparing a joint and separate return.runs liveuse_cases.py
salt-no-state-is-coveredNo state pack is bound, so no state's own tax is computed. The answer names what a state pack would have to contain.runs liveuse_cases.py
salt-below-the-threshold-gets-the-full-capMAGI of 400,000 is under the 505,000 threshold, so the full 40,400 cap applies and 60,000 of tax paid is limited to it. rulebusiness_compliance_salt.yaml
salt-phases-down-thirty-cents-on-the-dollarMAGI of 550,000 is 45,000 over the threshold. At 30 cents that removes 13,500, leaving an effective cap of 26,900. It is a slope, not a cliff. rulebusiness_compliance_salt.yaml
salt-stops-falling-at-the-floorMAGI of 700,000 is far past the 606,333 point where the cap reaches the 10,000 floor, and it does not fall below it however high income goes. rulebusiness_compliance_salt.yaml
salt-separate-filers-get-half-of-everythingMarried filing separately halves the cap to 20,200, the threshold to 252,500 and the floor to 5,000. rulebusiness_compliance_salt.yaml

Trust the data 12

Every segment, worst at UHNW. Unknown basis blocks rather than defaulting to zero, which is a question instead of a confident wrong answer.

CaseWhat it pinsSuiteDefined in
portfolio-intakeRun first on a new export. Lots with unknown basis are named here rather than silently treated as zero-basis later.runs liveuse_cases.py
missing-basis-blocks-rather-than-defaulting-to-zeroA lot with unknown basis produces a blocked disposition, not a gain computed from a zero that was never in the record. ruleinvestment_rules.yaml
data-missing-basis-is-never-soldA lot with unknown basis cannot be priced for tax and must not be traded.conformancedata_quality.yaml
data-missing-basis-is-reportedThe exclusion is visible in diagnostics rather than silent.conformancedata_quality.yaml
data-missing-acquisition-date-is-never-soldWithout an acquisition date the holding period is undeterminable.conformancedata_quality.yaml
data-missing-price-is-never-soldA lot with no supplied price cannot have proceeds computed.conformancedata_quality.yaml
data-missing-price-is-reportedA missing price is a data gap worth naming.conformancedata_quality.yaml
data-usable-lot-still-tradesBad neighbours do not disqualify the one sound lot.conformancedata_quality.yaml
data-all-unusable-is-infeasibleWhen only unusable lots could meet the target, the run is infeasible.conformancedata_quality.yaml
data-unusable-lots-do-not-count-toward-cashExcluded lots contribute nothing to the proceeds figure.conformancedata_quality.yaml
data-no-price-for-any-lotWith no prices at all nothing can be valued, so nothing is proposed.conformancedata_quality.yaml
data-blocked-lots-never-reach-the-trade-listThe invariant battery rejects any trade against an unusable lot.conformancedata_quality.yaml

Entitlement, scope and replay 6

Every deployment. An account the caller may not read is refused in the same words as one that does not exist.

CaseWhat it pinsSuiteDefined in
portfolio-discoverThe first call of a session. Account metadata comes from the document, so it is named here; entitlements still decide the answer.runs liveuse_cases.py
portfolio-deniedThe document describes it, so it exists. It is still refused, and with the same wording as an account that does not exist at all.runs liveuse_cases.py
portfolio-idempotentRepeating a request must not produce a second analysis with a new evidence package. The key is the caller's guarantee.runs liveuse_cases.py
portfolio-household-refusedRunning an account engine over a household is refused rather than approximated, and the refusal names what is available instead.runs liveuse_cases.py
portfolio-wrong-yearNo reviewed pack covers 2019, so the run is blocked rather than computed from this year's rules under last decade's law.runs liveuse_cases.py
portfolio-no-executionNot disabled, not permissioned off — absent. A caller asking for one is refused at the boundary.runs liveuse_cases.py

Conservation, restrictions and determinism 30

Not client scenarios: the invariant battery every proposal is recomputed against, whatever engine produced it.

CaseWhat it pinsSuiteDefined in
cons-no-lot-sold-twiceA lot may appear at most once in the trade list.conformanceconservation.yaml
cons-only-known-lotsEvery lot sold must exist in the frozen snapshot.conformanceconservation.yaml
cons-quantities-positiveA proposed quantity of zero or less is not a trade.conformanceconservation.yaml
cons-empty-portfolio-yields-no-tradesNothing to sell means an empty trade list, not an error.conformanceconservation.yaml
cons-no-cash-target-still-conservesWith no cash target the adapter may harvest, but not oversell.conformanceconservation.yaml
cons-proceeds-match-price-times-quantityRecomputed proceeds must equal quantity times the supplied price.conformanceconservation.yaml
cons-losses-only-portfolioA portfolio of losses raises cash without consuming any budget.conformanceconservation.yaml
cons-gains-only-portfolioA portfolio of gains can still be sold when the budget allows.conformanceconservation.yaml
cons-zero-cash-targetA zero cash target is satisfied immediately and needs no gain.conformanceconservation.yaml
cons-large-target-still-conservesAn unreachable target must not tempt an adapter into overselling.conformanceconservation.yaml
cons-no-budget-declaredWith no gain budget at all the run is unconstrained but still valid.conformanceconservation.yaml
cons-single-lot-portfolioA one-lot portfolio is a degenerate but legal problem.conformanceconservation.yaml
determinism-mixed-portfolioTwo independent solves of one problem must produce identical trades.conformancedeterminism.yaml
determinism-tight-budgetA binding budget must not introduce run-to-run variation.conformancedeterminism.yaml
determinism-infeasible-runEven a failing run must fail the same way each time.conformancedeterminism.yaml
determinism-with-restrictionsRestrictions must not make tie-breaking order dependent.conformancedeterminism.yaml
determinism-with-wash-screeningWash-sale probing must not vary between runs.conformancedeterminism.yaml
determinism-equal-value-lotsLots of similar appeal need a declared tie-break, not an arbitrary one.conformancedeterminism.yaml
restrict-security-is-never-soldA do-not-sell security stays untouched even when it is the obvious source of cash.conformancerestrictions.yaml
restrict-two-securitiesSeveral restricted securities are all excluded.conformancerestrictions.yaml
restrict-account-blocks-everything-in-itA restricted account contributes nothing at all.conformancerestrictions.yaml
restrict-min-holding-blocks-recent-lotA lot held fewer days than policy allows cannot be sold.conformancerestrictions.yaml
restrict-min-holding-allows-seasoned-lotA long-held lot passes the same minimum-holding policy.conformancerestrictions.yaml
restrict-min-holding-zero-blocks-nothingA zero-day minimum is no restriction at all.conformancerestrictions.yaml
restrict-everything-is-infeasibleRestricting every security leaves nothing to sell.conformancerestrictions.yaml
restrict-plus-budget-interactA restriction on the loss lot removes the offset the budget relied on.conformancerestrictions.yaml
restrict-unrelated-security-has-no-effectRestricting a security the portfolio does not hold changes nothing.conformancerestrictions.yaml
restrict-loss-lot-still-allows-other-lossesRestricting one loss lot leaves the other available.conformancerestrictions.yaml
restrict-no-restrictions-declaredAn empty restriction list is not a restriction.conformancerestrictions.yaml
restrict-account-and-security-togetherAccount and security restrictions apply together, not in preference.conformancerestrictions.yaml