Skip to content

Reconciliation, Risk & Exchange Consistency

This page documents how the discretionary runtime keeps its local trading state aligned with Hyperliquid while still responding quickly enough for realtime agent use, and how the risk engine bounds new entries and adapts to drawdown.

The discretionary runtime uses a hybrid model:

  • local runtime state for low-latency decision-making
  • exchange sync and reconciliation for correction and validation
  • backend/services/autodiscretionary/reconciliation.ts
  • backend/services/autodiscretionary/sync-worker.ts
  • backend/services/autodiscretionary/trader.ts
  • backend/services/autodiscretionary/runtime-snapshot.ts
  • backend/services/autodiscretionary/risk-engine.ts

Reconciliation is session-scoped — bound to a single agent wallet’s HL account.

Typical phases include:

  • checking
  • reconciling
  • verifying
  • consistent
  • failed

Why session-scoped: the discretionary agent is 1-to-1 with an agent wallet. Each agent wallet operates its own isolated HL portfolio. Reconciliation cannot cross sessions, because each session reconciles a different on-chain account.

Every discretionary trade requires:

  • stop-loss
  • take-profit reference (heuristic_target)
  • conviction (0–100)
  • absolute_risk_usd — maximum USD loss if the stop is hit

Position size is derived: size = absolute_risk_usd / |entry − SL|.

A trade is rejected if either cap is breached:

  • cap_portfolio — sum of all absolute_risk_usd across all markets
  • cap_per_market — sum of absolute_risk_usd within a single market

Per-Market Cap Is A Percentage Of The Portfolio Cap

Section titled “Per-Market Cap Is A Percentage Of The Portfolio Cap”

Configuration:

  • maxPortfolioRiskUsd — the portfolio cap, in USD (e.g. $500)
  • maxIndividualRiskPct — the per-market cap, expressed as a percentage of the (effective) portfolio cap (e.g. 20)

The per-market cap is derived dynamically as:

effectiveIndividualCap = effectivePortfolioCap × (maxIndividualRiskPct / 100)

So $500 portfolio × 20%$100 per-market cap. If the effective portfolio cap shrinks (drawdown penalty, account-level cap clipping), the per-market cap shrinks proportionally — the agent never has to reconfigure both.

Realised losses subtract from the effective portfolio cap as a decaying penalty:

  • A loss adds to cumulativeLoss
  • penalty = cumulativeLoss × (1 − minutesElapsed / lossDecayMinutes) (clamped at zero)
  • effectivePortfolioCap = max(0, maxPortfolioRiskUsd − penalty)
  • effectiveIndividualCap = effectivePortfolioCap × (maxIndividualRiskPct / 100)

The penalty decays linearly to zero over lossDecayMinutes. The runtime exposes penaltyUsd and decayRemainingMinutes to the agent so it can reason “I’m capped lower than nominal, but the cap will recover in N minutes.”

When equity drops below the configured lockout threshold, new entries are blocked at the exchange layer — not by the agent, by the runtime. The agent sees a DRAWDOWN LOCKOUT ACTIVE section in its context and is expected to focus on managing existing positions (tightening SLs, reducing, locking in profit). The lockout lifts when equity recovers above the threshold or the user manually resets it.

When a stop is trailed past entry (longs: SL above entry; shorts: SL below entry), the position is risk-free. It contributes $0 to both caps. Locked-in unrealised profit is surfaced as total_covered_unrealised_profit so the agent can decide whether to hold or close to free margin.

Cap checks are risk-USD-based. The exchange can still reject with Insufficient margin if notional × (1 / max_leverage) exceeds available margin.

If execute_trade_setup fails with Insufficient margin, the agent should:

  1. reduce absolute_risk_usd, or
  2. widen the SL (smaller size at the same risk), or
  3. close a lower-conviction existing position to free margin.

The session-scoped runtime identity is:

runtimeScope = `session:${sessionId}`

This is the core isolation boundary for multi-agent-per-user support. Each discretionary session:

  • is bound to one agent wallet (apiWalletId)
  • owns its own Redis state (positions, trade ledger, alerts, drawdown, equity peak, market conditions, risk config)
  • runs its own reconciliation worker
  • runs its own auto-risk worker, time-alert scheduler, and HL bridge

This prevents one agent wallet from correcting another wallet’s state under the same user, and prevents portfolio risk on one wallet from constraining trades on another.

Each turn, the discretionary system prompt is built with a structured Portfolio Risk block:

{
"active_risk_usd": 38.50,
"total_risk_usd": 38.50,
"headroom_usd": 11.50,
"market_risk_cap": 50.00,
"cap_portfolio": 50.00,
"active_trades": 2,
"drawdown_penalty_usd": 12.00,
"penalty_decays_in_minutes": 18,
"static_cap_portfolio": 62.00,
"per_market_risk": [
{ "market": "ETH/USDC:USDC", "risk_usd": 22.00, "pct_of_market_risk_cap": 44, "headroom_usd": 28.00, "trades": 1 },
{ "market": "SOL/USDC:USDC", "risk_usd": 16.50, "pct_of_market_risk_cap": 33, "headroom_usd": 33.50, "trades": 1 }
],
"auto_risk": {
"portfolio_bounds": "$50 — $2000",
"per_market_risk_pct": "20%"
}
}

Note that per_market_risk_pct is the configured percentage, while market_risk_cap is the derived USD value at the current effective portfolio cap.