PLUTUS · ENGINE ROOM // PAPER ONLY · NO LIVE CAPITAL
ENGINEERED BY LEOPARD DATA

The Entry Gauntlet

A stimulus becomes an order only by surviving seven ordered tests. Most do not survive, and the row records which test killed them. That is the design working, not failing.

A stimulus is a question, not an answer

Trades happen only at defined decision points and only from eight named causes. Everything else — a hunch, a headline that did not clear the classifier, a probability that moved but did not cross its configured line — is not a stimulus and produces no order. The absence of a trade needs no journal entry; the presence of one always has exactly one Stimuli row at its root.

CodeStimulusExample
S1Signal threshold crossingMomentum rank enters the top N; a sector grade upgrades to A
S2Probability threshold crossingp.regime-persist-30d drops below 0.40 → de-risk
S3Event triggerA news item scores materiality ≥ 4 with volume confirmation
S4Regime transitionThe GMI regime flips; allocation matrices re-key
S5Rebalance / driftAllocation drift breaches its band
S6Risk triggerStress gauge escalates; a position hits its drawdown stop — overrides everything
S7Desk signalAllan's workup-approved user signal
S8Scheduled review verdictAn exit-check or contradiction check returns broken

Seven stages, in order

The stage names below are the seed rows in PipelineStageTypes, in their SortOrder. Every stage reads the frozen MarketState blob, not the tables; the world was computed once at 06:45 and every stimulus sees the same copy.

StageAsksReads
predicateDoes this strategy's config actually say act?StrategyConfigs
confirmationDo the confirmation-role signals agree — RVOL, breadth, materiality corroboration?ResolverStances via the blob
vetoDoes a veto-role signal say no — stress gauge, priced-in gauge, the earnings pre-print window, a standing contradiction verdict?same — risk always outranks
probabilityDoes the named p-signal clear its bar, with enough nEffective and a narrow enough interval?ProbabilityEmissions via the blob
capsWould this breach a lane cap, an account cap, the holdings count, the minimum position size, the short-exposure cap?TradingAccounts, Positions
sizingHow much, in dollars, fractional — and does the commission-drag guard allow it?account equity, the sizing mode
constructionWhat order type, what collar, what execution window; for shorts, is borrow available?Instruments, the bars

Short entries run the same gauntlet with four additions: the strategy must declare can_short; borrow availability and modeled borrow cost enter sizing; a short-exposure cap applies; and a squeeze guard refuses new shorts in names above a short-interest or days-to-cover threshold. Until the live arming step exists, shorts are paper only.

Seven ordered testsThe entry gauntlet as a flowchart of seven ordered tests
Any stage can kill the trade. Every kill becomes a StandAside decision that is scored later by its counterfactual.

Most of them die, and the row says where

Each pass through the gauntlet is one PipelineRuns row. It records the outcome and, when the stimulus was refused, FkKilledAtPipelineStageTypesKey — which stage, and which gate within it. Most rows in this table are refusals. A system whose main output is trades it decided not to make, journaled as carefully as the ones it did, is a system that can be trusted to enter.

The kills are not waste. A refused stimulus becomes a StandAside decision, and the learning loop scores it against what would have happened: AvoidedLoss or MissedGain. "The veto gate saved us X this quarter" is a query, not an impression. The full reason chain is written before any order is submitted, never after.

Risk always outranks

Two places enforce it. On the way in, the veto stage sits third — after the strategy has said yes and confirmation has agreed — and a stress or priced-in resolver can still stand the trade down. The stress resolver is an absolute veto over the composite stance; no strategy opinion and no desk tilt outranks it.

On the way out, E5 risk stops can fire in any hourly window regardless of what any strategy thinks, and the exit-evaluation pipeline checks them first. Risk exits are also the one class of decision with no staleness budget: a stop that should have fired at 11:00 still fires at 15:00 after downtime, because the position is still wrong.

The other two pipelines

The same PipelineRuns table journals two more pipelines, distinguished by FkDecisionPipelineTypesKey, and they run on held positions rather than candidates.

hold-review asks three things in order at each position's own review tier — daily, weekly, or quarterly. exit-rules: has any bound exit fired? entry-quality: has the thesis-relevant signal set degraded below the entry predicate — would we enter this today? Tactical lanes exit on failure; long-horizon lanes flag for review. slot-defense: does the position still earn its slot against the current candidate ranking? Passing all three is the hold decision, journaled once at the review tier, not seven times a day.

exit-evaluation runs every hourly window. risk-precedence first: E5 and S6 exits, always allowed to fire. Then exit-arbitration: the remaining armed exits in order, first to fire wins, with hysteresis so positions do not flap. Then exit-construction: the exit order runs the same construction and journal path as an entry.

Eight ways out, bound at entry

Every position binds its exits when it opens. They are typed so that each kind gets its own learning-loop bucket — "our time stops make money, our panic never fires" becomes measurable.

CodeExitTrigger
E1Thesis completeThe entry thesis' stated outcome occurred — the good exit
E2Signal decayThe driving signal fell through its exit threshold, set below the entry threshold
E3Probability flipThe bound p-signal crossed its exit line
E4Time stopHorizon expiry — mandatory for capped lanes; re-entry is a new gauntlet
E5Risk stopDrawdown stop or stress-gauge de-risk — always outranks holds
E6Thesis brokenA contradiction verdict, or the entry's what would change this occurred
E7Valuation outrunPrice appreciation outran earnings-power growth past the stretch factor
E8DisplacementA rebalance ranked a new candidate above the weakest holding beyond the switch margin

These were a JSON list in an early draft. They are now PositionExits rows — one armed exit per row, with its trigger (a price, a time, a threshold value, a trail), its status (Armed, Triggered, Cancelled, Expired), and once triggered, the order that executed it. The reason is a query: every hourly window asks which armed stops are below the current price?, and that question should be an index, not a document scan.

The causal chain from signal to promotion
Stimuli → PipelineRuns → Decisions → TradeOrders → Positions → ClosedTrades → DecisionOutcomes. Every ClosedTrades row walks back to one stimulus.
Where the kill is written
  • Stimuli — the cause, with TriggerJson and a pointer to the frozen world
  • PipelineRuns — the pass, and FkKilledAtPipelineStageTypesKey if it died
  • Decisions — the verdict, with the typed expectation it will be graded on
  • PositionExits — the armed E1–E8 rows on a live position
Precedence
  • The stress resolver vetoes everything
  • E5 fires in any window, whatever the strategy thinks
  • First armed exit to fire wins; hysteresis stops flapping
  • A hold is journaled once, at its review tier