A prompt that says "be careful" is not a guardrail
Most agent setups start with a system prompt: keep leverage modest, avoid oversized positions, stop trading after a bad day. That is a request, not a rule. A language model reads it as one more piece of text, weighs it against everything else in its context, and can be argued out of it by a tool result, a user message, or its own earlier reasoning.
This is the fourth post in the Agentic Trading on Perps series. The second, AI Trading Bot vs AI Agent on Perps, covered how leverage changes failure modes. The third, Scoped Keys and Delegated Access, covered what a key lets an agent do. This one covers the layer in between: how much, how often, and under what conditions, enforced by code the model does not control.
Four knobs that belong in code
A risk budget becomes a guardrail when it is a number in a config file that a plain function checks before any order leaves the system. Four are worth separating.
- Max leverage. A hard ceiling on the leverage of any single order. If the requested leverage is above the cap, the order is rejected, not trimmed silently.
- Max notional. A ceiling on position size, both per order and in total across open positions. Notional exposure equals margin multiplied by leverage, so a leverage cap alone does not bound exposure.
- Max daily loss. A realized-plus-unrealized loss limit over a rolling window. When it is hit, the system stops opening new positions and flags a human.
- Human-confirm gates. Conditions under which the code pauses and asks a person: an order above a notional threshold, a first trade in a new market, a change to any limit, a request that would reduce margin on an open position.
Where these numbers sit is your decision, and this post does not suggest values. All figures below are an assumed example to show arithmetic, not a recommendation.
The order of derivation matters. Start from the loss you can accept (the risk budget), derive the notional that fits it, and only then read off the leverage cap. Starting from a leverage number and hoping the rest works out reverses the logic, because leverage describes how thinly a position is margined, not how much you can lose.
Why the model must not own write access
The point of a guardrail is that it holds when the model is wrong, confused, or manipulated. That only works if the model cannot change it.
- Separate the config. Limits live in a file or service that the agent's tool set has no write path to. The model can read the limits to explain a rejection; it cannot edit them.
- Separate the check from the intent. The model proposes
{market, side, size, leverage}. A deterministic function approves or rejects. The model does not call the order endpoint directly. - Treat rejection as final within a run. If a rejection can be retried with a reworded request until it passes, the check is a suggestion. Rejections should be logged and should not feed back as "try a smaller number until it works."
- Put limit changes behind a human. Raising a cap is a human-confirm event, performed outside the agent's session.
This is the same principle as scoped permissions, applied to size instead of access. Scoped keys limit which actions exist. Guardrails limit how large those actions can be.
LeverUp margin and liquidation distance
The guardrails above need real mechanics to check against. These are the documented LeverUp ones that affect each knob, all taken from the docs.
Margin is per position. LeverUp's opening-a-position docs describe collateral type, exposure size and leverage as choices made per position, and say positions with different margin tokens are separate. Our comparison, Cross Margin vs Isolated Margin, explains why that behaves much like isolated margin. For guardrails, it means a notional cap should be computed per position and in aggregate, since the account-level picture is the sum of separate positions.
Positions on the same pair merge. The opening-a-position docs say that opening again in the same direction on the same pair with the same margin token adds to the existing position: quantity and margin are summed and the entry price becomes the quantity-weighted average. A check that only looks at the new order's size can pass an order that pushes the merged position over the cap. The check should evaluate the post-merge position.
Liquidation distance depends on leverage. The liquidation docs say a position is liquidated when the price reaches the liquidation price or when collateral cannot cover outstanding fees. They give the loss rate as set per pair: 85% on most pairs, 80% on the high-leverage pairs and a few others. The simplified condition is that total loss and fees reach the threshold times the position margin.
Assumed example: linear exposure, constant USD collateral price, and fees ignored. Adverse price moves are measured from entry. Take an 85% threshold from the liquidation docs. Margin is notional divided by leverage, so the loss that triggers liquidation is roughly 0.85 divided by leverage, as a fraction of notional:
| Assumed leverage | Adverse move that consumes 85% of margin (docs) |
|---|---|
| 5x | about 17% (liquidation docs) |
| 10x | about 8.5% (liquidation docs) |
| 20x | about 4.25% (liquidation docs) |
The numbers only show the shape: the same price move that leaves a low-leverage position intact can liquidate a higher-leverage one. Close and holding fees reduce the liquidation buffer; funding paid reduces it further, while funding received can increase it. The liquidation formula accounts for these amounts. A guardrail can use that relationship to reject orders whose liquidation distance is narrower than a distance you chose in advance, which is a more direct control than a bare leverage number.
Fees accrue while a position is open. The holding and funding docs describe a holding fee that accrues per second on notional and is charged at close, and a funding fee paid by the crowded side. Both are part of the liquidation condition. An always-on agent that holds positions should have these counted in its loss limit; later posts in this series return to this.
Two edge cases for the confirm gate. The ADL docs say leverage-based checks apply only to positions opened on the current UTC day. For those positions, adding enough margin to bring effective leverage below 1x can trigger an automatic close. The opening-a-position docs also note a take-profit cap of 550% of PnL and open-interest caps that can temporarily suspend new positions in a direction. A pre-trade check should treat "order rejected by the venue" as a normal outcome rather than an error to retry blindly.
The protocol's own risk controls are a different layer. LeverUp's risk controls page describes protocol-side abuse detection and states that detection thresholds are not published. That is the protocol managing its own risk. It is not your risk budget, and nothing there caps a single trader's size or loss to a level you chose. Your limits are yours to write.
A minimal pre-trade check sequence
Pseudocode-level, not a bot. The shape is what matters: the model proposes, the gate disposes, and each branch is logged.
proposal = model.propose() # {market, side, size, leverage}
if proposal.market not in ALLOWED_MARKETS: reject("market")
if proposal.leverage > MAX_LEVERAGE: reject("leverage")
merged = merge_with_open_position(proposal) # same pair, side, margin token
if merged.notional > MAX_NOTIONAL_PER_POSITION: reject("position notional")
post_trade = replace_matched_position(open, merged)
if total_notional(post_trade) > MAX_TOTAL: reject("total notional")
if liq_distance(merged) < MIN_LIQ_DISTANCE: reject("liquidation distance")
if loss_over_rolling_window() >= MAX_DAILY_LOSS: halt_and_alert()
if needs_human(merged, proposal): queue_for_confirm()
else: submit(proposal)
A few notes on this sequence:
- Order matters. Cheap structural checks first (market, leverage), then checks that need state (merge, totals), then the loss limit, then the human gate.
halt_and_alertis a state, not a retry. After the daily limit is hit, the loop stops opening positions until a human resets it.needs_humancovers anything unusual: a new market, a size above a review threshold, any request to remove margin, any proposal following several rejections.- Fail closed. If the price feed, position read or loss calculation errors, the answer is "reject," not "assume zero."
Where E9's proposed decision layer fits
Our earlier post, AI Trading Agents Need a Decision Layer, describes a proposed architecture: a bounded model answering constrained questions, with code enforcing hard limits and a protocol that executes only authorized actions. It is a design discussion, not a live LeverUp feature or integration. The checks in this post are the "code for hard limits" part of that picture. Whatever sits upstream, whether a large model, a small decision model or a person, these gates sit downstream of it and apply the same way.
What comes next
Scoped keys decide what an agent may do. Guardrails decide how much of it can happen. The loss limit should include accrued fees; the next posts examine those costs and longer holding periods in more detail. Post six covers funding and holding costs for an always-on agent, and post seven covers what happens when positions are held overnight.
Closing
A guardrail is a number the model cannot change, checked by code the model does not run. The arithmetic in this post is assumed and the mechanics come from LeverUp's documentation, so read the liquidation and opening-a-position pages directly and confirm current parameters before encoding them.
This post is educational and is not financial advice. Trading perpetuals with leverage carries risk, including loss of your entire margin.
LeverUp uses a protocol-managed virtual liquidity system powered by the VMMV, where trades reference oracle pricing while execution, settlement, and risk management are handled at the protocol layer. See the documentation on VMMV settlement, protocol risk controls, and liquidation.