Risk engine

The risk engine is one pure function, plan(target, context), in engine/risk.mjs. It takes what the model asked for and the state of the book, and returns the orders that may be sent, the refusals with their reasons, and notes. It makes no network call and reads no setting the model can reach.

Rules and their default values

RuleDefaultEffect
maxWeight35%One stock never holds more than this share of the book.
minCash10%The total asked is scaled down until this share stays in USDG.
minOrderUsd$5No dust orders.
minDeltaPct2%A change smaller than this share of the book is ignored.
maxTurnover25%Buys are shrunk so one cycle trades at most this share of the book. Sells are never shrunk.
maxImpactPct0.6%An order that would move its pool more than this is cut to fit, or refused.
maxTwapDevPct1.0%A stock whose spot price is more than this far from its 5 minute average is not traded: the pool may be being pushed.
slippagePct1.2%Minimum output on every swap, so a price that moved reverts the swap.
cooldownMin30A stock that just traded is not bought or trimmed again until this has passed. Closing a position is always allowed.
noBuyWeekendtrueNo new buys from Friday 20:00 to Sunday 20:00 ET, nor on NYSE holidays.
dailyLossPct7%After a day this bad, no new buys until tomorrow.
drawdownBreaker15%Sell everything, pause, wait for a human.

How an answer is processed

  1. Clean. Weights that are not numbers or are negative become zero. Each weight is capped at maxWeight. If the sum exceeds 1 - minCash, all weights are scaled down together.
  2. Compare with the current weights. Differences under minDeltaPct are dropped. Differences worth less than minOrderUsd are refused.
  3. Check the price. If spot is more than maxTwapDevPct away from the 5 minute average, the stock is skipped.
  4. Check the context. Weekend and daily loss block buys. Cooldown blocks a stock.
  5. Cap turnover, shrinking buys only.
  6. Limit buys to the cash that exists, counting the proceeds of the sells in the same cycle.
  7. Measure impact for each order from the pool depth. Cut the order to fit under maxImpactPct, or refuse it if what is left is under minOrderUsd.

The order of sells and buys in the output is sells first, so the proceeds exist when the buys are sent.

The breaker

The breaker is checked before the model is consulted. It watches the time-weighted index of the book, which ignores deposits, so a deposit never hides a drawdown and a withdrawal never creates one. When the index is 15% under its high, the engine sells every position in that cycle, writes the reason, and sets the agent to paused. A paused agent never resumes by itself.

Price impact is a first-order estimate

The impact of an order is computed from the pool's current liquidity and price, inside the current tick range. A swap that crosses into a different range would move the price more than estimated. That is why every swap also carries a minimum-output guard: the estimate keeps the orders small, the guard makes a bad fill revert.

Run it yourself

The site's playground imports the same risk.mjs file and runs it in your browser on the live depths. The unit tests call plan with synthetic books and check each rule.