Skip to content

Backtest

Backtest takes a Hyperliquid address and replays its real trading history twice: once as it happened, once behind your rules. You see both equity curves and every trade Guard would have refused or resized.

It runs in your browser. The rules are judged by Zunder’s risk engine compiled to WebAssembly (crates/zunder-risk-wasm), the same Rust code that sizes Zunder’s own orders.

From Hyperliquid’s public info endpoint, for the address you enter: fills (userFillsByTime), funding payments (userFunding), order history with trigger orders (historicalOrders), current state (clearinghouseState), open orders, and the PnL history (portfolio). At most 10,000 fills per replay. No wallet connection, no signature, no key.

The address stays in the requests to Hyperliquid. It is not stored, logged, put in a URL, or sent to us.

The page is prefilled with a public Hyperliquid vault as an example. Enter any address.

Two curves come out of one pass over the history:

  • Actual: starting equity, plus closed PnL, minus fees, plus funding, exactly as Hyperliquid recorded them.
  • Guarded: the same trades behind your rules. Each entry is judged against the guarded account (its equity, its positions, the account’s stops). A refused entry is skipped; a resized one is scaled. Exits follow the account’s exits in proportion. A real RiskEngine observes the guarded equity at every event: after a daily loss stop nothing opens until the next UTC day; after a drawdown halt nothing opens again.

Every fill is also judged against the account as it really was. That is the audit: how many entries Guard would have allowed, resized or refused, and how many had a stop at all.

More on the method: How the honest backtest works.

A bot traded 400 entries in 30 days, 120 of them without any stop. With the default rules, Backtest refuses those 120 (no_protective_stop), resizes the ones that risked more than 2% at their stop, and shows where the guarded curve would have halted for the day. The page lists each refused trade with its rule.

(This is an illustration of the output, not a result.)

The page lists these assumptions next to every result:

  1. It is a what-if. The account’s later decisions are kept as they were, although skipping or shrinking a trade could have changed them.
  2. Equity is realised. Open positions are not marked to market, so a loss inside a trade shows when it closes, and the daily loss stop and drawdown halt see realised equity only.
  3. Deposits, withdrawals and transfers are left out of both curves.
  4. No stop is invented. A loss is capped at a stop only where the account had one.
  5. Fees and funding of guarded trades are the recorded ones, scaled to the guarded size.
  6. After a halt, guarded positions close at the account’s next fill price in that coin, or at the last price seen.
  7. Liquidation prices of past positions are unknown, so rule (d) is not judged.
  8. Other positions are valued at the last fill price seen in their coin.
  9. Stops placed late: a trigger order counts as an entry’s stop if it rested at the entry or was placed within 60 s after it.
  10. Old entries: where the order history does not reach back far enough, entries are kept as traded and counted as not judged.
  • A guarded curve above the actual one says “would have lost less on this history”. It never says “would have made more” as a promise.
  • Results include fees and funding as Hyperliquid recorded them.
  • Nothing is extrapolated beyond the window you chose.