# About HelloTrade

## Overview

HelloTrade is a self-custody brokerage and trading platform for global markets, running on Monad.

We’re building one place to trade 10,000+ markets onchain – stocks, crypto, commodities, ETFs and FX – with up to 50× leverage, 24/7.

The platform is mobile-first with institutional-grade performance.

## Access

We are already live in closed alpha with a core set of initial markets to stress test the platform.

We are opening access deliberately, onboarding enough traders to put real volume through the system before progressing to beta and later, public launch.

To get early access:

1. Join the waitlist at [hello.trade](https://www.hello.trade/)
2. Follow us on [X (Twitter)](https://x.com/hellotradeapp)\
   (Turn notifications on for invite code drops)

## Architecture

HelloTrade employs a hybrid architecture combining the performance of centralized exchanges with the security of decentralized settlement and custody:

* **Performance:** Extremely performant matching engine with <10ms latency on order cancellations
* **On-Chain Settlement**: Non-custodial settlement where all margin operations and positions are tracked on-chain in smart contracts
* **Multi-Protocol Access**: WebSocket and REST interfaces

This design ensures institutional-grade performance while maintaining the transparency and security guarantees of blockchain-based settlement.

## Markets & Liquidity

### 24/7 Trading

Markets trade continuously, even when underlying assets are closed. The exchange operates in different modes based on market conditions:

* **Normal hours**: Full liquidity with standard pricing mechanisms
* **Scheduled closures**: Weekend and holiday trading with price bands to limit volatility
* **Market halts**: Adaptive response to LULD halts, news events, and regulatory actions
* **Corporate events**: Handling of stock splits, M\&A, earnings, and delisting events

## Risk Management

The platform uses a cross-margin system with a single USDM collateral pool supporting positions across all instruments. Leverage up to 50x is available.

A multi-layer liquidation system protects the exchange and its participants:

1. Order book liquidation
2. Liquidation queue with vault participation
3. Insurance fund
4. Auto-deleveraging (ADL) as a final backstop


# Onboarding


# How to Start Trading

### What do I need in order to trade on HelloTrade?

You can trade on Hello Trade with a normal DeFi wallet or by logging in with your Google or Apple account.

If you choose to use a normal DeFi wallet, it would require:

1. An EVM wallet
   * If you don’t already have an EVM wallet (e.g., Rabby, MetaMask, WalletConnect, Coinbase Wallet).
   * After downloading a wallet extension for your browser, create a new wallet.
   * **Reminder:** Your wallet has a secret recovery phrase. Anyone with access to your password or seed phrase can access your funds. **Do** **not** **share** your private key with anyone.
2. Collateral
   * USDC and ETH (gas to deposit) on Arbitrum, or
   * BTC on Bitcoin, ETH/ENA on Ethereum

### **How do I onboard to HelloTrade?**

If you choose to log in to HelloTrade with Google or Apple account

1. Click the "Continue with Google" or “Continue with Apple” button and enter your account information, and you will be redirect to Hello Trade after you successfully signed in.
2. Now you're connected. All that's left is to deposit. A new blockchain address is created for your account. You can send USDC over Arbitrum, BTC on Bitcoin, ETH/ENA on Ethereum. It’s easy to do from a centralized exchange or another DeFi wallet.

### How do I trade perpetuals on HelloTrade?

With perpetual contracts, you use USDC as collateral to long or short the asset instead of buying the asset itself, like in spot trading.

1. Using the asset selector, choose an asset that you want to open a position in.
2. Decide if you want to long or short that asset. If you expect the price to go up, you want to long. If you expect the token price to go down, you want to short.
3. Use the slider or type in the size of your position. Position size = your leverage amount \* your collateral
4. Lastly, click Place Order. Click Confirm in the modal that appears. You can check the “Don’t show this again” box so you don’t have to confirm each order in the future.


# How to Use HelloTrade


# Trading


# Perpetual Contracts

Hello Trade currently supports trading of the below assets. Assets are added according to community input.

## Available Instruments

<table data-full-width="true"><thead><tr><th width="99.21484375">ID</th><th>Symbol</th><th>Max Leverage</th><th>Qty Step</th><th width="113.01953125">Price Precision</th><th width="94.0703125">OI Cap (m)</th></tr></thead><tbody><tr><td>4</td><td>BTC</td><td>40x</td><td>0.00000001</td><td>8</td><td>4</td></tr><tr><td>5</td><td>QQQ</td><td>30x</td><td>0.00001</td><td>10</td><td>6</td></tr><tr><td>8</td><td>NVDA</td><td>20x</td><td>0.00001</td><td>10</td><td>2</td></tr><tr><td>9</td><td>SPY</td><td>50x</td><td>0.00001</td><td>10</td><td>6</td></tr><tr><td>13</td><td>ETH</td><td>25x</td><td>0.0000000001</td><td>10</td><td>4</td></tr><tr><td>14</td><td>SOL</td><td>20x</td><td>0.0000000001</td><td>10</td><td>4</td></tr><tr><td>15</td><td>TSLA</td><td>10x</td><td>0.00001</td><td>10</td><td>2</td></tr><tr><td>16</td><td>EWY</td><td>20x</td><td>0.00001</td><td>10</td><td>2</td></tr><tr><td>17</td><td>XAG</td><td>25x</td><td>0.00001</td><td>10</td><td>2</td></tr><tr><td>18</td><td>XAU</td><td>25x</td><td>0.00001</td><td>10</td><td>6</td></tr><tr><td>19</td><td>AMZN</td><td>20x</td><td>0.00001</td><td>10</td><td>2</td></tr></tbody></table>

**Notes:**

* **ID**: Instrument ID used in EIP-712 signatures (market field)
* **Max Leverage**: Maximum allowed leverage multiplier
* **Qty Step**: Minimum quantity increment for orders
* **Price Precision**: Decimal places supported for prices
* **OI Cap**: Maximum open interest cap in USD notional value


# Trading Fees

Trading fees were set to zero during the first stage of the HelloTrade alpha.

Now that Alpha 2.0 is live, Growth Mode fees will soon take effect. These fees are competitive and lower than typical CEX fees.

As we progress to Beta and beyond, new fee schedules will be introduced.

### Fee Tiers - Growth Mode Alpha

<table><thead><tr><th width="74.37890625" align="center">Tier</th><th width="177.109375" align="center">14-day Perpetual Futures Volume ($)</th><th width="207.7578125" align="center">Maker Fees</th><th width="225.734375" align="center">Taker Fees</th></tr></thead><tbody><tr><td align="center">0</td><td align="center">≥ $0</td><td align="center">0.000% (0 bps)</td><td align="center">0.020% (2 bps)</td></tr><tr><td align="center">1</td><td align="center">≥ $5m</td><td align="center">0.000% (0 bps)</td><td align="center">0.018% (1.8 bps)</td></tr><tr><td align="center">2</td><td align="center">≥ $10m</td><td align="center">0.000% (0 bps)</td><td align="center">0.016% (1.6 bps)</td></tr><tr><td align="center">3</td><td align="center">≥ $50m</td><td align="center">0.000% (0 bps)</td><td align="center">0.014% (1.4 bps)</td></tr><tr><td align="center">4</td><td align="center">≥ $500m</td><td align="center">0.000% (0 bps)</td><td align="center">0.012% (1.2 bps)</td></tr><tr><td align="center">5</td><td align="center">≥ $1bn</td><td align="center">0.000% (0 bps)</td><td align="center">0.010% (1 bps)</td></tr></tbody></table>

Tier 0 is the rate charged at execution. Higher tiers are applied as a daily rebate of the difference, paid to your account the following day.


# Order Types

Understanding different order types and how they execute in the market.

Every order carries a price bound: each type below has an explicit worst-case price that is signed and margined exactly. There are no unpriced order types (market) — for immediate execution, use a crossing Limit IOC with your slippage tolerance as the limit price.

## Order Type Execution Behavior

* **Limit** — Executes at your specified price or better.
  * To trade immediately (market-style execution), place a **crossing Limit IOC**: set the limit price at your maximum acceptable slippage (e.g. buy with limit = mark × 1.01 for a 1% bound). It fills immediately up to your bound and cancels any remainder — speed of a market order, with a guaranteed worst-case price.
  * Supports an optional `reduceOnly` flag (see below).
* **Stop-Limit** — Becomes a limit order when the market reaches your stop (trigger) price. Requires both `stopPrice` and `limitPrice`.
* **Stop-Loss** — Closes (part of) your position when the market moves against you. Places as a Stop-Limit: requires a `stopPrice` (trigger) **and** a `limitPrice` (worst acceptable execution price).
* **Take-Profit** — Closes (part of) your position at a profit target: becomes a limit order when the market touches your trigger price. Requires a `stopPrice` (trigger) **and** a `limitPrice` (worst acceptable execution price).

### Reduce-Only Orders

`reduceOnly: true` marks an order that may only ever **reduce** your position — it can never open one, flip it, or grow it. Supported on **Limit, Stop-Limit, Stop-Loss and Take-Profit**, resting or immediate.

Requirements:

* You must hold an open position in that instrument.
* The order must be on the opposite side to the position.
* Quantity must not exceed the position size.
* Where several reduce-only orders would be reached by the same price move, their combined quantity must not exceed the position either. A stop-loss below the market and a take-profit above it are not combined — no single move reaches both — so you may hold both at full size.

A reduce-only order may rest on the book. It is kept in line with your position as that position changes, and cancelled once the position is closed or flipped.

### Post-Only Orders

`instructions: ["PostOnly"]` marks an order that only adds liquidity. If it would match immediately, it is cancelled. Supported on **Limit** and **Stop-Limit** with `GTC`, `GTD` or `DAY`.

**Stop-Loss & Take-Profit Modes:**

SL/TP orders support two modes controlled by the `closePosition` parameter:

**Managed Orders** (`closePosition: true`):

* Quantity automatically syncs with position size as it changes
* Only one managed SL and one managed TP allowed per instrument
* One-Cancels-Other (OCO): when managed SL or TP fills, the other is automatically cancelled
* Automatically cancelled if position closes or direction flips
* Cannot specify quantity - server manages it based on current position
* Requires either an open position, or a resting entry order on the same instrument to attach to. It can only reduce a position — it can never open one.

**Unmanaged Orders** (`closePosition: false`):

* Fixed quantity specified by you (like regular orders)
* Multiple unmanaged SL/TP orders allowed per instrument
* **Requires an open position.** Place the position first, then attach the trigger.
* Reduce-only: it can only close part or all of your position — it can never open or flip one.
* The quantity you set is an upper bound: the order is kept in line with your position as it changes, and cancelled once the position closes or flips.

### Attaching SL/TP to an entry order

There is no native bracket / OTOCO order type, and no OCO between an entry order and its SL/TP — OCO exists only between a managed SL and a managed TP. What you can do:

* **Managed** SL/TP may be placed while an entry order is still resting. They stay inert until the entry fills, then size themselves to the resulting position.
* **Unmanaged** SL/TP require the position to exist first, so place them after the entry fills.

Neither can open a position on its own, so an entry that never fills cannot leave you with a trigger that opens one. If you cancel the entry order, cancel any managed SL/TP you attached to it.

***

## Time in Force

Controls how long an order remains active:

* **GTC (Good-Til-Canceled)** - Remains active until filled or manually canceled
* **IOC (Immediate-or-Cancel)** - Fill immediately, cancel any unfilled portion
* **FOK (Fill-or-Kill)** - Fill entire order immediately or cancel entirely
* **GTD (Good-Til-Date)** - Expires at specified `expiryDate` timestamp
* **DAY** - Expires at end of trading day 00:00:00 UTC

***

## Order Type Compatibility Matrix

<table><thead><tr><th>Type</th><th width="106.60546875">GTC</th><th width="122.52734375">GTD</th><th width="116.08203125">FOK</th><th width="129.2578125">IOC</th><th>DAY</th></tr></thead><tbody><tr><td>Limit</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td></tr><tr><td>StopLimit</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td></tr><tr><td>StopLoss</td><td>❌</td><td>❌</td><td>✔️</td><td>✔️</td><td>❌</td></tr><tr><td>TakeProfit</td><td>❌</td><td>❌</td><td>✔️</td><td>✔️</td><td>❌</td></tr></tbody></table>

***

## Required Price Fields

| Type       | `limitPrice` | `stopPrice` |
| ---------- | ------------ | ----------- |
| Limit      | required     | —           |
| StopLimit  | required     | required    |
| StopLoss   | required     | required    |
| TakeProfit | required     | required    |


# Pricing

## Overview

HelloTrade employs a sophisticated pricing system that adapts to market conditions. The system distinguishes between **Index Price** and **Mark Price**, each serving specific purposes in the trading and risk management lifecycle.

Both Mark and Index Prices are computed from multiple data providers:

* Chainlink
* Stork
* Massive.io
* Direct exchange and ATS feeds

***

## Index Price

The index price represents the fair value of the underlying asset based on external market data.

### Computation

**Normal Market Hours (Equity 24/5, Crypto 24/7):**

Index price is computed as a **weighted median** of prices from multiple data providers:

Weights are distributed evenly across sources and updated as needed based on data quality and availability.

**Scheduled Market Closures (Weekends & Holidays):**

During scheduled closures (8PM Friday - 8PM Sunday for equities):

1. Index price is **initialized at the closing price** when markets close
2. Updates incrementally based on the **impacted price difference (IPD)** with a 1-hour exponentially weighted moving average time constant.
   * `IPD = max(Impact Bid - Index, 0) - max(Index - Impact Ask, 0)`

### Oracle Publishing

The index price is computed off-chain by the data engine and published to an on-chain oracle network for validation and synchronization across systems.

***

## Mark Price

The mark price is used for:

* Position valuation
* Unrealized PnL calculation
* Liquidation price determination
* Margin calculations

### Computation

**Normal Market Hours:**

Mark price is the **median** of the following three components:

1. **Index price adjusted for funding**

   ```
   Index Price × (1 + Hourly Funding Rate × (Time Until Next Funding / Funding Period))
   ```
2. **Median of order book data**

   ```
   median(Best Bid, Best Ask, Last Trade)
   ```
3. **Index price with market premium/discount**

   ```
   150s EMA(Index Price + IPD)
   ```

**Scheduled Market Closures:**

Mark price uses the **same candidates as normal hours** (see above). A **price band** clamps the mark price to prevent extreme deviations from the market close reference price.

### Oracle Publishing

Mark price is computed off-chain per-second and published to an on-chain oracle to validate liquidations and ensure system synchronization.

***

## Exchange Modes & Banding

HelloTrade operates in different modes based on underlying market conditions.

### Mode 1: Normal Operation (Liquid Markets)

**Market Hours:**

* Equities: 24/5 (Sunday 8PM - Friday 8PM)
* Crypto: 24/7

**Characteristics:**

* Index price from live feeds
* Standard mark price computation
* Funding distributed hourly

### Mode 2: Scheduled Market Closures

**When:** Weekends (8PM Friday - 8PM Sunday), Holidays

**Characteristics:**

* Index price anchored to closing price, updated via EMA of book price
* Same mark price candidates as normal hours
* **Strict price bands**: +/- 10% from 8PM reference price
  * Prevents market manipulation
  * Limits forced liquidations
  * Caps directional risk for makers
* Funding distributed every hour

### Mode 3: Unscheduled Halts

**Trading Halts (LULD, News Pending, Regulatory):**

When programmatic halt feeds detect market disruptions:

* Tighten open interest caps
* Switch to secondary feeds or fallback pricing
* Maintain trading where possible
* Switch to hourly funding if halt exceeds 1 hour

**Common Halt Types:**

| Halt Type                        | Duration | Frequency      | DEX Response                          |
| -------------------------------- | -------- | -------------- | ------------------------------------- |
| LULD pause                       | \~5 min  | 80% of halts   | Default (tighten OI, secondary feeds) |
| News pending/released            | \~15 min | 14% of halts   | Default                               |
| Regulatory concern               | Varies   | 1% of halts    | Default                               |
| ETF indicative value unavailable | \~5 min  | <0.1% of halts | Default                               |

### Mode 4: Scheduled Events

<table><thead><tr><th width="269.57421875">Event</th><th>DEX Handling</th></tr></thead><tbody><tr><td><strong>Stock Split</strong></td><td>Temporary halt. Adjust index price and re-denominate position sizes.</td></tr><tr><td><strong>Mergers &#x26; Acquisitions</strong></td><td>Temporary halt. Case-by-case: adjust pricing or delist asset.</td></tr><tr><td><strong>New Security Offering</strong></td><td>Switch price feed when live feeds become available (Pre-IPO only).</td></tr><tr><td><strong>Bankruptcy / Delisting</strong></td><td>ADL and close all positions at last reliable price. Delist from trading.</td></tr><tr><td><strong>Dividend Payment</strong></td><td>No pass-through to position holders. Spot reference price accounts for fair value adjustment.</td></tr><tr><td><strong>Earnings Announcements</strong></td><td>Reject new market and limit orders from takers 5 minutes before scheduled calls.</td></tr></tbody></table>

***

## Funding Rates

Funding rates ensure perpetual contract prices converge with spot prices. Funding payments are exchanged directly between long and short positions based on the premium index (deviation between mark prices and index price).

**Key Parameters:**

* **Sampling:** Every second
* **Distribution:** Every hour
* **Rate Caps:** Maximum ±1% per hour

See [Funding](/about/trading/funding) for detailed formulas, calculation methodology, and examples.

***


# Liquidation

## Overview

Liquidation occurs when an account's margin ratio falls below the maintenance margin requirement. The system automatically closes positions to prevent negative equity and protect users.

Liquidation is evaluated **per margin scope**: cross positions are assessed together against the shared collateral pool, while an isolated position is assessed against only the margin dedicated to it — an isolated liquidation cannot touch cross collateral or any other position's isolated margin.

All liquidation calculations use **mark price** to ensure fair and manipulation-resistant valuations.

***

## Margin Requirements

### Opening Margin

Minimum margin required to open a position:

```
Opening Margin = Position Notional / Leverage
```

Opening margin ensures traders have sufficient capital to support new positions.

### Maintenance Margin

Minimum margin required to maintain open positions:

```
Maintenance Margin = |Position Size| × Mark Price / (2 × Max Leverage)
```

With 10x maximum leverage, maintenance margin = 5% of position notional value.

**Example:**

* Position: 100 NVDA at $500 mark price
* Position Notional: 100 × $500 = $50,000
* Max Leverage: 10x
* Maintenance Margin: $50,000 / (2 × 10) = $2,500 (5%)

## Liquidation Price

The liquidation price is the price at which an account's margin falls below the maintenance margin requirement.

### Simplified Calculation

Liquidation occurs when:

```
Account Equity < Total Maintenance Margin
```

Where:

```
Account Equity = Balance + Unrealized PnL
Unrealized PnL = Position Size × (Mark Price - Entry Price)
```

For a single position, the liquidation price can be approximated as the mark price where account equity equals maintenance margin, accounting for the entry price and current balance.

***

## Liquidation Waterfall

When an account falls below the maintenance margin requirement, positions are liquidated through a multi-step waterfall:

### 1. Order Book Liquidation

Positions are closed directly against the live order book at the best available prices.

* Minimize slippage by filling against resting limit orders
* Liquidations execute as taker orders
* Standard taker fees apply
* Orders will fill execute up until the zero-price of the positions, which is the execution price at which the account's margin ratio remains constant during liquidation.

For **LONG** positions:\
zero\_price = mark\_price × (1 - (M\_i × TAV) / MMR)

For **SHORT** positions:\
zero\_price = mark\_price × (1 + (M\_i × TAV) / MMR)

Where:

* M\_i = 1 / (2 × max\_leverage) — maintenance margin fraction
* TAV = balance + total\_unrealized\_pnl — total account value
* MMR = Σ(|size| × mark\_price × M\_i) — total maintenance margin requirement

### 2. Liquidation Auction

If order book liquidity is insufficient, the unfilled position is offered in a short on-demand auction. The gateway broadcasts the position to all subscribed trading clients, which respond with signed claims to take it over. The first claimant with sufficient margin becomes the counterparty and absorbs the position at its zero-price. If no claim is received, the position falls through to the insurance fund or ADL.

Integrators can participate via the WebSocket API — see [Liquidation Auction](/developer-tools/websocket-api/liquidation-auction).

### 3. Insurance Fund

If the liquidation queue cannot fully close positions before incurring bad debt, the insurance fund covers the shortfall.

**Insurance Fund Mechanics:**

* X% of Hello Vault capital is designated to the insurance fund
* Trading fees contribute to insurance fund reserves
* Insurance fund absorbs losses when liquidations cannot be filled profitably

### 4. Auto-Deleveraging (ADL)

If the insurance fund is depleted, profitable positions on the opposite side are automatically reduced to cover losses.

***

## Auto-Deleveraging (ADL)

ADL is the liquidation backstop when all other mechanisms fail.

### ADL Ranking

Accounts are ranked by **ADL score**, which prioritizes the most profitable and most leveraged positions:

```
ADL Score = PnL Ratio × Cross Effective Leverage
```

Where:

```
PnL Ratio = Raw PnL / Equity
Cross Effective Leverage = Notional / Equity
```

And:

```
Raw PnL = Position Size × (Mark Price - Entry Price)
Equity = Balance + Total Unrealized PnL
Notional = |Position Size| × Mark Price
```

**Ranking:**

1. Highest ADL scores are deleveraged first
2. Positions are reduced pro-rata within each score tier
3. ADL continues until losses are fully socialized

### ADL Notification

When ADL occurs, affected accounts receive an execution report with `messageType: "Liquidation"` indicating:

* Position reduction
* ADL execution price (bankruptcy price of the liquidated account)
* Reason: "Auto-deleveraging"

***

## Liquidation Process

### Step 1: Detection

The system performs periodic margin checks (every second):

```
Account Equity = Balance + Unrealized PnL
```

If `Account Equity < Total Maintenance Margin`, liquidation is triggered.

### Step 2: Account Freeze

Upon liquidation detection:

1. **Block account at exchange level** - Cancels all active orders, RFQs, and quotes
2. **Prevent new trading activity** - Local freeze prevents new user orders during liquidation

### Step 3: Position Closure

The liquidation engine places Limit IOC (Immediate-or-Cancel) orders to close all positions:

1. **Place Limit IOC orders** for each open position at zero-price
2. **Close via order book** at best available prices
3. **Auction unfilled portions to market makers** (if order book depth is insufficient)
4. **Invoke insurance fund** if positions cannot be filled above zero price
5. **Trigger ADL** if insurance fund is depleted

### Step 4: Post-Liquidation

After liquidation orders are placed:

* Account is unblocked at exchange level
* Local freeze is removed
* Account can resume trading once additional collateral is deposited

***

## Preventing Liquidation

To avoid liquidation:

1. **Monitor margin ratio** regularly
2. **Add collateral** when margin portfolio becomes high risk
3. **Reduce position size** to lower maintenance margin requirements
4. **Use stop-loss orders** to close positions before liquidation


# Funding

### Overview

HelloTrade uses funding payments to anchor perpetual and leveraged contract prices to their underlying reference prices. Funding is exchanged directly between traders holding long and short positions, ensuring that contract prices remain aligned with the mark price over time. When market demand skews toward one side, funding incentivizes traders to rebalance positions, promoting healthy and efficient markets. HelloTrade does not collect funding payments; all transfers occur peer-to-peer.

### Technical Details

The **Funding Rate (F)** on HelloTrade is calculated using a combination of the **Average Premium Index (P)** and a baseline **Interest Rate (I).** The funding rate formula applies to an 8 hour funding rate. Funding is paid each hour at one eighth of the computed rate. The formula is:

* **Funding Rate (F)** = Average Premium Index (P) + clamp(Interest Rate (I) - Average Premium Index (P), 0.05%, -0.05%)

Where:

* **Premium Index (P)** reflects the deviation between impacted market prices and the index price.
* **Interest Rate (I)** represents the cost component embedded in leveraged trading. This value is set at 0.01%.
* The **clamp** function bounds the interest rate adjustment to ±0.05% to prevent excessive funding volatility.

The **Premium Index** is defined as:

* Premium Index (P) = Impacted Price Difference / Index Price

The **Impacted Price Difference** is calculated as:

* **Impacted Price Difference** = (Max(0, Impact Bid Price - Index Price) - Max(0, Index Price - Impact Ask Price))
* **Impact Bid Price** and **Impact Ask Price** are the average execution prices required to trade a fixed impact notional on each side of the order book.
* Impact notional sizes are dynamically determined based on **average daily volume (ADV)**, **average top-of-book notional**, and **average market depth**, ensuring funding reflects real liquidity conditions.

The Premium Index is **sampled every second** and averaged across the funding interval to compute the **Average Premium Index** used in the funding rate calculation. The final funding rate is capped at ±1% per hour.

During market closures, the index tracks the orderbook via a 1-hour EMA. The premium index is additionally scaled down during closures to further reduce funding rate sensitivity during low-liquidity periods.

### Numerical Example

Assume the following values during a funding interval:

* Index Price = 100
* Impact Bid Price = 100.20
* Impact Ask Price = 99.90
* Interest Rate (I) = 0.03%

First, compute the Impacted Price Difference:

max(0, 100.20−100) − max(0, 100−99.90) = 0.20 − 0.10 = 0.10

Then calculate the Premium Index:

P = 0.10 / 100 = 0.10%

Now compute the funding adjustment:

I - P = 0.03% - 0.10% = -0.07%

After applying the clamp:

clamp(−0.07%, 0.05%, −0.05%) = −0.05%

Finally, calculate the Funding Rate:

**F = 0.10% − 0.05% = 0.05%**


# Self Trade Prevention

Self-trade prevention ensures that trades between the same account are not executed. This protects traders from paying un-necessary fees without a change in position. HelloTrade will cancel the incoming order or the maker order if it will be matched against the same user's limit (taker) order.

<br>


# Spot Trading

Coming Soon


# Getting Started

Quick start guide for integrating with the trading platform

> **⚠️ SDK Coming Soon**
>
> We are developing an official SDK that will simplify signature creation, payload encoding, and API integration. The manual integration steps below will remain supported but are intended primarily for advanced use cases. Stay tuned for SDK updates!

> **Environment**
>
> All examples in this documentation target the **production + mainnet (Monad)** environment unless otherwise specified. For testnet/staging endpoints, contracts, EIP-712 domains, and instrument IDs, see [Supported Chains](/developer-tools/supported-chains) and [Supported Assets](/developer-tools/supported-assets).
>
> See the [Staging Environment Guide](https://github.com/perps-core/rust-services/tree/main/external-docs/developer-tools/staging-environment.md) *(coming soon)* for staging + testnet API documentation. Staging APIs are backward compatible.

## Overview

The trading platform provides gasless perpetual futures trading using EIP-712 and EIP-191 cryptographic signatures. All operations are authenticated with wallet signatures - no API keys or passwords required.

**Key Features:**

* Gasless trading (no gas fees for orders)
* One wallet = one account (auto-created)
* WebSocket for real-time trading and execution reports
* REST API for account management and historical data
* Public market data feed (no authentication required)

***

## Integration Flow

### 1. Connect to Market Data (Optional)

Public market data is available without authentication.

**Endpoint**: `wss://marketdata.app.hello.trade/ws`

Subscribe to real-time market data:

* Light tickers (prices, volumes, 24h stats)
* Partial order book
* Candles (OHLCV data)

No authentication required. See market data documentation for details.

### 2. App-Access Gate (Invite-Code Required)

The trading platform is invite-gated. A wallet that has not been granted app access cannot authenticate against trading endpoints — `POST /api/margin/deposit` (and every other authed write) returns:

```
HTTP 403
{ "error": "USER_NOT_INVITED: this wallet has not been granted app access. Apply a referral code via POST /api/referral/apply." }
```

On WebSocket trading, the same condition surfaces as error code **4015 `USER_NOT_INVITED`**.

```typescript
const res = await fetch(
  `https://api.app.hello.trade/api/user/access-status/${walletAddress.toLowerCase()}`
);
const { hasAppAccess } = await res.json() as { hasAppAccess: boolean };
```

#### Apply an invite code

If `hasAppAccess` is `false`, submit your invite code via `POST /api/referral/apply`. The endpoint is signed (viem `personal_sign` over a canonical-JSON payload that inlines the request body) and replay-protected by a unique `x-nonce` plus a 5-minute timestamp window.

On `201`, your wallet immediately has app access and can authenticate. On `422`, the response carries an `error` string — one of `ALREADY_REFERRED`, `CODE_NOT_FOUND`, `CODE_REVOKED`, `CODE_EXHAUSTED`, or `SELF_REFERRAL`.

See [REST API — POST /api/referral/apply](/developer-tools/rest-api/authenticated-endpoints#post-apireferralapply) for the full payload format, headers, error codes, and a TypeScript example.

***

### 3. Authenticate with Wallet

Sign an EIP-191 message to establish your trading session.

**WebSocket**: `wss://api.app.hello.trade/ws`

```javascript
const nonce = Date.now();
const message = `${walletAddress}:${nonce}`;
const signature = await signer.signMessage(message);

// Encode the payload (see Signatures documentation for encoding details)
const encodedPayload = encodeSimpleSignaturePayload({ account: walletAddress, nonce, deadline });

// Send via WebSocket
{
  "type": "authenticate",
  "signature": {
    "sig": signature,
    "payload": encodedPayload  // Hex-encoded with type discriminator
  }
}
```

Your account is automatically created on first authentication, provided the wallet has app access (see Step 2). See [Authentication & Account Model](/developer-tools/authentication-and-account-model).

### 4. Manage Collateral

Deposit and withdraw USDC collateral via REST API using EIP-712 signatures.

**REST API**: `https://api.app.hello.trade/api`

* Deposits use ERC-2612 permit (gasless approval + transfer)
* Withdrawals require EIP-712 signature

See [REST API - Authenticated Endpoints](/developer-tools/rest-api/authenticated-endpoints).

### 5. Trade

Place, modify, and cancel orders via WebSocket using EIP-712 signatures.

Supported order types:

* Limit, StopLimit, StopLoss, TakeProfit
* Time in force: GTC, GTD, IOC, FOK, DAY

See [WebSocket API - Trading Operations](/developer-tools/websocket-api/trading-operations).

### 6. Monitor Execution Reports

Subscribe to real-time execution reports for order updates, fills, and account events.

After `subscribeTrading`, receive:

* Order status changes
* Trade executions
* Margin operations (deposits, withdrawals, leverage updates)
* Liquidations and funding payments

See [Execution Reports](/developer-tools/websocket-api/execution-reports).

***

## API Endpoints

### Trading APIs

| Service           | Endpoint                          | Authentication  |
| ----------------- | --------------------------------- | --------------- |
| WebSocket Trading | `wss://api.app.hello.trade/ws`    | EIP-191/EIP-712 |
| REST API          | `https://api.app.hello.trade/api` | EIP-712         |

### Market Data API

| Service               | Endpoint                              |
| --------------------- | ------------------------------------- |
| WebSocket Market Data | `wss://marketdata.app.hello.trade/ws` |

**Note:** Market data does not require wallet authentication.

***

## Chain Configuration

HelloTrade runs on Monad. Use the parameters for the network you are targeting. Testnet values correspond to the staging environment.

| Parameter        | Testnet (Monad)                              | Mainnet (Monad)                              |
| ---------------- | -------------------------------------------- | -------------------------------------------- |
| Chain ID         | `10143`                                      | `143`                                        |
| Vault contract   | `0xfA6fcb77ACA7F941861F8Ee0Fb40662A5B28B77b` | `0xe52B14240514E7A05DddA336cFF0D99ce8bB7230` |
| Collateral token | `0xd1eC521d1A49A1590b6ed06ed7d96dabA24E28C1` | `0x754704bc059f8c67012fed69bc8a327a5aafb603` |

### EIP-712 Domains

EIP-712 signatures require domain-specific parameters. Use the column matching your target network.

**Vault Domain** (Orders, Withdrawals, Leverage, Cancels):

| Field               | Testnet (Monad)                              | Mainnet (Monad)                              |
| ------------------- | -------------------------------------------- | -------------------------------------------- |
| `name`              | `HelloVault`                                 | `HelloVault`                                 |
| `version`           | `1`                                          | `1`                                          |
| `chainId`           | `10143`                                      | `143`                                        |
| `verifyingContract` | `0xfA6fcb77ACA7F941861F8Ee0Fb40662A5B28B77b` | `0xe52B14240514E7A05DddA336cFF0D99ce8bB7230` |

**Token Domain** (Deposits via ERC-2612):

| Field               | Testnet (Monad)                              | Mainnet (Monad)                              |
| ------------------- | -------------------------------------------- | -------------------------------------------- |
| `name`              | `UsdcMock`                                   | `USDC`                                       |
| `version`           | `1`                                          | `2`                                          |
| `chainId`           | `10143`                                      | `143`                                        |
| `verifyingContract` | `0xd1eC521d1A49A1590b6ed06ed7d96dabA24E28C1` | `0x754704bc059f8c67012fed69bc8a327a5aafb603` |

See [Signatures](/developer-tools/signatures) for signature construction details.

***

## Key Concepts

### Precision & Encoding

| Type         | Precision           | Example                                |
| ------------ | ------------------- | -------------------------------------- |
| **Prices**   | 18 decimals         | `500.00` → `"500000000000000000000"`   |
| **USDC**     | 6 decimals          | `1000.00` → `"1000000000"`             |
| **Quantity** | Instrument-specific | `100 NVDA` → `"10000000"` (5 decimals) |
| **Leverage** | × 100               | `10x` → `1000`                         |

### Timestamps

Nonces and event timestamps use **milliseconds**; signed `deadline` fields use **seconds**:

```
Date.now() → 1705600000000
```

### Nonces

Two types of nonces:

* **Wallet nonce**: `Date.now()` for most operations
* **Token nonce**: Fetch from token contract for deposits

See [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits).

***

## Quick Reference

| Topic             | Documentation                                                                       |
| ----------------- | ----------------------------------------------------------------------------------- |
| Authentication    | [Authentication & Account Model](/developer-tools/authentication-and-account-model) |
| Signatures        | [Signatures](/developer-tools/signatures)                                           |
| WebSocket Trading | [WebSocket API](/developer-tools/websocket-api)                                     |
| REST API          | [REST API](/developer-tools/rest-api)                                               |
| Error Codes       | [Error Codes](/developer-tools/error-codes)                                         |
| Order Types       | [Trading Concepts](/about/trading/order-types)                                      |

***

## Rate Limits

| Limit                 | Value                       |
| --------------------- | --------------------------- |
| Requests per second   | 20 per authenticated wallet |
| WebSocket connections | 8 per account               |
| Idle timeout          | 30 seconds                  |

See [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits) for details.

***

## Common Data Types

| Type      | Description                          | Example                     |
| --------- | ------------------------------------ | --------------------------- |
| `Address` | Ethereum address                     | `"0x742d35Cc6634..."`       |
| `string`  | String representation of decimal/qty | `"42000.50"`                |
| `U256`    | Unsigned 256-bit integer (as string) | `"42000000000000000000000"` |
| `u64`     | Unsigned 64-bit integer              | `1705600000000`             |

***

## Support

For questions or issues, please contact support or file an issue in the documentation repository.


# Supported Chains

HelloTrade runs on Monad. Each network maps to a deployment environment: **mainnet → production**, **testnet → staging**. Use the parameters, endpoints, and EIP-712 domains for the network you are targeting.

***

## Mainnet (Monad)

Production environment.

### Network Parameters

| Parameter        | Value                                        |
| ---------------- | -------------------------------------------- |
| Chain ID         | `143`                                        |
| Vault contract   | `0xe52B14240514E7A05DddA336cFF0D99ce8bB7230` |
| Collateral token | `0x754704bc059f8c67012fed69bc8a327a5aafb603` |

### Endpoints

| Service               | Endpoint                              | Authentication  |
| --------------------- | ------------------------------------- | --------------- |
| REST API              | `https://api.app.hello.trade/api`     | EIP-712         |
| WebSocket Trading     | `wss://api.app.hello.trade/ws`        | EIP-191/EIP-712 |
| WebSocket Market Data | `wss://marketdata.app.hello.trade/ws` | Public token    |
| Public Chain RPC      | `https://rpc.monad.xyz`               | None            |

### EIP-712 Domains

**Vault Domain** (Orders, Withdrawals, Leverage, Cancels):

```json
{
  "name": "HelloVault",
  "version": "1",
  "chainId": 143,
  "verifyingContract": "0xe52B14240514E7A05DddA336cFF0D99ce8bB7230"
}
```

**Token Domain** (Deposits via ERC-2612):

```json
{
  "name": "USDC",
  "version": "2",
  "chainId": 143,
  "verifyingContract": "0x754704bc059f8c67012fed69bc8a327a5aafb603"
}
```

***

## Testnet (Monad)

Staging environment.

### Network Parameters

| Parameter        | Value                                        |
| ---------------- | -------------------------------------------- |
| Chain ID         | `10143`                                      |
| Vault contract   | `0xfA6fcb77ACA7F941861F8Ee0Fb40662A5B28B77b` |
| Collateral token | `0xd1eC521d1A49A1590b6ed06ed7d96dabA24E28C1` |

### Endpoints

| Service               | Endpoint                                  | Authentication  |
| --------------------- | ----------------------------------------- | --------------- |
| REST API              | `https://api.staging.hello.trade/api`     | EIP-712         |
| WebSocket Trading     | `wss://api.staging.hello.trade/ws`        | EIP-191/EIP-712 |
| WebSocket Market Data | `wss://marketdata.staging.hello.trade/ws` | Public token    |
| Public Chain RPC      | `https://rpc-testnet.monad.xyz`           | None            |

### EIP-712 Domains

**Vault Domain** (Orders, Withdrawals, Leverage, Cancels):

```json
{
  "name": "HelloVault",
  "version": "1",
  "chainId": 10143,
  "verifyingContract": "0xfA6fcb77ACA7F941861F8Ee0Fb40662A5B28B77b"
}
```

**Token Domain** (Deposits via ERC-2612):

```json
{
  "name": "UsdcMock",
  "version": "1",
  "chainId": 10143,
  "verifyingContract": "0xd1eC521d1A49A1590b6ed06ed7d96dabA24E28C1"
}
```

***

## Verifying the On-Chain EIP-712 Domain

Both Vault contracts implement [EIP-5267](https://eips.ethereum.org/EIPS/eip-5267) and expose an `eip712Domain()` view function that returns the canonical domain fields (`name`, `version`, `chainId`, `verifyingContract`, …) straight from the deployment. Use it to confirm the values above against the live contract.

Query it through the public chain RPC with a single `eth_call` (the `eip712Domain()` selector is `0x84b0196e`):

```bash
curl -s https://rpc.monad.xyz \
  -X POST -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_call",
    "params": [
      { "to": "0xe52B14240514E7A05DddA336cFF0D99ce8bB7230", "data": "0x84b0196e" },
      "latest"
    ]
  }'
```

The ABI-encoded result decodes to `(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions)`. For testnet, send the same call to `https://rpc-testnet.monad.xyz` with the testnet vault address (`0xfA6fcb77ACA7F941861F8Ee0Fb40662A5B28B77b`).

***

See [Signatures](/developer-tools/signatures) for signature construction details.


# Supported Assets

HelloTrade lists a different set of instruments per environment: see [Supported Chains](/developer-tools/supported-chains) for environment details. The **ID** is the instrument ID used in the EIP-712 signature `market` field and differs between environments — always use the ID for the network you are targeting.

**Column reference:**

* **ID**: Instrument ID used in EIP-712 signatures (market field)
* **Max Leverage**: Maximum allowed leverage multiplier
* **Qty Step**: Minimum quantity increment for orders
* **Price Precision**: Decimal places supported for prices
* **OI Cap**: Maximum open interest cap in USD notional value

***

## Mainnet (Monad)

Production environment.

<table data-full-width="true"><thead><tr><th width="99.21484375">ID</th><th>Symbol</th><th>Max Leverage</th><th>Qty Step</th><th width="113.01953125">Price Precision</th><th width="94.0703125">OI Cap (m)</th></tr></thead><tbody><tr><td>4</td><td>BTC</td><td>40x</td><td>0.00000001</td><td>8</td><td>4</td></tr><tr><td>5</td><td>QQQ</td><td>30x</td><td>0.00001</td><td>10</td><td>6</td></tr><tr><td>8</td><td>NVDA</td><td>20x</td><td>0.00001</td><td>10</td><td>2</td></tr><tr><td>9</td><td>SPY</td><td>50x</td><td>0.00001</td><td>10</td><td>6</td></tr><tr><td>13</td><td>ETH</td><td>25x</td><td>0.0000000001</td><td>10</td><td>4</td></tr><tr><td>14</td><td>SOL</td><td>20x</td><td>0.0000000001</td><td>10</td><td>4</td></tr><tr><td>15</td><td>TSLA</td><td>10x</td><td>0.00001</td><td>10</td><td>2</td></tr><tr><td>16</td><td>EWY</td><td>20x</td><td>0.00001</td><td>10</td><td>2</td></tr><tr><td>17</td><td>XAG</td><td>25x</td><td>0.00001</td><td>10</td><td>2</td></tr><tr><td>18</td><td>XAU</td><td>25x</td><td>0.00001</td><td>10</td><td>6</td></tr><tr><td>19</td><td>AMZN</td><td>20x</td><td>0.00001</td><td>10</td><td>2</td></tr></tbody></table>

***

## Testnet (Monad)

Staging environment.

<table data-full-width="true"><thead><tr><th width="99.21484375">ID</th><th>Symbol</th><th>Max Leverage</th><th>Qty Step</th><th width="113.01953125">Price Precision</th><th width="94.0703125">OI Cap (m)</th></tr></thead><tbody><tr><td>7</td><td>ETH</td><td>10x</td><td>0.0000000001</td><td>10</td><td>100</td></tr><tr><td>8</td><td>NVDA</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>12</td><td>TSLA</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>13</td><td>BTC</td><td>10x</td><td>0.00000001</td><td>8</td><td>1000</td></tr><tr><td>15</td><td>SPY</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>16</td><td>MSTR</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>17</td><td>BMNR</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>18</td><td>QQQ</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>21</td><td>TLT</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>22</td><td>IEMG</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>23</td><td>USO</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>24</td><td>EWJ</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>25</td><td>KWEB</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>36</td><td>XAU</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>38</td><td>XAG</td><td>10x</td><td>0.00001</td><td>10</td><td>1000</td></tr><tr><td>39</td><td>AMZN</td><td>20x</td><td>0.00001</td><td>10</td><td>1000</td></tr></tbody></table>

***

See [Supported Chains](/developer-tools/supported-chains) for network parameters and endpoints, and [Signatures](/developer-tools/signatures) for signature construction details.


# Authentication & Account Model

Authentication and account model specification.

## Account Model

Each Ethereum wallet address corresponds to a unique trading account. Accounts are created automatically on first authentication, provided the wallet has been granted app access.

### Account Creation

* Accounts are created automatically on first authenticated request (WebSocket authentication or HTTP request with valid signature)
* Subsequent requests from the same wallet use the existing account
* No separate registration or account creation step required
* The wallet must have **app access** before authentication will succeed (see [App Access Gate](#app-access-gate) below)

### Margin Modes

Positions are margined in one of two modes:

* **Cross** (default) — all cross positions share one collateral pool; any position's loss can draw on the entire pool. `withdrawable` (free collateral) is cross margin.
* **Isolated** — the position carries its own dedicated margin for that market. Losses are capped at the margin committed there, and a liquidation cannot touch cross collateral or any other position's margin.

Isolated margin is funded by isolated deposits or cross → isolated transfers ([`POST /api/margin/transfer`](/developer-tools/rest-api/authenticated-endpoints#post-apimargintransfer)), and leaves via isolated withdrawal or a transfer back to cross. Leverage is tracked per mode, so the same instrument can run different leverage cross vs isolated.

Writes that touch margin carry a `marginMode` field (`Cross` | `Isolated`) that must match bit 0 of the signed payload's `flags`. Reads are combined: [`GET /api/account_summary`](/developer-tools/rest-api/public-endpoints#get-apiaccount_summary) covers cross and isolated in one response, with positions tagged by `marginMode`.

***

## App Access Gate

The trading platform is invite-gated. A wallet that has not applied a valid referral code cannot authenticate.

### How the gate surfaces

| Surface                                             | Response when gated                                      |
| --------------------------------------------------- | -------------------------------------------------------- |
| REST authed write (e.g. `POST /api/margin/deposit`) | `HTTP 403` with body `{"error":"USER_NOT_INVITED: ..."}` |
| WebSocket trading authentication                    | Error code **4015 `USER_NOT_INVITED`**                   |

Always parse the `USER_NOT_INVITED:` prefix rather than the freeform tail — the tail is human-readable and may change.

### Endpoints

| Method | Path                                     | Auth   | Purpose                                                                                                                                                                                                               |
| ------ | ---------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/api/user/access-status/:walletAddress` | public | `{ hasAppAccess: boolean }`. Single source of truth for the gate predicate.                                                                                                                                           |
| `POST` | `/api/referral/apply`                    | signed | Submit an invite code. See [REST API — POST /api/referral/apply](/developer-tools/rest-api/authenticated-endpoints#post-apireferralapply) for the full request/response spec, signed payload format, and error codes. |

***

## Authentication

WebSocket connections must authenticate before performing trading operations.

### Authentication Flow

1. Connect to WebSocket endpoint
2. Send `authenticate` message with EIP-191 signature
3. Receive `authenticateSuccess` response
4. Connection is authenticated for trading operations

### Signature Requirements

Authentication uses EIP-191 (`personal_sign`). See [Signatures - EIP-191 Operations](/developer-tools/signatures#eip-191-operations) for signature construction.

Message format: `{wallet_address}:{nonce}`

All authentication signatures are validated for:

* Signature validity (recovered address must match claimed address)
* Nonce validity (5-minute time window)
* Nonce uniqueness (replay protection)
* Rate limiting (20 requests/second per authenticated wallet)

See [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits) for validation details.

***

## Connection State

### Authenticated

After successful authentication, connections can:

* Place, replace, and cancel orders
* Subscribe to private execution reports
* Manage margin and leverage

### Unauthenticated

Before authentication, connections can only:

* Authenticate
* Access public market data (if available)

### Logout

Send `logout` message to clear authentication state. Connection returns to unauthenticated state.

***

## Connection Limits

| Limit                       | Value      |
| --------------------------- | ---------- |
| Max connections per account | 8          |
| Idle timeout                | 30 seconds |
| Ping interval               | 15 seconds |

Connections exceeding idle timeout are automatically disconnected. Maintain connection by responding to ping frames or sending requests.

***

## Request Processing

Requests from the same wallet are processed sequentially. The system waits for each operation to complete before processing the next request from that wallet.

***

## Common Errors

| Error Code | Cause                                                                                |
| ---------- | ------------------------------------------------------------------------------------ |
| 4001       | Signature verification failed                                                        |
| 4002       | Nonce validation failed (replay, expired, or out of window)                          |
| 4003       | Connection not authenticated                                                         |
| 4004       | Signature wallet doesn't match authenticated wallet                                  |
| 4015       | Wallet not invited — apply a referral code (see [App Access Gate](#app-access-gate)) |
| 4029       | Rate limit exceeded                                                                  |
| 5000       | Connection limit exceeded (>8 connections per account)                               |
| 5003       | Failed to create or retrieve account                                                 |

For complete error code reference and troubleshooting guidance, see [Error Codes](/developer-tools/error-codes).

***

## API Reference

For detailed request and response formats, see:

* [WebSocket API](/developer-tools/websocket-api) - WebSocket message specifications
* [REST API](/developer-tools/rest-api) - HTTP endpoint specifications


# Nonce & Rate Limits

Nonce generation and rate limiting specifications for request authentication and replay attack prevention.

## Overview

Every signed request requires a nonce (millisecond timestamp). The system validates nonces using time window validation, replay prevention, and rate limiting.

***

## Nonce Types

### Wallet Nonce

**Used for:** Orders, Withdrawals, Leverage Updates, Authentication, Cancel Orders

* **Format**: Millisecond timestamp as `uint256`
* **Generation**: Current Unix time in milliseconds

```typescript
const nonce = Date.now();
```

```rust
let nonce = U256::from(chrono::Utc::now().timestamp_millis() as u64);
```

Each wallet nonce can only be used once. Generate a fresh timestamp for each operation.

### Token Contract Nonce

**Used for:** ERC-2612 Permit signatures (deposits only)

* **Format**: Sequential `uint256` managed by token contract
* **Source**: Fetch from token contract before signing

```typescript
const tokenNonce = await tokenContract.nonces(walletAddress);
```

```rust
let token_nonce: U256 = token_contract.nonces(wallet_address).call().await?;
```

The nonce for Permit signatures must be fetched from the token contract. Using `Date.now()` for deposit nonces will cause validation failure.

***

## Wallet Nonce Validation

### Validation Rules

| Rule                 | Value             | Description                                           |
| -------------------- | ----------------- | ----------------------------------------------------- |
| **Time Window**      | 5 minutes         | Nonce must be within 5 minutes of current server time |
| **Future Tolerance** | 1 minute          | Tolerance for clock skew between client and server    |
| **Server Restart**   | Server start time | Nonce must be after server restart                    |
| **Uniqueness**       | Per wallet        | Each nonce can only be used once per wallet           |

**Valid nonce range:**

```
current_time - 5 minutes ≤ nonce ≤ current_time + 1 minute
```

***

## Rate Limiting

Two independent limits apply per wallet:

| Limit                     | Value                                             | Applies to                                                                             |
| ------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------- |
| **Request rate**          | 20 requests/second (rolling 1-second window)      | Wallet-authenticated requests — REST and WebSocket, including orders and subscriptions |
| **Signed request budget** | 3,000 signed requests per 5-minute rolling window | All signed operations (orders, replaces, cancels, withdrawals, leverage updates)       |

The request rate is hard-enforced over a rolling 1-second window: a request is rejected if 20 have already been made in the trailing second.

The signed request budget allows a sustained rate of \~10 signed operations/second. Each signed operation consumes one nonce; the budget frees up as nonces age out of the 5-minute window. A wallet that exhausts its budget is rejected with error code `4035 SUSTAINED_RATE_LIMIT_EXCEEDED` until older nonces expire.

**Cancels keep working:** cancel and cancel-all operations are granted 2x the signed request budget, so a wallet that has exhausted its sustained budget can still pull its resting orders.

Signed requests consume their nonce before the request rate check, so requests rejected for exceeding the request rate still count against the signed request budget.

### Unauthenticated read endpoints

Read endpoints that take a wallet as a query parameter — account summary, open orders, max order size, required margin, funding history, trade/order reporting, and PnL history — are **not** subject to the per-wallet request rate. They carry no signature, so anyone could name any wallet, and a per-wallet limit on them would let one caller consume another wallet's allowance.

These endpoints are rate limited **per client IP at the edge instead**. Treat them as a shared budget across everything you call from one address, and prefer polling them no faster than your UI actually needs. `max_order_size` in particular is a search over placement validation rather than a cached read: call it when a user asks for a maximum, not on a timer. If you know the size you want and need the collateral for it, use `required_margin` — it is a single evaluation and far cheaper.

***

## Common Errors

| Error                                         | Cause                                 | Solution                                                                                       |
| --------------------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `Nonce too old (outside 300000ms window)`     | Nonce older than 5 minutes            | Generate fresh timestamp                                                                       |
| `Nonce too far in future`                     | System clock ahead of server          | Synchronize system clock                                                                       |
| `Nonce already used (replay attack detected)` | Nonce reused                          | Generate new nonce                                                                             |
| `Nonce predates server start`                 | Nonce from before server restart      | Generate new nonce                                                                             |
| `Rate limit exceeded` (4029)                  | More than 20 requests/second          | Reduce request rate                                                                            |
| `Sustained request rate exceeded` (4035)      | Signed request budget exhausted       | Back off; budget frees as nonces age past 5 minutes. Cancels have 2x headroom and keep working |
| `Order not found or already closed` (4041)    | Cancel/replace raced a fill or cancel | Benign — treat as already-closed, do not retry or alert                                        |
| `Signature verification failed` (deposits)    | Incorrect token contract nonce        | Fetch fresh nonce from contract                                                                |

For complete error code reference, see [Error Codes](/developer-tools/error-codes).


# Signatures

EIP-712 and EIP-191 signature specifications for gasless trading operations.

> **⚠️ SDK Coming Soon**
>
> We are developing an official SDK that will simplify signature creation and payload encoding. The manual signature construction detailed below will remain supported but is intended primarily for advanced integrations. Stay tuned for SDK updates!

All trading operations require cryptographic signatures using Ethereum's EIP-712 (structured data) or EIP-191 (simple message) standards.

## Signature Standards

| Operation           | Standard | Struct                   | Domain |
| ------------------- | -------- | ------------------------ | ------ |
| Place/Replace Order | EIP-712  | `Order`                  | Vault  |
| Cancel Order        | EIP-712  | `OrderCancel`            | Vault  |
| Deposits (Permit)   | EIP-712  | `Permit` (ERC-2612)      | Token  |
| Deposits (Vault)    | EIP-712  | `Deposit`                | Vault  |
| Withdrawals         | EIP-712  | `Withdrawal`             | Vault  |
| Leverage Updates    | EIP-712  | `LeverageUpdate`         | Vault  |
| Margin Transfers    | EIP-712  | `MarginTransfer`         | Vault  |
| Authentication      | EIP-191  | `SimpleSignaturePayload` | N/A    |
| Mass Cancel         | EIP-191  | `SimpleSignaturePayload` | N/A    |

***

## EIP-712 Domains

EIP-712 signatures require environment-specific domain configurations. See [Getting Started - Chain Configuration](/developer-tools/getting-started#chain-configuration) for vault and token domain parameters.

***

## V2 Signature Format

### Payload Encoding

All payloads are now encoded with a type discriminator byte before being signed:

```
[1 byte type][abi.encode(struct)]
```

**Type Discriminators:**

* `0` = PlaceOrder (Order struct)
* `1` = OrderCancel
* `2` = SimpleSignature
* `3` = Withdrawal
* `4` = LeverageUpdate
* `5` = Permit (Deposit - Token domain)
* `6` = Liquidation (system use)
* `7` = Deposit (Deposit - Vault domain)
* `8` = MarginTransfer

**Encoding Example (TypeScript/ethers.js):**

```typescript
import { ethers } from 'ethers';

// 1. Create the struct
const order = {
  account: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  market: 8,
  size: ethers.BigNumber.from("10000000000"),
  limitPrice: ethers.BigNumber.from("500000000000000000000"),
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 86400 * 30, // 30 days
  flags: 0 // See Flags section below
};

// 2. ABI-encode the struct
const abiCoder = new ethers.utils.AbiCoder();
const encodedStruct = abiCoder.encode(
  ['address', 'uint32', 'int256', 'uint256', 'uint256', 'uint256', 'uint256'],
  [order.account, order.market, order.size, order.limitPrice, order.nonce, order.deadline, order.flags]
);

// 3. Prepend type discriminator (0 for Order)
const payload = ethers.utils.concat([
  ethers.utils.arrayify(0), // Type byte
  encodedStruct
]);

// 4. Sign with EIP-712 (signing the original struct, not the encoded bytes)
const domain = { /* Vault domain config */ };
const types = {
  Order: [
    { name: 'account', type: 'address' },
    { name: 'market', type: 'uint32' },
    { name: 'size', type: 'int256' },
    { name: 'limitPrice', type: 'uint256' },
    { name: 'nonce', type: 'uint256' },
    { name: 'deadline', type: 'uint256' },
    { name: 'flags', type: 'uint256' }
  ]
};
const signature = await signer._signTypedData(domain, types, order);

// 5. Send to API
{
  signature: {
    sig: signature,
    payload: ethers.utils.hexlify(payload) // Encoded payload with type byte
  }
}
```

**Important:** You sign the **original struct** using EIP-712, but send the **encoded payload with type byte** in the request.

### Request Format

All signed operations use a standard wrapper format:

```typescript
{
  signature: {
    sig: string,      // Hex-encoded signature (0x-prefixed)
    payload: string   // Hex-encoded payload ([type byte][abi.encode(struct)])
  }
}
```

**Example (Order):**

```json
{
  "signature": {
    "sig": "0x1234...",
    "payload": "0x00000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f12345..."
  }
}
```

***

## Operations

### Orders

**Struct:**

```solidity
struct Order {
    address account;     // Wallet address
    uint32 market;       // Market/instrument ID (numeric)
    int256 size;         // Signed quantity (positive = buy, negative = sell)
    uint256 limitPrice;  // Limit price or market order flag
    uint256 nonce;       // Wallet nonce (milliseconds timestamp)
    uint256 deadline;    // Unix timestamp expiration (seconds)
    uint256 flags;       // Order flags (see Flags section)
}
```

**Field Specifications:**

* **`account`**: Wallet address
* **`market`**: Numeric instrument ID (e.g., 8 for NVDA)
* **`size`**: Order quantity with sign indicating direction
  * **Managed SL/TP orders** (`closePosition: true`): Always `0` - server manages quantity based on position
  * **Unmanaged SL/TP orders** (`closePosition: false`): Signed quantity (positive for Buy, negative for Sell)
  * **Regular orders**: Signed quantity (positive for Buy, negative for Sell)
  * Scaled by instrument's `quantity_precision`
* **`limitPrice`**:
  * **All order types** sign their execution-bound limit price in 18 decimals — including `StopLoss` and `TakeProfit`, which sign the `limitPrice` from the request (the trigger stays in `stopPrice` and is not part of the signature).
  * A signed `limitPrice` of `0` or `U256::MAX` (an unbounded order) is **rejected at placement**.
* **`nonce`**: Millisecond timestamp (e.g., `Date.now()`)
* **`deadline`**: Unix timestamp in **seconds** (not milliseconds). Recommended: 30+ days for resting orders.
  * Minimums depend on how long the order may wait, not on its time in force. Immediate orders (`IOC`/`FOK`) must be at least **1 hour** out, `DAY` orders **25 hours**, and resting orders **7 days**.
  * `StopLoss` and `TakeProfit` take the **7 day** minimum even though they are placed `IOC`/`FOK`: the time in force governs execution once the trigger is hit, while the trigger itself may rest for days.
* **`flags`**: Encoded flags (see Flags section below)

#### Order Flags

The `flags` field encodes order metadata as a U256 bitmask:

**Bit Layout:**

* **Bit 0**: Margin mode (`0` = Cross, `1` = Isolated). Must match the request's `marginMode`; isolated orders draw on the market's isolated margin.
* **Bit 1**: Reduce-only (`0` = false, `1` = true)
* **Bit 2**: Managed SL/TP (`0` = unmanaged, `1` = managed)
* **Bits 3-7**: Order type (5 bits = 32 types max)
  * `0` = Limit
  * `3` = StopLimit
  * `7` = StopLoss
  * `8` = TakeProfit
  * All other values are reserved for internal use and rejected at placement.

**Encoding Example:**

```typescript
function encodeOrderFlags(options: {
  marginMode: 'Cross' | 'Isolated',
  reduceOnly: boolean,
  managed: boolean,
  orderType: 'Limit' | 'StopLimit' | 'StopLoss' | 'TakeProfit'
}): bigint {
  const orderTypeMap = {
    'Limit': 0, 'StopLimit': 3, 'StopLoss': 7, 'TakeProfit': 8
  };

  let flags = 0n;
  if (options.marginMode === 'Isolated') flags |= (1n << 0n);
  if (options.reduceOnly) flags |= (1n << 1n);
  if (options.managed) flags |= (1n << 2n);
  flags |= BigInt(orderTypeMap[options.orderType]) << 3n;

  return flags;
}

// Example: Cross margin, not reduce-only, managed SL
const flags = encodeOrderFlags({
  marginMode: 'Cross',
  reduceOnly: false,
  managed: true,
  orderType: 'StopLoss'
});
// flags = 0b0111100 = 60
```

<details>

<summary>Examples</summary>

**Limit Buy:**

```typescript
{
  account: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  market: 8,
  size: "10000000000",
  limitPrice: "500123456789012345678",
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 86400 * 30, // 30 days
  flags: 0 // Cross margin, not reduce-only, unmanaged, Limit order
}
```

**Immediate Sell (crossing Limit IOC):**

```typescript
{
  account: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  market: 8,
  size: "-20000000000",
  limitPrice: "495000000000000000000", // e.g. mark x 0.99 — your worst acceptable price
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 7200, // 2 hours (1 hour minimum)
  flags: 0 // Cross margin, not reduce-only, unmanaged, Limit order
}
```

**Managed Stop-Loss (Long Position, closePosition=true):**

```typescript
{
  account: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  market: 8,
  size: "0",  // Managed: server auto-syncs quantity with position
  limitPrice: "480000000000000000000",  // Execution bound (18 decimals) — must match the request's limitPrice, not the trigger
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 86400 * 30,
  flags: 60 // Cross margin, not reduce-only, managed=1, StopLoss order (type=7)
            // Binary: 0b0111100 = (managed=1 << 2) | (orderType=7 << 3)
}
```

**Unmanaged Stop-Loss (Long Position, closePosition=false, 10 BTC):**

```typescript
{
  account: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  market: 8,
  size: "-10000000000",  // Unmanaged: fixed quantity (negative = Sell)
  limitPrice: "480000000000000000000",  // Execution bound (18 decimals) — must match the request's limitPrice
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 86400 * 30,
  flags: 56 // Cross margin, not reduce-only, unmanaged=0, StopLoss order (type=7)
            // Binary: 0b0111000 = (managed=0 << 2) | (orderType=7 << 3)
}
```

</details>

***

### Cancel Order

**Struct:**

```solidity
struct OrderCancel {
    address account;    // Wallet address
    uint32 market;      // Market/instrument ID
    uint64 orderId;     // Order ID to cancel
    uint256 nonce;      // Wallet nonce (milliseconds timestamp)
    uint256 deadline;   // Unix timestamp expiration (seconds)
}
```

**Field Specifications:**

* **`account`**: Wallet address
* **`market`**: Numeric instrument ID
* **`orderId`**: The identifier the signature authorizes cancellation of. Please sign the same identifier sent in the cancel request — either the exchange `order_id` or your `traderOrderId`.
* **`nonce`**: Millisecond timestamp
* **`deadline`**: Unix timestamp in seconds. Must be at least 1 hour in the future (server minimum); recommended: 2 hours

Order ownership is validated before cancellation.

<details>

<summary>Example</summary>

```typescript
{
  account: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  market: 8,
  orderId: 123456789,
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 7200 // 2 hours (1 hour minimum)
}
```

</details>

***

### Deposits

Deposits require **two signatures**:

1. **Permit signature** (ERC-2612, Token domain) - Approves token transfer
2. **Deposit signature** (Vault domain) - Authorizes vault deposit

#### Permit (ERC-2612)

**Struct:**

```solidity
struct Permit {
    address owner;      // Wallet address
    address spender;    // Vault contract address
    uint256 value;      // Amount in token base units (6 decimals for USDC)
    uint256 nonce;      // Token contract nonce
    uint256 deadline;   // Unix timestamp expiration
}
```

**Field Specifications:**

* **`owner`**: Wallet address
* **`spender`**: Vault contract address
* **`value`**: Amount in token base units (6 decimals for USDC, e.g., "1000500000" for 1000.50)
* **`nonce`**: Token contract nonce (fetch from `tokenContract.nonces(walletAddress)`)
* **`deadline`**: Unix timestamp (seconds). Must be at least 1 hour in the future (server minimum); recommended: 2 hours

**Validation Rules:**

* `value` > 0
* `spender` must equal vault contract address
* `deadline` at least 1 hour in the future

The nonce must be fetched from the token contract, not generated as a timestamp. See [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits).

#### Deposit (Vault Authorization)

**Struct:**

```solidity
struct Deposit {
    address account;    // Wallet address
    uint256 amount;     // Amount in token base units (6 decimals for USDC)
    uint32 market;      // Market ID (0 for cross margin)
    uint256 flags;      // Deposit flags (margin mode)
    uint256 nonce;      // Wallet nonce (milliseconds timestamp)
    uint256 deadline;   // Unix timestamp expiration
}
```

**Field Specifications:**

* **`account`**: Wallet address
* **`amount`**: Must match `Permit.value`
* **`market`**: Instrument ID of the isolated market for isolated margin; `0` for cross margin
* **`flags`**: Margin mode flags (bit 0: `0` = Cross, `1` = Isolated)
* **`nonce`**: Millisecond timestamp (different from Permit nonce)
* **`deadline`**: Unix timestamp (seconds). Must be at least 1 hour in the future (server minimum); recommended: 2 hours

**Validation Rules:**

* `amount` must equal `Permit.value`
* `market` and `flags` bit 0 must agree with the request's `instrument`/`marginMode`: `market: 0` + bit 0 = `0` for cross, `market` = instrument ID + bit 0 = `1` for isolated

<details>

<summary>Example</summary>

```typescript
// 1. Fetch token nonce
const tokenNonce = await tokenContract.nonces(walletAddress);

// 2. Create Permit signature
const permit = {
  owner: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  spender: "0x54332b922cF8c4978522f342af6b30AC01bE6f99",
  value: "100500000", // 100.50 USDC (6 decimals)
  nonce: tokenNonce,
  deadline: Math.floor(Date.now() / 1000) + 7200 // 2 hours (1 hour minimum)
};
const permitSig = await signer._signTypedData(tokenDomain, permitTypes, permit);

// 3. Encode permit payload
const permitPayload = encodePayload(5, permit); // Type 5 = Permit

// 4. Create Deposit signature
const deposit = {
  account: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  amount: "100500000", // Must match permit.value (6 decimals)
  market: 0, // Cross margin
  flags: 0, // Cross margin (bit 0 = 0)
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 7200 // 2 hours (1 hour minimum)
};
const depositSig = await signer._signTypedData(vaultDomain, depositTypes, deposit);

// 5. Encode deposit payload
const depositPayload = encodePayload(7, deposit); // Type 7 = Deposit

// 6. Send both signatures to API
{
  amount: "100.50",
  permitSignature: {
    sig: permitSig,
    payload: permitPayload
  },
  depositSignature: {
    sig: depositSig,
    payload: depositPayload
  }
}
```

</details>

***

### Withdrawals

**Struct:**

```solidity
struct Withdrawal {
    address owner;      // Wallet address
    uint256 amount;     // Amount in token base units (6 decimals for USDC)
    uint32 market;      // Market ID (0 for cross margin)
    uint256 flags;      // Withdrawal flags (margin mode)
    uint256 nonce;      // Wallet nonce (milliseconds timestamp)
    uint256 deadline;   // Unix timestamp expiration
}
```

**Field Specifications:**

* **`owner`**: Wallet address (note: `owner`, not `account`)
* **`amount`**: Amount in token base units (6 decimals for USDC, e.g., "500250000" for 500.25)
* **`market`**: Instrument ID of the isolated market for isolated margin; `0` for cross margin
* **`flags`**: Margin mode flags (bit 0: `0` = Cross, `1` = Isolated)
* **`nonce`**: Millisecond timestamp
* **`deadline`**: Unix timestamp (seconds). Must be at least 1 hour in the future (server minimum); recommended: 2 hours

**Validation Rules:**

* `amount` > 0
* `market` and `flags` bit 0 must agree with the request's `instrument`/`marginMode`: `market: 0` + bit 0 = `0` for cross, `market` = instrument ID + bit 0 = `1` for isolated
* `deadline` at least 1 hour in the future

<details>

<summary>Example</summary>

```typescript
{
  owner: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  amount: "50250000", // 50.25 USDC (6 decimals)
  market: 0, // Cross margin
  flags: 0, // Cross margin
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 7200 // 2 hours (1 hour minimum)
}
```

</details>

***

### Leverage Updates

**Struct:**

```solidity
struct LeverageUpdate {
    address account;    // Wallet address
    uint32 market;      // Market/instrument ID
    uint32 leverage;    // Leverage × 100
    uint256 flags;      // Leverage flags (margin mode)
    uint256 nonce;      // Wallet nonce (milliseconds timestamp)
    uint256 deadline;   // Unix timestamp expiration
}
```

**Field Specifications:**

* **`account`**: Wallet address
* **`market`**: Numeric instrument ID
* **`leverage`**: Desired leverage × 100
* **`flags`**: Margin mode flags (bit 0: `0` = Cross, `1` = Isolated) — selects which mode's leverage to update
* **`nonce`**: Millisecond timestamp
* **`deadline`**: Unix timestamp (seconds). Must be at least 1 hour in the future (server minimum); recommended: 2 hours

**Leverage Encoding:**

| Desired Leverage | `leverage` Value |
| ---------------- | ---------------- |
| 1x               | 100              |
| 5x               | 500              |
| 10x              | 1000             |
| 20x              | 2000             |
| 100x             | 10000            |

**Validation Rules:**

* `leverage` ≥ 100
* `leverage` ≤ `instrument.max_leverage × 100`
* `market` must be valid instrument ID
* `flags` bit 0 must match the request's `marginMode`
* `deadline` at least 1 hour in the future

<details>

<summary>Example</summary>

```typescript
{
  account: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  market: 8,
  leverage: 1000, // 10x
  flags: 0, // Cross margin
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 7200 // 2 hours (1 hour minimum)
}
```

</details>

***

### Margin Transfers

Move collateral between cross margin and a market's isolated margin. Signed over the Vault domain, payload type `0x08`.

**Struct:**

```solidity
struct MarginTransfer {
    address owner;      // Wallet address
    uint256 amount;     // Amount in token base units (6 decimals for USDC)
    uint32 market;      // Instrument ID of the isolated market
    uint256 flags;      // Transfer flags (source margin mode)
    uint256 nonce;      // Wallet nonce (milliseconds timestamp)
    uint256 deadline;   // Unix timestamp expiration
}
```

**Field Specifications:**

* **`owner`**: Wallet address
* **`amount`**: Amount in token base units (6 decimals for USDC)
* **`market`**: Instrument ID of the isolated market being funded or drained (both directions)
* **`flags`**: Bit 0 carries the **source** account's margin mode — the account being debited: `0` for cross → isolated, `1` for isolated → cross. Must agree with the request's `direction`.
* **`nonce`**: Millisecond timestamp
* **`deadline`**: Unix timestamp (seconds). Must be at least 1 hour in the future (server minimum); recommended: 2 hours

**Validation Rules:**

* `amount` > 0
* `market` must match the request's `instrument`
* `flags` bit 0 must match the source side of the request's `direction`
* `deadline` at least 1 hour in the future

<details>

<summary>Example</summary>

```typescript
// Move 250 USDC from cross into BTC isolated margin
const transfer = {
  owner: "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  amount: "250000000", // 250 USDC (6 decimals)
  market: 4, // BTC — the isolated market's instrument ID
  flags: 0, // Source is cross (bit 0 = 0); use 1 for isolated -> cross
  nonce: Date.now(),
  deadline: Math.floor(Date.now() / 1000) + 7200 // 2 hours (1 hour minimum)
};
const transferSig = await signer._signTypedData(vaultDomain, marginTransferTypes, transfer);
const transferPayload = encodePayload(8, transfer); // Type 8 = MarginTransfer

// Send to API
{
  amount: "250.00",
  instrument: "BTC",
  direction: "crossToIsolated",
  signature: {
    sig: transferSig,
    payload: transferPayload
  }
}
```

</details>

***

## EIP-191 Operations

EIP-191 uses simple personal message signing (`personal_sign`).

### Message Format

```solidity
struct SimpleSignaturePayload {
    address account;    // Wallet address
    uint256 nonce;      // Wallet nonce (milliseconds timestamp)
    uint256 deadline;   // Unix timestamp expiration (seconds)
}
```

Message construction:

```
{account}:{nonce}
```

Example:

```
0x742d35Cc6634C0532925a3b844Bc9e7595f12345:1705600000000
```

**Note:** The deadline is part of the struct but **not** part of the signed message string.

### Authentication

Used for WebSocket connection authentication.

<details>

<summary>Example</summary>

```typescript
const account = "0x742d35Cc6634C0532925a3b844Bc9e7595f12345";
const nonce = Date.now();
const deadline = Math.floor(Date.now() / 1000) + 300; // 5 minutes
const message = `${account}:${nonce}`;
const signature = await signer.signMessage(message);

// Encode payload
const payload = {
  account: account,
  nonce: nonce,
  deadline: deadline
};
const encodedPayload = encodePayload(2, payload); // Type 2 = SimpleSignature

{
  signature: {
    sig: signature,
    payload: encodedPayload
  }
}
```

</details>

### Mass Cancel

Cancels all orders, optionally filtered by instrument.

<details>

<summary>Example</summary>

```typescript
const account = "0x742d35Cc6634C0532925a3b844Bc9e7595f12345";
const nonce = Date.now();
const deadline = Math.floor(Date.now() / 1000) + 300;
const message = `${account}:${nonce}`;
const signature = await signer.signMessage(message);

const payload = {
  account: account,
  nonce: nonce,
  deadline: deadline
};
const encodedPayload = encodePayload(2, payload);

{
  signature: {
    sig: signature,
    payload: encodedPayload
  },
  instrument: "NVDA"
}
```

</details>

***

## Signature Validation Errors

| Error                       | Description                                                                                                                     |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `SignatureError`            | Malformed signature bytes                                                                                                       |
| `IncorrectAddress`          | Recovered signer doesn't match claimed address                                                                                  |
| `DeadlineExpired`           | Signature deadline expired                                                                                                      |
| `DeadlineTooSoon`           | Deadline below the server minimum: 1 hour for collateral ops, cancels, and IOC/FOK orders; 25 hours for DAY; 7 days for GTC/GTD |
| `InstrumentNotFound`        | Unknown instrument symbol                                                                                                       |
| `InstrumentMismatch`        | Signed market ID doesn't match requested instrument                                                                             |
| `AmountMismatch`            | Signed amount doesn't match request                                                                                             |
| `OrderSizeMismatch`         | Signed size doesn't match requested quantity                                                                                    |
| `PriceMismatch`             | Signed price doesn't match request                                                                                              |
| `OrderIdMismatch`           | Signed order ID doesn't match request                                                                                           |
| `LeverageMismatch`          | Signed leverage doesn't match request                                                                                           |
| `MarginModeMismatch`        | Signed margin mode doesn't match request                                                                                        |
| `FlagMismatch`              | Signed flags don't match request                                                                                                |
| `OrderTypeMismatch`         | Signed order type doesn't match request                                                                                         |
| `ZeroAmount`                | Amount must be greater than zero                                                                                                |
| `WrongSpender`              | Permit spender doesn't match vault address                                                                                      |
| `LeverageBelowMinimum`      | Leverage must be ≥ 100                                                                                                          |
| `LeverageExceedsMaximum`    | Leverage exceeds instrument maximum                                                                                             |
| `VaultAddressNotConfigured` | Vault address not configured                                                                                                    |
| `TokenAddressNotConfigured` | Token address not configured                                                                                                    |
| `DecodeError`               | Failed to decode payload                                                                                                        |

For complete error code reference, see [Error Codes](/developer-tools/error-codes).


# WebSocket API

WebSocket API for real-time trading operations and execution reports.

## Connection Endpoint

**Testnet**: `wss://api.app.hello.trade/ws`

**Mainnet**: N/A

***

## Message Format

All messages use JSON with a `type` field discriminator.

**Request:**

```json
{
  "type": "authenticate",
  ...
}
```

**Response:**

```json
{
  "type": "authenticateSuccess",
  ...
}
```

***

## Connection Flow

1. **Connect** to WebSocket endpoint
2. **Authenticate** with EIP-191 signature
3. **Subscribe** to trading stream (optional, required for execution reports)
4. **Trade** (place, cancel, replace orders)
5. **Logout** or disconnect when done

***

## Authentication

Connections must authenticate before performing trading operations. See [Authentication & Account Model](/developer-tools/authentication-and-account-model) for details.

**Request:**

```json
{
  "type": "authenticate",
  "signature": {
    "sig": "0x...",
    "payload": "0x02..."
  }
}
```

**Success Response:**

```json
{
  "type": "authenticateSuccess",
  "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  "message": "Authentication successful"
}
```

For signature construction, see [Signatures - EIP-191 Operations](/developer-tools/signatures#eip-191-operations).

***

## Connection Limits

| Limit                       | Value      |
| --------------------------- | ---------- |
| Max connections per account | 8          |
| Idle timeout                | 30 seconds |
| Ping interval               | 15 seconds |

Connections exceeding idle timeout are automatically disconnected. Maintain connection by:

* Responding to ping frames
* Sending requests

***

## Rate Limiting

Maximum request rate: **20 requests/second per wallet** (rolling 1-second window, orders included). Applies to wallet-authenticated requests; unauthenticated reads are limited per client IP — see [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits).

See [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits) for details.

***

## Error Responses

All errors use the same format:

```json
{
  "type": "error",
  "code": 4001,
  "message": "Signature verification failed"
}
```

For complete error code reference, see [Error Codes](/developer-tools/error-codes).

***

## Message Types

### Connection Management

| Message Type       | Description                         | Authentication Required |
| ------------------ | ----------------------------------- | ----------------------- |
| `authenticate`     | Authenticate connection with wallet | No                      |
| `logout`           | Clear authentication state          | Yes                     |
| `subscribeTrading` | Subscribe to execution reports      | Yes                     |

See [Connection Management](/developer-tools/websocket-api/connection-management) for details.

### Trading Operations

| Message Type     | Description                                           | Authentication Required |
| ---------------- | ----------------------------------------------------- | ----------------------- |
| `placeOrder`     | Place new order                                       | Yes                     |
| `replaceOrder`   | Atomically replace existing order                     | Yes                     |
| `cancelOrder`    | Cancel order by ID                                    | Yes                     |
| `cancelAll`      | Cancel all orders (optionally by instrument)          | Yes                     |
| `transferMargin` | Transfer collateral between cross and isolated margin | Yes                     |

See [Trading Operations](/developer-tools/websocket-api/trading-operations) for details.

### Execution Reports

After subscribing to the trading stream, execution reports are automatically pushed for:

* Order status changes (new, filled, cancelled, etc.)
* Trade executions
* Account events (deposits, withdrawals, margin transfers, leverage updates, liquidations)

See [Execution Reports](/developer-tools/websocket-api/execution-reports) for details.

### Liquidation Auction

| Message Type               | Direction        | Description                                                                         | Authentication Required |
| -------------------------- | ---------------- | ----------------------------------------------------------------------------------- | ----------------------- |
| `manualLiquidationOffer`   | Gateway → Client | Broadcast of a position open for claim during liquidation                           | Yes (subscribed)        |
| `claimLiquidation`         | Client → Gateway | Claim an open offer with a signed order                                             | Yes                     |
| `claimLiquidationAccepted` | Gateway → Client | Ack that a claim entered the auction                                                | Yes                     |
| `claimLiquidationResult`   | Gateway → Client | Terminal win/lose outcome sent to each accepted claimant after the auction resolves | Yes                     |

See [Liquidation Auction](/developer-tools/websocket-api/liquidation-auction) for details.

### Account Summary Stream

| Message Type                | Direction        | Description                                                                                                                                            | Authentication Required |
| --------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- |
| `subscribeAccountSummary`   | Client → Gateway | Subscribe to account summary updates (margin, positions, withdrawable)                                                                                 | Yes                     |
| `unsubscribeAccountSummary` | Client → Gateway | Stop receiving account summary updates                                                                                                                 | Yes                     |
| `accountSummary`            | Gateway → Client | Snapshot (on subscribe), then a full snapshot on each position/margin/balance change (fills, busts, liquidations, funding, leverage, deposit/withdraw) | Yes (subscribed)        |

Independent of `subscribeTrading`. See [Account Summary Stream](/developer-tools/websocket-api/account-summary) for details.

***

## WebSocket vs REST

| Operation                   | WebSocket  | REST         |
| --------------------------- | ---------- | ------------ |
| Place/Cancel/Replace Orders | ✓          | -            |
| Real-time Execution Reports | ✓          | -            |
| Deposit/Withdraw Margin     | -          | ✓            |
| Update Leverage             | -          | ✓            |
| Account Summary             | ✓ (stream) | ✓ (snapshot) |
| Historical Orders/Trades    | -          | ✓            |

For REST API operations, see [REST API](/developer-tools/rest-api).


# Connection Management

WebSocket connection lifecycle management (authenticate, logout, subscribe)

## Connection Basics

HelloTrade uses WebSocket endpoints for real-time data exchange and trading.

**Endpoints:**

| Service     | Environment | Endpoint                              |
| ----------- | ----------- | ------------------------------------- |
| Trading     | Production  | `wss://api.app.hello.trade/ws`        |
| Market Data | Production  | `wss://marketdata.app.hello.trade/ws` |

Once connected to the trading endpoint, you must authenticate your connection before performing trading operations or subscribing to execution reports.

**CLI Example:**

```bash
wscat -c wss://api.app.hello.trade/ws
# After connection, send authentication (with encoded payload):
{"type":"authenticate","signature":{"sig":"0x...","payload":"0x02..."}}
# Then subscribe to trading:
{"type":"subscribeTrading"}
```

### Heartbeat

The server sends periodic ping frames. Clients must respond with pong frames to maintain the connection.

| Setting       | Value      |
| ------------- | ---------- |
| Ping interval | 15 seconds |
| Idle Timeout  | 30 seconds |

Connections are closed after 30 seconds of inactivity (no messages or ping/pong responses).

### Reconnection

On disconnect:

1. Reconnect to WebSocket endpoint
2. Re-authenticate with fresh nonce
3. Re-subscribe to trading stream
4. Reconcile order state from initial snapshot

Always reconcile your local order state with the snapshot received after re-subscribing.

***

## authenticate

Authenticate WebSocket connection with EIP-191 signature.

### Request

```json
{
  "type": "authenticate",
  "signature": {
    "sig": "0x...",
    "payload": "0x02..."
  }
}
```

| Field               | Type   | Description                                                |
| ------------------- | ------ | ---------------------------------------------------------- |
| `signature.sig`     | string | EIP-191 signature (hex-encoded with 0x prefix)             |
| `signature.payload` | string | Encoded SimpleSignaturePayload (type discriminator `0x02`) |

**Signature Construction:**

1. Create SimpleSignaturePayload struct with `account`, `nonce`, and `deadline`
2. Sign the message string: `{account}:{nonce}` (note: deadline not in message, only in struct)
3. Encode the struct with type discriminator byte `0x02`

See [Signatures - EIP-191 Operations](/developer-tools/signatures#eip-191-operations) for complete details.

### Success Response

```json
{
  "type": "authenticateSuccess",
  "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  "message": "Authentication successful"
}
```

### Common Errors

| Code | Cause                                             |
| ---- | ------------------------------------------------- |
| 4001 | Signature verification failed                     |
| 4002 | Invalid nonce (expired, replay, or out of window) |
| 4029 | Rate limit exceeded                               |
| 5000 | Connection limit exceeded (>8 connections)        |
| 5003 | Failed to create or retrieve account              |

***

## logout

Clear authentication state and unsubscribe from trading stream.

**Authentication Required:** Yes

### Request

```json
{
  "type": "logout"
}
```

No parameters required.

### Success Response

```json
{
  "type": "logoutSuccess",
  "message": "Logout successful"
}
```

Connection returns to unauthenticated state. To trade again, send new `authenticate` message.

***

## subscribeTrading

Subscribe to execution reports stream.

**Authentication Required:** Yes

After successful subscription, the gateway automatically:

1. Sends initial order snapshot (all resting orders for the account)
2. Streams real-time execution reports for order updates, trades, and account events

### Request

```json
{
  "type": "subscribeTrading"
}
```

No parameters required. Uses authenticated wallet from `authenticate` message.

### Success Response

```json
{
  "type": "subscribeTradingSuccess",
  "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f12345",
  "message": "Subscribed to trading stream. Received 3 orders in snapshot. Execution reports will follow."
}
```

### Execution Report Flow

After subscription:

1. **Order snapshot** - All resting orders sent as execution reports with `messageType: "OrderStatus"`
2. **Real-time updates** - Ongoing execution reports for:
   * Order status changes (filled, cancelled, etc.)
   * Trade executions
   * Account events (deposits, withdrawals, leverage updates, liquidations)

See [Execution Reports](/developer-tools/websocket-api/execution-reports) for message formats.

### Common Errors

| Code  | Cause                                         |
| ----- | --------------------------------------------- |
| 4003  | Not authenticated (send `authenticate` first) |
| 4029  | Rate limit exceeded                           |
| 4033  | Already subscribed to trading stream          |
| 5000  | Internal error                                |
| 5001+ | Exchange error (5001 + exchange error code)   |

***


# Trading Operations

WebSocket trading operations (place, replace, cancel orders)

All trading operations require authentication. See [Connection Management](/developer-tools/websocket-api/connection-management) for authentication details.

***

## Error Responses

Any operation can return an error frame instead of its success response:

```json
{
  "type": "error",
  "code": 4100,
  "message": "Insufficient margin: required 1000, available 500",
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "instrument": "NVDA"
}
```

A `4107` rejection also carries `safeLimitPrice`: the worst limit price at which the reduce-only or stop-loss/take-profit order would still settle above maintenance margin. Resubmitting with that limit (or a better one) passes the check.

`traderOrderId`, `orderId`, and `instrument` echo back whatever the originating request carried, so a client with multiple in-flight requests can match an error to the request that caused it. Each field is present only when the request supplied it, and omitted entirely (never sent as `null`) otherwise:

| Operation           | `traderOrderId` | `orderId`            | `instrument`     |
| ------------------- | --------------- | -------------------- | ---------------- |
| `placeOrder`        | if supplied     | — (not yet assigned) | yes              |
| `cancelOrder`       | if supplied     | if supplied          | yes              |
| `replaceOrder`      | if supplied     | if supplied          | yes              |
| `cancelAll`         | —               | —                    | if scoped to one |
| auth / parse errors | —               | —                    | —                |

These same correlation fields are also echoed on the **success** responses (`orderPlaced`, `orderReplaced`, `orderCanceled`, `massCanceled`), so you can correlate without relying on response ordering. Do not rely on response ordering: requests are pipelined per (wallet, instrument) lane and replies are emitted as each operation completes, so replies can arrive out of request order. Always correlate by `traderOrderId`/`orderId`.

***

## placeOrder

Place a new order.

**Authentication Required:** Yes

### Request

```json
{
  "type": "placeOrder",
  "traderOrderId": 1705600000001,
  "orderType": "Limit",
  "side": "Buy",
  "instrument": "NVDA",
  "quantity": "100",
  "timeInForce": "GTC",
  "limitPrice": "500.00",
  "stopPrice": null,
  "expiryDate": null,
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x00000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f12345..."
  }
}
```

**Note:** The `payload` field contains the encoded Order struct (see [Signatures - V2 Signature Format](/developer-tools/signatures#v2-signature-format)).

**Stop-Loss & Take-Profit Orders:**

SL/TP orders support two modes via the `closePosition` parameter:

**Managed Orders** (`closePosition: true`):

```json
{
  "type": "placeOrder",
  "traderOrderId": 1705600000002,
  "orderType": "StopLoss",
  "side": "Sell",
  "instrument": "NVDA",
  "quantity": null,
  "closePosition": true,
  "timeInForce": "IOC",
  "stopPrice": "490.00",
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x00..."
  }
}
```

**Unmanaged Orders** (`closePosition: false`):

```json
{
  "type": "placeOrder",
  "traderOrderId": 1705600000003,
  "orderType": "StopLoss",
  "side": "Sell",
  "instrument": "NVDA",
  "quantity": "10",
  "closePosition": false,
  "timeInForce": "IOC",
  "stopPrice": "490.00",
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x00..."
  }
}
```

**Signature Rules:**

* **Managed** (`closePosition: true`): Sign Order struct with `size: "0"`, `flags` with managed bit set (see [Order Flags](/developer-tools/signatures#order-flags))
* **Unmanaged** (`closePosition: false`): Sign Order struct with actual signed quantity, `flags` without managed bit
* Order struct must include `deadline` and `flags` fields (see [Signatures - Orders](/developer-tools/signatures#orders))

See [Order Types](/about/trading/order-types) for managed vs unmanaged behavior, OCO, and auto-cancellation.

### Request Fields

| Field           | Type      | Required    | Description                                                                                                                                      |
| --------------- | --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `traderOrderId` | u64       | No          | Optional client-supplied order identifier. Max **36 bits** (≤ `68,719,476,735`)                                                                  |
| `orderType`     | string    | Yes         | `Limit`, `StopLimit`, `StopLoss`, `TakeProfit`                                                                                                   |
| `side`          | string    | Yes         | `Buy` or `Sell`                                                                                                                                  |
| `instrument`    | string    | Yes         | Instrument symbol (e.g., NVDA)                                                                                                                   |
| `quantity`      | string    | Conditional | Order quantity (null for managed SL/TP, required otherwise)                                                                                      |
| `closePosition` | boolean   | SL/TP: Yes  | SL/TP only, required for SL/TP orders: `true` = managed (auto-sync qty), `false` = unmanaged (fixed qty)                                         |
| `reduceOnly`    | boolean   | No          | Order may only reduce an existing position. Must match the signed flag (bit 1) — the signed value is what is enforced. Requires an open position |
| `instructions`  | string\[] | No          | `["PostOnly"]` for a [post-only](#post-only-orders) order                                                                                        |
| `timeInForce`   | string    | Yes         | `GTC`, `GTD`, `IOC`, `FOK`, `DAY`                                                                                                                |
| `limitPrice`    | string    | Yes         | Required for every order type (all orders carry a price bound)                                                                                   |
| `stopPrice`     | string    | Conditional | Required for `StopLimit`, `StopLoss`, `TakeProfit`                                                                                               |
| `expiryDate`    | i64       | Conditional | Required for `GTD` (Unix timestamp in milliseconds)                                                                                              |
| `marginMode`    | string    | Yes         | `Cross` or `Isolated`. Must match the signed order's margin flag (bit 0).                                                                        |
| `signature`     | Object    | Yes         | EIP-712 signed order payload                                                                                                                     |

### Order Type Requirements

| Order Type   | `limitPrice` | `stopPrice` | `timeInForce`           |
| ------------ | ------------ | ----------- | ----------------------- |
| `Limit`      | Required     | -           | GTC, GTD, IOC, FOK, DAY |
| `StopLimit`  | Required     | Required    | GTC, GTD, IOC, FOK, DAY |
| `StopLoss`   | Required     | Required    | IOC, FOK                |
| `TakeProfit` | Required     | Required    | IOC, FOK                |

These are the only accepted order types — every order carries a price bound. For market-style immediate execution, place a crossing `Limit` `IOC` with your slippage tolerance as the limit price; for a stop-market, use `StopLimit` with an aggressive limit price.

Minimum order size: **$5** notional.

For signature construction, see [Signatures - Orders](/developer-tools/signatures#orders).

### Post-Only Orders

A post-only order only adds liquidity. If it would match immediately, it is cancelled.

* Supported on `Limit` and `StopLimit` with `GTC`, `GTD` or `DAY`.
* Not part of the signature.
* If it would match, you receive `orderPlaced`, then a `Cancelled` execution report.
* That cancel carries `cancelReason: "PostOnly"`.
* `replaceOrder` keeps it.
* Shown as `instructions` on execution reports, open orders and historic orders.

### Success Response

```json
{
  "type": "orderPlaced",
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "orderStatus": "Pending"
}
```

`traderOrderId` is echoed back only when it was supplied on the request (see [Error Responses](#error-responses) for correlation details).

### Common Errors

| Code  | Cause                                                               |
| ----- | ------------------------------------------------------------------- |
| 4000  | Invalid request (missing required fields, invalid order parameters) |
| 4001  | Signature verification failed                                       |
| 4002  | Invalid nonce                                                       |
| 4029  | Rate limit exceeded                                                 |
| 4030  | Invalid order type                                                  |
| 4031  | Invalid time in force                                               |
| 4032  | Invalid or missing price                                            |
| 4100  | Insufficient margin                                                 |
| 4101  | Order too small (below $5 minimum notional)                         |
| 4102  | Max leverage exceeded                                               |
| 4103  | OI cap exceeded                                                     |
| 4107  | Limit would fill below maintenance margin (see `safeLimitPrice`)    |
| 5000  | Internal error                                                      |
| 5001+ | Exchange error (5001 + exchange code)                               |
| 5002  | Risk engine unavailable                                             |

***

## replaceOrder

Atomically cancel existing order and place new one.

**Authentication Required:** Yes

### Request

```json
{
  "type": "replaceOrder",
  "orderId": 123456789,
  "instrument": "NVDA",
  "quantity": "200",
  "timeInForce": "GTC",
  "limitPrice": "510.00",
  "stopPrice": null,
  "expiryDate": null,
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x00..."
  }
}
```

**Note:** The `payload` field contains the encoded Order struct with new order parameters.

### Request Fields

| Field           | Type   | Required    | Description                                                                                                            |
| --------------- | ------ | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| `orderId`       | u64    | Conditional | Exchange order ID to replace (required if `traderOrderId` omitted)                                                     |
| `traderOrderId` | u64    | Conditional | Client-supplied order ID of the order to replace (required if `orderId` omitted). Max **36 bits** (≤ `68,719,476,735`) |
| `instrument`    | string | Yes         | Instrument symbol                                                                                                      |
| `quantity`      | string | Yes         | New order quantity                                                                                                     |
| `timeInForce`   | string | Yes         | New time in force                                                                                                      |
| `limitPrice`    | string | Conditional | New limit price (if applicable)                                                                                        |
| `stopPrice`     | string | Conditional | New stop price (if applicable)                                                                                         |
| `expiryDate`    | i64    | Conditional | New expiry date for GTD                                                                                                |
| `marginMode`    | string | Yes         | `Cross` or `Isolated` (must match the original order and the signed order's margin flag).                              |
| `signature`     | Object | Yes         | EIP-712 signed order payload (same format as placeOrder)                                                               |

**Notes:**

* At least one of `orderId` or `traderOrderId` must be provided to identify the order being replaced
* Signature authorizes the **new** order parameters (same format as `placeOrder`)
* Side and order type are inherited from the original order
* Only resting orders can be replaced; immediate-execution orders (`Limit` IOC/FOK, `StopLoss`, `TakeProfit` legs in flight) cannot

For signature construction, see [Signatures - Orders](/developer-tools/signatures#orders).

### Success Response

```json
{
  "type": "orderReplaced",
  "traderOrderId": 1705600000001,
  "orderId": 123456789
}
```

`traderOrderId` is echoed back only when it was supplied on the request.

**Replace Managed Stop-Loss:**

```json
{
  "type": "replaceOrder",
  "orderId": 123456789,
  "instrument": "NVDA",
  "quantity": null,
  "stopPrice": "495.00",
  "timeInForce": "IOC",
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x00..."
  }
}
```

**Replace Unmanaged Stop-Loss:**

```json
{
  "type": "replaceOrder",
  "orderId": 123456790,
  "instrument": "NVDA",
  "quantity": "15",
  "stopPrice": "495.00",
  "timeInForce": "IOC",
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x00..."
  }
}
```

**Important:**

* **Managed SL/TP**: Use `quantity: null` and sign Order struct with `size: "0"` and managed flag set
* **Unmanaged SL/TP**: Provide `quantity` and sign Order struct with actual signed quantity and managed flag unset
* **The `closePosition` mode cannot be changed on replace** - you must use the same signing pattern (flags) as the original order

### Common Errors

| Code  | Cause                                                              |
| ----- | ------------------------------------------------------------------ |
| 4000  | Invalid request (order not owned by wallet)                        |
| 4041  | Order not found                                                    |
| 4001  | Signature verification failed                                      |
| 4002  | Invalid nonce                                                      |
| 4029  | Rate limit exceeded                                                |
| 4030  | Invalid order type (unsupported type, or order cannot be replaced) |
| 4031  | Invalid time in force                                              |
| 4032  | Invalid or missing price                                           |
| 4100  | Insufficient margin                                                |
| 4102  | Max leverage exceeded                                              |
| 4103  | OI cap exceeded                                                    |
| 5000  | Internal error                                                     |
| 5001+ | Exchange error                                                     |
| 5002  | Risk engine unavailable                                            |

***

## cancelOrder

Cancel an existing order.

**Authentication Required:** Yes

### Request

```json
{
  "type": "cancelOrder",
  "orderId": 123456789,
  "traderOrderId": 1705600000001,
  "instrument": "NVDA",
  "signature": {
    "sig": "0x...",
    "payload": "0x01..."
  }
}
```

**Note:** The `payload` field contains the encoded OrderCancel struct (type discriminator `0x01`). See [Signatures - Cancel Order](/developer-tools/signatures#cancel-order) for struct details.

### Request Fields

| Field           | Type   | Required    | Description                                                                            |
| --------------- | ------ | ----------- | -------------------------------------------------------------------------------------- |
| `orderId`       | u64    | Conditional | Exchange order ID (required if `traderOrderId` not provided)                           |
| `traderOrderId` | u64    | Conditional | Client order ID (optional if `orderId` provided). Max **36 bits** (≤ `68,719,476,735`) |
| `instrument`    | string | Yes         | Instrument symbol                                                                      |
| `signature`     | Object | Yes         | EIP-712 signed cancel payload                                                          |

**Notes:** At least one of `orderId` or `traderOrderId` must be provided. Order ownership is validated before cancellation. Please sign the same identifier sent in the cancel request.

For signature construction, see [Signatures - Cancel Order](/developer-tools/signatures#cancel-order).

### Success Response

```json
{
  "type": "orderCanceled",
  "traderOrderId": 1705600000001,
  "orderId": 123456789
}
```

`traderOrderId` is echoed back only when it was supplied on the request.

### Common Errors

| Code  | Cause                                                         |
| ----- | ------------------------------------------------------------- |
| 4000  | Invalid request (order not owned by wallet, missing order ID) |
| 4041  | Order not found                                               |
| 4001  | Signature verification failed                                 |
| 4002  | Invalid nonce                                                 |
| 4029  | Rate limit exceeded                                           |
| 5000  | Internal error                                                |
| 5001+ | Exchange error                                                |

***

## cancelAll

Cancel all orders, optionally filtered by instrument.

**Authentication Required:** Yes

### Request

**Cancel all orders across all instruments:**

```json
{
  "type": "cancelAll",
  "targetScope": "All",
  "signature": {
    "sig": "0x...",
    "payload": "0x02..."
  }
}
```

**Cancel orders for specific instrument:**

```json
{
  "type": "cancelAll",
  "instrument": "NVDA",
  "targetScope": "Instrument",
  "signature": {
    "sig": "0x...",
    "payload": "0x02..."
  }
}
```

**Note:** The `payload` field contains the encoded SimpleSignaturePayload (type discriminator `0x02`). See [Signatures - Mass Cancel](/developer-tools/signatures#mass-cancel) for struct details.

### Request Fields

| Field         | Type   | Required    | Description                                                                     |
| ------------- | ------ | ----------- | ------------------------------------------------------------------------------- |
| `targetScope` | string | Yes         | `All` to cancel all orders, or `Instrument` to cancel only specified instrument |
| `instrument`  | string | Conditional | Instrument symbol (REQUIRED if `targetScope` is `Instrument`)                   |
| `signature`   | Object | Yes         | EIP-191 signed auth payload (same format as `authenticate`)                     |

**Important:** `targetScope` is required. Use `"All"` to cancel all orders across all instruments, or `"Instrument"` with an `instrument` to cancel orders for a specific instrument only.

For signature construction, see [Signatures - Mass Cancel](/developer-tools/signatures#mass-cancel).

### Success Response

```json
{
  "type": "massCanceled",
  "numberOfOrders": 5,
  "instrument": "NVDA",
  "targetScope": "Instrument"
}
```

`instrument` and `targetScope` are echoed from the request so a client with several in-flight `cancelAll`s can tell which one this answers. `instrument` is omitted for an all-instruments cancel (`targetScope: "All"`).

### Common Errors

| Code  | Cause                         |
| ----- | ----------------------------- |
| 4000  | Invalid request               |
| 4001  | Signature verification failed |
| 4002  | Invalid nonce                 |
| 4029  | Rate limit exceeded           |
| 5000  | Internal error                |
| 5001+ | Exchange error                |

***

## transferMargin

Move collateral between cross margin and a market's isolated margin, without leaving the WebSocket session. Identical semantics to [`POST /api/margin/transfer`](/developer-tools/rest-api/authenticated-endpoints#post-apimargintransfer).

### Request

```json
{
  "type": "transferMargin",
  "amount": "250.00",
  "instrument": "BTC",
  "direction": "crossToIsolated",
  "signature": {
    "sig": "0x...",
    "payload": "0x08..."
  }
}
```

### Request Fields

| Field        | Type   | Required | Description                                                                                                                                                                          |
| ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `amount`     | string | Yes      | Transfer amount (human-readable)                                                                                                                                                     |
| `instrument` | string | Yes      | Instrument symbol of the isolated market                                                                                                                                             |
| `direction`  | string | Yes      | `crossToIsolated` or `isolatedToCross`                                                                                                                                               |
| `signature`  | Object | Yes      | EIP-712 MarginTransfer signature, type `0x08` — flag bit 0 carries the **source** account's mode (see [Signatures - Margin Transfers](/developer-tools/signatures#margin-transfers)) |

### Success Response

```json
{
  "type": "marginTransferred",
  "commandId": 100004
}
```

The ack only means the transfer was submitted. The settlement result arrives asynchronously as a `TransferMargin` message on the [Execution Reports](/developer-tools/websocket-api/execution-reports) stream.

***

## Trader Order ID

`traderOrderId` is an **optional** client-supplied identifier for an order.

### Requirements

* **Maximum width: 36 bits** — any `u64` up to `68,719,476,735` (`2^36 − 1`) is accepted. Values larger than this are rejected. Within that range, choose whatever encoding you prefer (monotonic counter, timestamp-derived, random, etc.).
* Optional on `placeOrder`.
* If supplied, the value must be unique per instrument across your own active and historical orders (you cannot reuse a `traderOrderId` you have previously submitted on the same instrument from the same wallet).

### Using `traderOrderId` for subsequent operations

Once an order is placed with a `traderOrderId`, that value can be used in place of the exchange `orderId` when:

* Cancelling via `cancelOrder` (see [cancelOrder](#cancelorder))
* Replacing via `replaceOrder` (see [replaceOrder](#replaceorder))
* Filtering historic orders and trades (see [REST API](/developer-tools/rest-api/public-endpoints))

***


# Execution Reports

Real-time order and account event notifications via WebSocket

Execution reports are automatically streamed after calling `subscribeTrading`. All execution reports use the same wrapper format:

```json
{
  "type": "executionReport",
  ...
}
```

**Note:** Execution reports echo back the signatures sent in requests. The `signature.payload` field contains the hex-encoded payload with type discriminator that was originally submitted (see [Signatures](/developer-tools/signatures) for payload format details).

Execution reports provide:

1. Resting order snapshot
   1. **Initial snapshot** - All resting orders as `OrderStatus` messages. The snapshot contains all active, pending, partially filled, and suspended orders for the account.
2. **Real-time updates** - Ongoing execution reports
   1. Order status changes (new, filled, cancelled, etc.)
   2. Trade execution notifications
   3. Account events (deposits, withdrawals, leverage updates, liquidations, funding)

***

## Message Types

Execution reports contain a `messageType` field indicating the event type:

### Order Events

| Message Type  | Description                                       |
| ------------- | ------------------------------------------------- |
| `Add`         | Order accepted and added to book                  |
| `Executed`    | Order partially or fully filled                   |
| `Cancelled`   | Order cancelled                                   |
| `Modified`    | Order replaced                                    |
| `Suspended`   | Stop order waiting for trigger                    |
| `OrderStatus` | Order snapshot (sent during initial subscription) |
| `TradeReport` | Trade execution details                           |
| `TradeCancel` | Trade cancellation                                |

### Account Events

| Message Type        | Description                        |
| ------------------- | ---------------------------------- |
| `DepositMargin`     | Collateral deposit                 |
| `WithdrawMargin`    | Collateral withdrawal              |
| `TransferMargin`    | Cross ↔ isolated margin transfer   |
| `UpdateLeverage`    | Leverage update                    |
| `FundingRates`      | Funding rate payment               |
| `Liquidation`       | Position liquidation               |
| `ManualLiquidation` | Manual liquidation / ADL execution |

***

## Order Execution Reports

### Add

Order accepted and added to order book.

```json
{
  "type": "executionReport",
  "messageType": "Add",
  "side": "Buy",
  "instrument": "NVDA",
  "quantity": "100",
  "price": "500.00",
  "eventTimestamp": 1705600000000000000,
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "orderType": "Limit",
  "timeInForce": "GTC",
  "orderTimestamp": 1705600000000000000,
  "filledQuantity": "0",
  "filledPrice": "0",
  "remainingOpenQuantity": "100",
  "signature": {
    "Order": {
      "sig": "0x...",
      "payload": "0x..."
    }
  }
}
```

### Executed

Order partially or fully filled.

```json
{
  "type": "executionReport",
  "messageType": "Executed",
  "side": "Buy",
  "instrument": "NVDA",
  "quantity": "100",
  "price": "500.00",
  "eventTimestamp": 1705600001000000000,
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "orderType": "Limit",
  "timeInForce": "GTC",
  "filledQuantity": "100",
  "filledPrice": "500.00",
  "remainingOpenQuantity": "0",
  "lastFilledQuantity": "100",
  "lastFilledPrice": "500.00",
  "makerTaker": "Maker",
  "matchId": 987654321,
  "signature": {
    "Order": {
      "sig": "0x...",
      "payload": "0x..."
    }
  }
}
```

| Field                | Description                        |
| -------------------- | ---------------------------------- |
| `filledQuantity`     | Total filled quantity (cumulative) |
| `filledPrice`        | Weighted average filled price      |
| `lastFilledQuantity` | Quantity filled in this execution  |
| `lastFilledPrice`    | Price of this execution            |
| `makerTaker`         | `Maker` or `Taker`                 |
| `matchId`            | Unique match identifier            |

### Cancelled

Order cancelled (by user or system).

```json
{
  "type": "executionReport",
  "messageType": "Cancelled",
  "side": "Buy",
  "instrument": "NVDA",
  "quantity": "100",
  "price": "500.00",
  "eventTimestamp": 1705600002000000000,
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "orderType": "Limit",
  "timeInForce": "GTC",
  "filledQuantity": "40",
  "filledPrice": "500.00",
  "remainingOpenQuantity": "0",
  "cancelReason": "User requested",
  "cancelledQuantity": "60",
  "signature": {
    "Order": {
      "sig": "0x...",
      "payload": "0x..."
    }
  }
}
```

| Field               | Description                      |
| ------------------- | -------------------------------- |
| `cancelReason`      | Reason for cancellation          |
| `cancelledQuantity` | Quantity cancelled in this event |

### Modified

Order replaced with new parameters.

```json
{
  "type": "executionReport",
  "messageType": "Modified",
  "side": "Buy",
  "instrument": "NVDA",
  "quantity": "200",
  "price": "510.00",
  "eventTimestamp": 1705600003000000000,
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "orderType": "Limit",
  "timeInForce": "GTC",
  "filledQuantity": "0",
  "filledPrice": "0",
  "remainingOpenQuantity": "200",
  "signature": {
    "Order": {
      "sig": "0x...",
      "payload": "0x..."
    }
  }
}
```

### Suspended

Stop order waiting for trigger price.

```json
{
  "type": "executionReport",
  "messageType": "Suspended",
  "side": "Buy",
  "instrument": "NVDA",
  "quantity": "100",
  "price": "0",
  "stopPrice": "490.00",
  "eventTimestamp": 1705600000000000000,
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "orderType": "Stop",
  "timeInForce": "IOC",
  "signature": {
    "Order": {
      "sig": "0x...",
      "payload": "0x..."
    }
  }
}
```

### OrderStatus

Order snapshot (sent during initial subscription).

```json
{
  "type": "executionReport",
  "messageType": "OrderStatus",
  "side": "Buy",
  "status": "Pending",
  "instrument": "NVDA",
  "quantity": "100",
  "price": "500.00",
  "eventTimestamp": 1705600000000000000,
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "orderType": "Limit",
  "timeInForce": "GTC",
  "filledQuantity": "40",
  "filledPrice": "500.00",
  "remainingOpenQuantity": "60"
}
```

For a resting SL/TP leg, the snapshot also carries `closePosition`:

```json
{
  "type": "executionReport",
  "messageType": "OrderStatus",
  "side": "Sell",
  "status": "Suspended",
  "instrument": "BTC",
  "quantity": "0.05",
  "price": "0",
  "stopPrice": "58000.00",
  "closePosition": true,
  "eventTimestamp": 1705600000000000000,
  "traderOrderId": 1705600000003,
  "orderId": 123456791,
  "orderType": "StopLoss",
  "timeInForce": "GTC",
  "filledQuantity": "0",
  "filledPrice": "0",
  "remainingOpenQuantity": "0.05"
}
```

**Order Status Values:**

* `Active` - Order resting in order book
* `Suspended` - Stop order awaiting trigger
* `Executed` - Order executed (partially or fully filled)
* `Cancelled` - Order cancelled
* `Pending` - Order accepted, awaiting processing
* `Ended` - Order is no longer active (expired or otherwise terminated)

### TradeReport

Report sent for every trade.

```json
{
  "type": "executionReport",
  "messageType": "TradeReport",
  "side": "Sell",
  "instrument": "NVDA",
  "quantity": "100",
  "price": "500.00",
  "eventTimestamp": 1705600001000000000,
  "matchId": 987654321,
  "tradeType": "Matched"
}
```

### TradeCancel

Trade busted because its on-chain settlement failed (dropped or reverted). The fill is reversed.

```json
{
  "type": "executionReport",
  "messageType": "TradeCancel",
  "side": "Buy",
  "instrument": "NVDA",
  "quantity": "100",
  "price": "500.00",
  "eventTimestamp": 1705600002000000000,
  "traderOrderId": 1705600000001,
  "orderId": 123456789,
  "matchId": 987654321,
  "tradeType": "Matched"
}
```

***

## Account Event Reports

### DepositMargin

Collateral deposit event.

```json
{
  "type": "executionReport",
  "messageType": "DepositMargin",
  "id": 100001,
  "eventTimestamp": 1705600000000000000,
  "message": {
    "messageType": "depositMargin",
    "amount": "1000.00",
    "status": "submitted",
    "permitSignature": "0x05...",
    "depositSignature": {
      "sig": "0x...",
      "payload": "0x07..."
    },
    "transactionHash": "0xabcd..."
  }
}
```

### WithdrawMargin

Collateral withdrawal event.

```json
{
  "type": "executionReport",
  "messageType": "WithdrawMargin",
  "id": 100002,
  "eventTimestamp": 1705600001000000000,
  "message": {
    "messageType": "withdrawMargin",
    "amount": "500.00",
    "status": "settled",
    "signature": {
      "sig": "0x...",
      "payload": "0x..."
    },
    "transactionHash": "0xef01..."
  }
}
```

### TransferMargin

Cross ↔ isolated margin transfer event.

```json
{
  "type": "executionReport",
  "messageType": "TransferMargin",
  "id": 100004,
  "eventTimestamp": 1705600003000000000,
  "message": {
    "messageType": "transferMargin",
    "amount": "250.00",
    "instrument": "BTC",
    "direction": "crossToIsolated",
    "status": "settled",
    "signature": {
      "sig": "0x...",
      "payload": "0x08..."
    },
    "transactionHash": "0x2345..."
  }
}
```

`direction` is `crossToIsolated` or `isolatedToCross`. A failed transfer carries `status: "failed"` and a `reason` field instead of `transactionHash`.

### UpdateLeverage

Leverage update event.

```json
{
  "type": "executionReport",
  "messageType": "UpdateLeverage",
  "id": 100003,
  "eventTimestamp": 1705600002000000000,
  "message": {
    "messageType": "updateLeverage",
    "leverage": 1000,
    "instrument": "NVDA",
    "status": "settled",
    "signature": {
      "sig": "0x...",
      "payload": "0x..."
    }
  }
}
```

**Note:** `leverage` value is leverage × 100 (e.g., 1000 = 10x leverage)

### FundingRates

Funding rate payment event.

```json
{
  "type": "executionReport",
  "messageType": "FundingRates",
  "id": 100004,
  "eventTimestamp": 1705600003000000000,
  "message": {
    "messageType": "fundingRates",
    "instant": "2024-01-18T12:00:00Z",
    "fundingVec": [
      {
        "symbol": "AAPL",
        "markPrice": "180.50",
        "fundingRate": "0.0001"
      },
      {
        "symbol": "NVDA",
        "markPrice": "500.25",
        "fundingRate": "0.00015"
      }
    ],
    "status": "settled"
  }
}
```

### Liquidation

Position liquidation event.

```json
{
  "type": "executionReport",
  "messageType": "Liquidation",
  "id": 100005,
  "eventTimestamp": 1705600004000000000,
  "message": {
    "messageType": "liquidation",
    "instrument": "NVDA",
    "filledQuantity": "100",
    "remainingQuantity": "0",
    "status": "settled",
    "reason": "Insufficient margin"
  }
}
```

| Field        | Type   | Description                 |
| ------------ | ------ | --------------------------- |
| `instrument` | string | Instrument being liquidated |

### ManualLiquidation

Manual liquidation or ADL (auto-deleveraging) execution event.

```json
{
  "type": "executionReport",
  "messageType": "ManualLiquidation",
  "id": 100006,
  "eventTimestamp": 1705600005000000000,
  "message": {
    "messageType": "manualLiquidation",
    "matchId": 987654321,
    "status": "settled",
    "reason": "ADL",
    "transactionHash": "0xabcd..."
  }
}
```

| Field             | Type   | Description                               |
| ----------------- | ------ | ----------------------------------------- |
| `matchId`         | u64    | Match identifier for the liquidation fill |
| `status`          | string | Transaction status (see below)            |
| `reason`          | string | *(Optional)* Reason for the liquidation   |
| `transactionHash` | string | *(Optional)* On-chain transaction hash    |

***

## Transaction Status

Account events include a `status` field:

| Status      | Description                             |
| ----------- | --------------------------------------- |
| `submitted` | Operation submitted, pending processing |
| `settled`   | Operation completed successfully        |
| `failed`    | Operation failed                        |

***

## Common Fields

All execution reports include:

| Field            | Type   | Description                        |
| ---------------- | ------ | ---------------------------------- |
| `type`           | string | Always `"executionReport"`         |
| `messageType`    | string | Event type (see tables above)      |
| `eventTimestamp` | i64    | Event timestamp (Unix nanoseconds) |

Order events include:

| Field           | Type    | Description                                                                                                                                     |
| --------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `side`          | string  | `Buy` or `Sell`                                                                                                                                 |
| `instrument`    | string  | Instrument symbol                                                                                                                               |
| `orderId`       | u64     | Exchange order ID                                                                                                                               |
| `traderOrderId` | u64     | *(Optional)* Your client order ID. Omitted for system-generated orders (e.g. liquidations), which have no client ID                             |
| `quantity`      | string  | Order quantity                                                                                                                                  |
| `price`         | string  | Order price                                                                                                                                     |
| `reduceOnly`    | boolean | *(Optional)* Whether the order was signed reduce-only. Omitted when the report carries no order payload, so absent means *unknown*, not `false` |
| `closePosition` | boolean | *(Optional)* SL/TP only: `true` = managed (auto-syncs to position size), `false` = unmanaged (fixed qty)                                        |
| `signature`     | Object  | *(Optional)* Original signed payload echoed back. See [Signatures](/developer-tools/signatures) for format                                      |

**Note:** `reduceOnly` tells you why a resting order changed size or was cancelled on its own — a reduce-only order is kept in step with your position. See [Reduce-Only Orders](/about/trading/order-types#reduce-only-orders).

**Note:** The `signature` field is included in `Add`, `Executed`, `Cancelled`, `Modified`, and `Suspended` messages to provide cryptographic proof of the original order. It is also included in `OrderStatus` snapshots, and omitted from `TradeReport` and `TradeCancel` messages.

***


# Liquidation Auction

Real-time auction that lets subscribed trading clients claim positions during the manual phase of a liquidation.

When an account is liquidated, positions that cannot be closed against the live order book enter a short **manual-phase auction** before falling back to auto-deleveraging (ADL). The gateway broadcasts the unfilled position to all subscribed trading clients and collects signed claims for the duration of the auction window. The first claimant with sufficient margin becomes the counterparty and takes over the position at the liquidatee's zero-price.

See [Liquidation](/about/trading/liquidation) for where the auction sits in the overall liquidation waterfall.

***

## Eligibility

To participate in auctions a connection must:

1. **Authenticate** (see [Connection Management](/developer-tools/websocket-api/connection-management))
2. **Subscribe** to the trading stream via `subscribeTrading`

Subscribed connections automatically receive `manualLiquidationOffer` broadcasts — no separate subscription is required. Clients that are not subscribed when the auction opens never see the offer, and positions are routed straight to ADL if no client is subscribed.

***

## Auction Flow

```
Gateway                                          Subscribed client
   │                                                     │
   │  ── manualLiquidationOffer ───────────────────────► │   (broadcast to all
   │     {offerId, instrument, takerSide,                │    subscribed clients)
   │      quantity, frozenPrice, deadlineMs}             │
   │                                                     │
   │                                                     │  sign EIP-712 Order
   │                                                     │  matching offer terms
   │                                                     │
   │  ◄── claimLiquidation ───────────────────────────── │
   │      {offerId, signedOrder}                         │
   │                                                     │
   │  ── claimLiquidationAccepted ─────────────────────► │   (claim entered the
   │     {offerId}                                       │    auction)
   │                                                     │
   │   ... auction window closes (~500ms) ...            │
   │   pick winner, enter trade                          │
   │                                                     │
   │  ── executionReport ──────────────────────────────► │   (winner only: the
   │     (trade fill for the claimed position)           │    resulting fill)
```

1. Gateway broadcasts a `manualLiquidationOffer` to every subscribed client.
2. Interested clients reply with `claimLiquidation` carrying an EIP-712-signed order whose fields match the offer exactly.
3. Each accepted claim is acknowledged with `claimLiquidationAccepted`. The ack only confirms the claim entered the auction — it is **not** a win.
4. After the auction window closes, the gateway selects a winner and enters the trade. The winner observes the resulting fill on the [execution report](/developer-tools/websocket-api/execution-reports) stream. Losing claimants receive nothing further.

***

## manualLiquidationOffer

Broadcast pushed to all subscribed clients when a position enters the manual-phase auction.

**Direction:** Gateway → Client

### Message

```json
{
  "type": "manualLiquidationOffer",
  "offerId": "550e8400-e29b-41d4-a716-446655440000",
  "instrumentId": 42,
  "instrument": "NVDA",
  "takerSide": "Buy",
  "quantity": "100",
  "frozenPrice": "498.50",
  "deadlineMs": 1705600000200
}
```

### Fields

| Field          | Type   | Description                                                                                      |
| -------------- | ------ | ------------------------------------------------------------------------------------------------ |
| `offerId`      | string | UUID identifying this auction. Echo it back in `claimLiquidation`.                               |
| `instrumentId` | u32    | Numeric instrument ID of the position being liquidated.                                          |
| `instrument`   | string | Instrument symbol (e.g. `NVDA`).                                                                 |
| `takerSide`    | string | `Buy` or `Sell` — the side **you** take if you win (opposite of the liquidatee's closing side).  |
| `quantity`     | string | Quantity available to claim. Sign your order's `size` according to `takerSide`.                  |
| `frozenPrice`  | string | The execution (zero) price. Your signed order's `limitPrice` must equal this value.              |
| `deadlineMs`   | i64    | Unix timestamp (ms) at which the auction window closes. Claims arriving after this are rejected. |

***

## claimLiquidation

Submit a claim for an open offer. Fast-pathed past the request queue so it is not delayed behind other order operations during the short auction window.

**Authentication Required:** Yes

### Request

```json
{
  "type": "claimLiquidation",
  "offerId": "550e8400-e29b-41d4-a716-446655440000",
  "marginMode": "Cross",
  "signedOrder": {
    "sig": "0x...",
    "payload": "0x00..."
  }
}
```

**Note:** `signedOrder` carries an EIP-712 `Order` payload (type discriminator `0x00`), identical in shape to the `placeOrder` signature. See [Signatures - Orders](/developer-tools/signatures#orders).

### Request Fields

| Field         | Type   | Required | Description                                                                                |
| ------------- | ------ | -------- | ------------------------------------------------------------------------------------------ |
| `offerId`     | string | Yes      | The `offerId` from the `manualLiquidationOffer` you are claiming.                          |
| `marginMode`  | string | Yes      | Must be `Cross` — isolated claims are rejected. Must match the signed order's margin flag. |
| `signedOrder` | Object | Yes      | EIP-712 signed `Order` whose fields match the offer terms exactly.                         |

### Signed Order Requirements

The gateway validates the signed order against the offer terms. All of the following must hold or the claim is rejected:

| Order field  | Required value                                                                                                                                                                                                                         |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `account`    | Your authenticated wallet (the recovered signer must also match this address).                                                                                                                                                         |
| `market`     | The offer's `instrumentId`.                                                                                                                                                                                                            |
| `size`       | The offer's `quantity`, signed per `takerSide` (positive = `Buy`, negative = `Sell`).                                                                                                                                                  |
| `limitPrice` | The offer's `frozenPrice`.                                                                                                                                                                                                             |
| `deadline`   | A Unix timestamp (seconds) at least **1 hour** in the future — the claim validates as an IOC order, so the standard 1-hour minimum applies. A claim whose own signed deadline has already passed when the winner is picked is skipped. |
| `flags`      | Regular cross-margin order flags (not a managed close, not reduce-only).                                                                                                                                                               |

The order is treated as a `Limit` / `IOC` order. For payload encoding and flag bits, see [Signatures - Orders](/developer-tools/signatures#orders).

### Success Response

```json
{
  "type": "claimLiquidationAccepted",
  "offerId": "550e8400-e29b-41d4-a716-446655440000"
}
```

`claimLiquidationAccepted` only confirms your claim was validated and entered the auction. **Winning is determined after the window closes.** Once it resolves, every claimant whose claim was accepted receives a private `claimLiquidationResult` message (see below). Winners additionally receive the resulting position fill on the [execution report](/developer-tools/websocket-api/execution-reports) stream.

### Claim Result

After the auction resolves, the gateway sends a `claimLiquidationResult` to **each accepted claimant** — winners (`won: true`) and losers (`won: false`) alike. It is delivered privately to the claimant's authenticated connection(s).

```json
{
  "type": "claimLiquidationResult",
  "offerId": "550e8400-e29b-41d4-a716-446655440000",
  "won": false
}
```

| Field     | Type    | Description                                              |
| --------- | ------- | -------------------------------------------------------- |
| `offerId` | string  | UUID of the liquidation offer that was claimed.          |
| `won`     | boolean | `true` if your claim won the auction, `false` otherwise. |

Winners also receive the resulting position fill on the [execution report](/developer-tools/websocket-api/execution-reports) stream.

### Common Errors

| Code | Cause                                                                                                 |
| ---- | ----------------------------------------------------------------------------------------------------- |
| 4000 | Invalid request — unknown `offerId`, auction already closed, or a duplicate claim for the same offer. |
| 4001 | Signature verification failed, or `signed.account` does not match the authenticated wallet.           |
| 4003 | Not authenticated (authenticate before claiming).                                                     |
| 4029 | Rate limit exceeded.                                                                                  |
| 4100 | Insufficient margin to take the position (your account cannot absorb the claimed quantity).           |

***

## Winner Selection

Claims are filled **first-come, first-served** by gateway receive time. After the window closes, the gateway walks claims in arrival order and picks the first one whose signed `deadline` has not passed and whose margin can absorb the position. The winner is entered as the maker-side counterparty against the liquidatee at `frozenPrice`. If no claim is eligible, the position falls through to ADL.

Only one claim per wallet per offer is accepted — a duplicate is rejected with `4000`.


# Account Summary Stream

Real-time account summary (margin, positions, withdrawable) via WebSocket

The account summary stream pushes a fresh snapshot of your account — withdrawable balance, margin usage, and open positions — **whenever your position, margin, or balance state changes**: fills, trade busts, liquidations, funding payments, leverage changes, and deposit/withdraw settlements. The payload is the full account summary, identical in shape to the REST [`GET /api/account_summary`](/developer-tools/rest-api/public-endpoints) endpoint.

It does **not** push on working-order margin — placing, cancelling, or replacing an order, or reserving margin for a liquidation-auction claim — nor on mark-price ticks. Poll `GET /api/account_summary` when you need withdrawable/margin figures that reflect those. See [What triggers a push](#what-triggers-a-push) below.

This channel is **independent** of `subscribeTrading` — a connection can subscribe to account summaries without execution reports, or to both. A market maker will typically want **both**: `subscribeAccountSummary` for position snapshots and `subscribeTrading` for order/fill and account events.

***

## Subscribing

Send `subscribeAccountSummary` (requires an authenticated connection). The gateway replies with an immediate `accountSummary` snapshot, then pushes a fresh summary on every subsequent change.

**Request:**

```json
{
  "type": "subscribeAccountSummary"
}
```

The first `accountSummary` message is the initial snapshot; there is no separate success acknowledgement.

To stop receiving updates:

```json
{
  "type": "unsubscribeAccountSummary"
}
```

***

## Account Summary Message

The summary is **combined**: the wallet's cross and isolated margin in one payload, positions tagged with `marginMode`.

```json
{
  "type": "accountSummary",
  "assetPositions": [
    {
      "instrument": "NVDA",
      "marginMode": "Cross",
      "entryPx": "500.00",
      "leverage": 10,
      "liquidationPx": "450.00",
      "marginUsed": "100.00",
      "maxLeverage": 20,
      "positionValue": "1000.00",
      "returnOnEquity": "0.05",
      "szi": "2",
      "unrealizedPnl": "50.00"
    },
    {
      "instrument": "BTC",
      "marginMode": "Isolated",
      "entryPx": "65000.00",
      "leverage": 10,
      "liquidationPx": "59240.50",
      "marginUsed": "650.00",
      "maxLeverage": 40,
      "positionValue": "6500.00",
      "returnOnEquity": "0",
      "szi": "0.1",
      "unrealizedPnl": "0",
      "transferable": "0.00"
    }
  ],
  "crossMaintenanceMarginUsed": "25.00",
  "crossMarginSummary": {
    "accountValue": "10000.00",
    "totalMarginUsed": "100.00",
    "totalNtlPos": "1000.00",
    "totalRawUsd": "9950.00"
  },
  "maintenanceMarginUsed": "106.25",
  "marginSummary": {
    "accountValue": "10650.00",
    "totalMarginUsed": "750.00",
    "totalNtlPos": "7500.00",
    "totalRawUsd": "10600.00"
  },
  "time": 1705600000000,
  "withdrawable": "9900.00"
}
```

The payload is identical to the REST `GET /api/account_summary` response with a `"type": "accountSummary"` discriminator added. See [Account Summary (REST)](/developer-tools/rest-api/public-endpoints#get-apiaccount_summary) for the complete field reference.

| Field                        | Type   | Description                                |
| ---------------------------- | ------ | ------------------------------------------ |
| `type`                       | string | Always `"accountSummary"`                  |
| `assetPositions`             | Array  | Cross and isolated positions               |
| `crossMaintenanceMarginUsed` | string | Maintenance margin used (cross only)       |
| `crossMarginSummary`         | Object | Margin summary (cross only)                |
| `maintenanceMarginUsed`      | string | Maintenance margin used (cross + isolated) |
| `marginSummary`              | Object | Combined margin summary (cross + isolated) |
| `time`                       | i64    | Snapshot timestamp (milliseconds)          |
| `withdrawable`               | string | Cross collateral available for withdrawal  |

***

## What triggers a push

A push is emitted whenever your position, margin, or balance state changes:

* **Fills** — a position opened, increased, reduced, closed, or flipped.
* **Trade busts** — a previously reported fill reversed.
* **Liquidations** — manual-liquidation and ADL executions.
* **Funding payments** — settled funding changes your balance (and therefore `liquidationPx` / `withdrawable`).
* **Leverage changes** — a successful leverage update changes the position's `leverage`, `marginUsed`, and `liquidationPx`.
* **Deposit / withdraw settlements** — a settled collateral change moves your balance (and `liquidationPx` / `withdrawable`).
* **Margin transfers** — a settled cross ↔ isolated transfer reallocates your collateral (combined totals unchanged; cross figures and `withdrawable` shift).

The following do **not** trigger a push:

* **Order placement, cancel, and replace** — reserving/releasing working-order margin changes `withdrawable`, but not your position or realized balance. Order events arrive on the [Execution Reports](/developer-tools/websocket-api/execution-reports) stream; poll `GET /api/account_summary` for the resulting `withdrawable`.
* **Liquidation-auction claim reservations** — reserved claim margin, likewise pollable.
* **Mark-price ticks** — fields derived from the mark price (`unrealizedPnl`, `withdrawable`, `transferable`, `liquidationPx`, `returnOnEquity`, `accountValue`) reflect the mark price at the moment of the last push and are **not** refreshed when the mark price alone moves. Recompute these locally from your own price feed and current positions if you need them between events.

***

## Delivery

* **Snapshot on subscribe**, then a push on each position / margin / balance change.
* **Coalesced**: multiple changes within a short window batch into a single push carrying the net resulting state, rather than one message per event.
* Each message is a **complete snapshot** (not a delta). **Always treat the message with the largest `time` as the current state** — do not assume arrival order. In particular, a change that occurs while you are subscribing can produce a push that arrives *around or before* the initial snapshot; keying on `time` (last-write-wins) resolves this correctly since every message carries the full account state as of its `time`.
* Per-order acknowledgements, fills, and account events arrive separately and immediately on the [Execution Reports](/developer-tools/websocket-api/execution-reports) stream (`subscribeTrading`); the account summary stream is the aggregate position/margin view.

***


# Market Data

Public market data WebSocket API for real-time market information

Public market data is available via WebSocket without authentication.

**Endpoint**: `wss://marketdata.app.hello.trade/ws`

## Available Channels

| Channel                                                                    | Description                                                                                                 |
| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| [ticker](/developer-tools/websocket-api/market-data/light-tickers)         | Market data snapshots with prices, volume, 24h stats, mark prices, and funding rates                        |
| [orderbook](/developer-tools/websocket-api/market-data/partial-order-book) | Order book snapshots with configurable depth (1, 5, 10, 20, or 100 levels), sampled or live on every change |
| [trades](/developer-tools/websocket-api/market-data/live-trades)           | Real-time trade executions                                                                                  |
| [candles](/developer-tools/websocket-api/market-data/candles)              | OHLCV candlestick data with configurable intervals                                                          |
| [instruments](/developer-tools/websocket-api/market-data/instrument-list)  | Instrument list with metadata and status                                                                    |

***

## Connection

Connect to the WebSocket endpoint and subscribe to channels using JSON messages.

**CLI Example:**

```bash
wscat -c wss://marketdata.app.hello.trade/ws

# Subscribe to ticker for BTC and ETH
{"type":"subscribe","channel":"ticker","symbols":["BTC","ETH"]}

# Subscribe to orderbook
{"type":"subscribe","channel":"orderbook","symbols":["BTC"],"levels":10}

# Subscribe to all instruments
{"type":"subscribe","channel":"instruments"}
```

***

## Subscription Format

**Subscribe Request:**

```json
{
  "type": "subscribe",
  "channel": "channelName",
  "sid": "my-request-1",
  "symbols": ["BTC", "ETH"]
}
```

| Field     | Type      | Required | Description                                                                                                                                                                                                                                                                                                                                                                 |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`    | string    | Yes      | Must be `"subscribe"`                                                                                                                                                                                                                                                                                                                                                       |
| `channel` | string    | Yes      | Channel name (see Available Channels)                                                                                                                                                                                                                                                                                                                                       |
| `sid`     | string    | No       | Client-supplied identifier, echoed back on every response tied to this subscription (`subscribed` and `marketData` messages). Lets a client open multiple concurrent requests for the same channel/symbol (e.g. two different candle date ranges) and tell their responses apart, instead of relying on symbol matching. Omit it and nothing changes from today's behavior. |
| `symbols` | string\[] | Varies   | List of symbols (required for most channels, optional for `instruments`)                                                                                                                                                                                                                                                                                                    |

**Unsubscribe Request:**

```json
{
  "type": "unsubscribe",
  "channel": "channelName",
  "sid": "my-request-1",
  "symbols": ["BTC"]
}
```

`sid` is optional here too and is only echoed back on the `unsubscribed` confirmation — it doesn't change which subscription gets torn down (that's still determined by `channel` + params).

**Response Format:**

```json
{
  "type": "marketData",
  "channel": "channelName",
  "sid": "my-request-1",
  "data": {
    // Channel-specific data
  }
}
```

`sid` is only present in the response if the subscribe request included one; otherwise the field is omitted entirely, exactly as before.

***

## Error Response

```json
{
  "type": "error",
  "message": "Invalid subscription request",
  "code": 400
}
```

| Field     | Type      | Description                                                                                      |
| --------- | --------- | ------------------------------------------------------------------------------------------------ |
| `type`    | string    | Always `"error"`                                                                                 |
| `message` | string    | Human-readable reason                                                                            |
| `code`    | number    | See the table below                                                                              |
| `sid`     | string    | The request's `sid`. Only present on unknown-symbol errors, and only if the request included one |
| `symbols` | string\[] | The symbols the error applies to. Only present on unknown-symbol errors                          |

| Code | Description                                                   |
| ---- | ------------------------------------------------------------- |
| 400  | Invalid request, or unknown symbol                            |
| 429  | Too many candle history requests in flight on this connection |
| 503  | Live orderbook not yet loaded after a server restart; retry   |

### Unknown symbols

The `ticker`, `orderbook`, `trades` and `candles` channels only accept symbols listed on the [instruments](/developer-tools/websocket-api/market-data/instrument-list) channel. Any other symbol is rejected with a single error naming every rejected symbol:

```json
{
  "type": "error",
  "message": "Unknown symbols: ADBE, 1138.HK",
  "code": 400,
  "sid": "my-request-1",
  "symbols": ["ADBE", "1138.HK"]
}
```

* Valid symbols in the same request are still subscribed. The `subscribed` confirmation lists only the symbols that were accepted.
* If every symbol is rejected, no `subscribed` confirmation is sent.
* Use the `symbols` array rather than parsing `message`.

See individual channel documentation for detailed specifications and examples.


# Light Tickers

Real-time market data with 24-hour statistics, mark prices, and funding rates.

**Channel:** `ticker`

## Overview

## Connection

Connect to the WebSocket endpoint:

```
wss://marketdata.app.hello.trade/ws
```

## Subscription Request

```json
{
  "type": "subscribe",
  "channel": "ticker",
  "symbols": ["BTC", "ETH", "NVDA"],
  "interval": 1000
}
```

| Field      | Type      | Required | Description                                                              |
| ---------- | --------- | -------- | ------------------------------------------------------------------------ |
| `type`     | string    | Yes      | Must be `"subscribe"`                                                    |
| `channel`  | string    | Yes      | Must be `"ticker"`                                                       |
| `symbols`  | string\[] | Yes      | List of instrument symbols                                               |
| `interval` | Number    | No       | Update interval in milliseconds: `100`, `1000`, `2000` (default: `1000`) |

## Unsubscription Request

```json
{
  "type": "unsubscribe",
  "channel": "ticker",
  "symbols": ["BTC"]
}
```

## Response

```json
{
  "type": "marketData",
  "channel": "ticker",
  "data": {
    "symbol": "BTC",
    "lastPrice": "45000.75",
    "lastPriceTimestamp": 1705596400000,
    "lastQuantity": "0.5",
    "bidPrice": "45000.50",
    "bidQuantity": "2.5",
    "askPrice": "45001.25",
    "askQuantity": "1.8",
    "timeStamp": 1705596400000,
    "openingPrice": "44500.00",
    "closingPrice": "45000.00",
    "low": "44000.00",
    "high": "46000.00",
    "volume": "1500.50",
    "quoteVolume": "67500000.00",
    "status": "Trading",
    "volumeTypes": [
      { "type": "Matched", "volume": "1450.00" },
      { "type": "RFQ", "volume": "50.50" }
    ],
    "priceLimits": {
      "minPrice": "40000.00",
      "maxPrice": "50000.00"
    },
    "markPrice": "45000.85",
    "indexPrice": "45001.00",
    "fundingRate": "0.0001",
    "nextFundingTime": 1705600000
  }
}
```

| Field                 | Type       | Description                                                        |
| --------------------- | ---------- | ------------------------------------------------------------------ |
| `symbol`              | string     | Instrument symbol                                                  |
| `lastPrice`           | string     | Last execution price (omitted if no trades)                        |
| `lastPriceTimestamp`  | Number     | Unix timestamp (milliseconds) of last price (omitted if no trades) |
| `lastQuantity`        | string     | Last execution quantity (omitted if no trades)                     |
| `bidPrice`            | string     | Highest bid price (omitted if no bids)                             |
| `bidQuantity`         | string     | Total quantity at best bid (0 if no bids)                          |
| `askPrice`            | string     | Lowest ask price (omitted if no asks)                              |
| `askQuantity`         | string     | Total quantity at best ask (0 if no asks)                          |
| `timeStamp`           | Number     | Unix timestamp (milliseconds) of ticker data                       |
| `openingPrice`        | string     | 24h opening price (omitted if not available)                       |
| `closingPrice`        | string     | 24h closing price (omitted if not available)                       |
| `low`                 | string     | Lowest price in last 24h (omitted if no trades)                    |
| `high`                | string     | Highest price in last 24h (omitted if no trades)                   |
| `volume`              | string     | Total volume in last 24h (omitted if no trades)                    |
| `quoteVolume`         | string     | Total quote currency volume in last 24h (omitted if no trades)     |
| `status`              | string     | Trading status: `Trading`, `Closed`, `Halted`                      |
| `volumeTypes`         | Array      | Volume breakdown by trade type (omitted if no trades)              |
| `priceLimits`         | Object     | Min/max price limits (omitted if not configured)                   |
| `**markPrice**`       | **string** | **Current mark price from Stork Oracle**                           |
| `**indexPrice**`      | **string** | **Current index price from Stork Oracle**                          |
| `**fundingRate**`     | **string** | **Current funding rate - hourly**                                  |
| `**nextFundingTime**` | **Number** | **Unix timestamp (seconds) of next funding**                       |

## Error Response

```json
{
  "type": "error",
  "message": "Invalid subscription request",
  "code": 400
}
```

Unknown symbols are rejected with a `400` that names them. See [Unknown symbols](/developer-tools/websocket-api/market-data#unknown-symbols).


# Partial Order Book

Real-time order book snapshots with top levels of bids and asks.

**Channel:** `orderbook`

## Overview

The orderbook channel provides real-time snapshots of the order book showing configurable price levels on both bid and ask sides. Upon subscription, the current order book state is sent immediately. Afterward, updates are sent when the book changes.

## Connection

Connect to the WebSocket endpoint:

```
wss://marketdata.app.hello.trade/ws
```

## Subscription Request

```json
{
  "type": "subscribe",
  "channel": "orderbook",
  "symbols": ["BTC", "ETH"],
  "levels": 10,
  "orderbookInterval": 100
}
```

| Field               | Type      | Required | Description                                                                                                                                                                             |
| ------------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`              | string    | Yes      | Must be `"subscribe"`                                                                                                                                                                   |
| `channel`           | string    | Yes      | Must be `"orderbook"`                                                                                                                                                                   |
| `symbols`           | string\[] | Yes      | List of instrument symbols                                                                                                                                                              |
| `levels`            | Number    | No       | Number of levels: `1`, `5`, `10`, `20`, or `100` (default: `10`). Max `20` with `orderbookInterval: 0`.                                                                                 |
| `orderbookInterval` | Number    | No       | Update interval in milliseconds: `100`, `1000`, or `2000` (default: `100`). `0` selects the [live stream](#live-stream-orderbookinterval-0).                                            |
| `decimals`          | Number    | No       | Group price levels to this many decimal places: bids round down, asks round up, equal buckets merge. Omit for raw price levels. `0` groups to whole prices. Max `8` on the live stream. |

## Live Stream (`orderbookInterval: 0`)

Setting `orderbookInterval` to `0` switches from sampled snapshots to an event-driven stream. Instead of polling the venue every N milliseconds, the server maintains the full book from the exchange's order-by-order feed and pushes a new top-`levels` snapshot every time the displayed book changes.

```json
{
  "type": "subscribe",
  "channel": "orderbook",
  "symbols": ["BTC"],
  "levels": 10,
  "orderbookInterval": 0
}
```

* The response shape is identical to the sampled stream (`bids`, `asks`, `timeStamp`), so existing parsers work unchanged.
* The current book is sent immediately on subscribe, then whenever the top `levels` of either side change. Changes deeper than your requested `levels` do not produce a message.
* Messages are coalesced with a 10 ms window per symbol (capped at 100 messages per second per symbol): several changes inside the window arrive as one message carrying the newest book, never a backlog. Every message is a complete snapshot.
* `levels` is capped at `20`. Deeper event-driven data is not offered on this channel because full snapshots scale with depth times change rate.
* `timeStamp` is the server-side time of the last applied change.
* If the server's upstream feed reconnects, there is a brief pause and then a fresh snapshot. Treat any gap longer than a few seconds as a signal to fall back to the sampled stream or the `ticker` channel.
* `decimals` works the same as on the sampled stream. `levels: 100`, `decimals` above `8`, and unknown symbols are rejected with a `400`. The `subscribed` confirmation lists only the symbols that were attached.
* For a few seconds after a server restart the live stream answers `503` with `"Live orderbook is loading, retry shortly"`. Retry with backoff, or use the sampled stream meanwhile.

## Unsubscription Request

```json
{
  "type": "unsubscribe",
  "channel": "orderbook",
  "symbols": ["BTC"]
}
```

## Response

```json
{
  "type": "marketData",
  "channel": "orderbook",
  "data": {
    "symbol": "BTC",
    "bids": [
      { "price": "45000.50", "quantity": "1.5", "numberOfOrders": "3" },
      { "price": "45000.00", "quantity": "2.3", "numberOfOrders": "5" },
      { "price": "44999.50", "quantity": "1.8", "numberOfOrders": "2" }
    ],
    "asks": [
      { "price": "45001.00", "quantity": "1.2", "numberOfOrders": "2" },
      { "price": "45001.50", "quantity": "2.1", "numberOfOrders": "4" },
      { "price": "45002.00", "quantity": "1.6", "numberOfOrders": "3" }
    ],
    "timeStamp": 1705596400000
  }
}
```

| Field       | Type   | Description                                                   |
| ----------- | ------ | ------------------------------------------------------------- |
| `symbol`    | string | Instrument symbol                                             |
| `bids`      | Array  | Bid price levels, sorted by price descending (best bid first) |
| `asks`      | Array  | Ask price levels, sorted by price ascending (best ask first)  |
| `timeStamp` | Number | Unix timestamp (milliseconds) of order book snapshot          |

**Price Level Structure:**

| Field            | Type   | Description                          |
| ---------------- | ------ | ------------------------------------ |
| `price`          | string | Price level                          |
| `quantity`       | string | Total quantity at this price level   |
| `numberOfOrders` | string | Number of orders at this price level |

## Error Response

```json
{
  "type": "error",
  "message": "Invalid subscription request",
  "code": 400
}
```

| Code | When                                                                                         |
| ---- | -------------------------------------------------------------------------------------------- |
| 400  | Invalid request, `levels: 100` or `decimals` above `8` on the live stream, or unknown symbol |
| 503  | Live stream not yet loaded after a server restart; retry                                     |

Unknown symbols are rejected with a `400` that names them. See [Unknown symbols](/developer-tools/websocket-api/market-data#unknown-symbols).

## Notes

* Configurable depth: 1, 5, 10, 20, or 100 levels per side
* Bids are sorted highest price first
* Asks are sorted lowest price first
* Empty arrays indicate no orders on that side
* Updates are sent only when the order book changes
* Update interval controls snapshot frequency (100ms, 1000ms, or 2000ms), or `0` for a push on every change


# Live Trades

Real-time trade execution data.

**Channel:** `trades`

## Overview

The trades channel provides real-time trade executions for subscribed instruments. Each trade includes price, quantity, and timestamp information.

## Connection

Connect to the WebSocket endpoint:

```
wss://marketdata.app.hello.trade/ws
```

## Subscription Request

```json
{
  "type": "subscribe",
  "channel": "trades",
  "symbols": ["BTC", "ETH"],
  "limit": 1
}
```

| Field     | Type      | Required | Description                                                    |
| --------- | --------- | -------- | -------------------------------------------------------------- |
| `type`    | string    | Yes      | Must be `"subscribe"`                                          |
| `channel` | string    | Yes      | Must be `"trades"`                                             |
| `symbols` | string\[] | Yes      | List of instrument symbols                                     |
| `limit`   | Number    | No       | Number of recent trades to return on subscription (default: 1) |

## Unsubscription Request

```json
{
  "type": "unsubscribe",
  "channel": "trades",
  "symbols": ["BTC"]
}
```

## Response

```json
{
  "type": "marketData",
  "channel": "trades",
  "data": {
    "symbol": "BTC",
    "price": "45000.50",
    "quantity": "0.5",
    "timestamp": 1705596400000
  }
}
```

| Field       | Type   | Description                                                                                               |
| ----------- | ------ | --------------------------------------------------------------------------------------------------------- |
| `symbol`    | string | Instrument symbol                                                                                         |
| `price`     | string | Trade execution price                                                                                     |
| `quantity`  | string | Trade quantity. `"0"` marks the end of the snapshot; a negative value is a trade cancellation (see below) |
| `timestamp` | Number | Unix timestamp (milliseconds) of trade execution                                                          |

## Snapshot and Special Messages

On subscribe, each symbol first receives a snapshot of its most recent trades (up to `limit`), oldest first, followed by live trades as they occur.

### End of snapshot

When the snapshot is complete, the last snapshot trade is sent again with `quantity` set to `"0"` — same `price` and `timestamp`. It is sent once per symbol per subscription and is not a trade.

```json
{
  "type": "marketData",
  "channel": "trades",
  "data": {
    "symbol": "BTC",
    "price": "45000.50",
    "quantity": "0",
    "timestamp": 1705596400000
  }
}
```

If the instrument has no trades yet, the end-of-snapshot message has `price` `"0"`, `quantity` `"0"` and `timestamp` `0`.

### Trade cancellations

A cancelled trade is sent with a **negative** `quantity` equal to the cancelled trade's quantity multiplied by -1. Subtract it from any volume you have accumulated for that trade.

## Error Response

```json
{
  "type": "error",
  "message": "Invalid subscription request",
  "code": 400
}
```

Unknown symbols are rejected with a `400` that names them. See [Unknown symbols](/developer-tools/websocket-api/market-data#unknown-symbols).

## Notes

* Trades are sent in real-time as they occur
* Includes symbol, price, quantity, and timestamp information
* Treat `quantity` `"0"` as a snapshot boundary, not a trade, and negative `quantity` as a cancellation


# Instrument List

Real-time instrument list with metadata and status updates.

**Channel:** `instruments`

## Overview

The instruments channel provides a list of all available trading instruments with their metadata, including trading parameters, precision, and activity status. Upon subscription, all active instruments are sent immediately. Afterward, updates are sent when instrument details change or new instruments are added.

## Connection

Connect to the WebSocket endpoint:

```
wss://marketdata.app.hello.trade/ws
```

## Subscription Request

```json
{
  "type": "subscribe",
  "channel": "instruments"
}
```

| Field     | Type   | Required | Description             |
| --------- | ------ | -------- | ----------------------- |
| `type`    | string | Yes      | Must be `"subscribe"`   |
| `channel` | string | Yes      | Must be `"instruments"` |

**Note:** The instruments channel does not require symbols - it streams all instruments.

## Unsubscription Request

```json
{
  "type": "unsubscribe",
  "channel": "instruments",
  "symbols": []
}
```

## Response

```json
{
  "type": "marketData",
  "channel": "instruments",
  "data": {
    "id": 13,
    "symbol": "BTC",
    "activityStatus": "ACTIVE",
    "minQuantity": "0.00000001",
    "maxQuantity": "1000000",
    "pricePrecision": 8,
    "quantityPrecision": 8,
    "quantityIncrement": "0.00000001",
    "maxOrderValue": "10000000.00",
    "maxDepth": "1000000.00",
    "quoteCurrency": "USD",
    "storkMark": "BTC/USD",
    "storkIndex": "BTC/USD",
    "fundingMax": "0.0005",
    "fundingMin": "-0.0005",
    "interestRate": "0.0001",
    "maxLeverage": 10
  }
}
```

| Field               | Type   | Description                                         |
| ------------------- | ------ | --------------------------------------------------- |
| `id`                | Number | Instrument ID                                       |
| `symbol`            | string | Instrument symbol                                   |
| `activityStatus`    | string | Trading status: `ACTIVE`, `DISABLED`, or `ARCHIVED` |
| `minQuantity`       | string | Minimum order quantity                              |
| `maxQuantity`       | string | Maximum order quantity                              |
| `pricePrecision`    | Number | Decimal places for price                            |
| `quantityPrecision` | Number | Decimal places for quantity                         |
| `quantityIncrement` | string | Minimum quantity increment (may be null)            |
| `maxOrderValue`     | string | Maximum order value in quote currency (may be null) |
| `maxDepth`          | string | Maximum depth of order book (may be null)           |
| `quoteCurrency`     | string | Quote currency (e.g., `USD`)                        |
| `storkMark`         | string | Stork Oracle mark price identifier (may be null)    |
| `storkIndex`        | string | Stork Oracle index price identifier (may be null)   |
| `fundingMax`        | string | Maximum funding rate cap (may be null)              |
| `fundingMin`        | string | Minimum funding rate floor (may be null)            |
| `interestRate`      | string | Base interest rate for funding (may be null)        |
| `maxLeverage`       | Number | Maximum leverage allowed (may be null)              |

## End-of-Snapshot Marker

After all instruments in the initial snapshot have been sent, a final marker message is sent:

```json
{
  "type": "marketData",
  "channel": "instruments",
  "data": {
    "lastMessage": "Y"
  }
}
```

This marker indicates that the initial snapshot is complete. Any subsequent messages are live updates.

## Error Response

```json
{
  "type": "error",
  "message": "Invalid subscription request",
  "code": 400
}
```

## Notes

* Initial snapshot sends all active instruments followed by end-of-snapshot marker
* Updates sent when instrument metadata changes
* New instruments are streamed as they become available


# Candles

OHLCV (Open, High, Low, Close, Volume) candlestick data.

**Channel:** `candles`

## Overview

The candles channel provides real-time OHLCV candlestick data for subscribed instruments with configurable intervals.

## Connection

Connect to the WebSocket endpoint:

```
wss://marketdata.app.hello.trade/ws
```

## Subscription Request

```json
{
  "type": "subscribe",
  "channel": "candles",
  "symbols": ["BTC", "ETH"],
  "timespan": "MINUTE",
  "multiplier": 1
}
```

| Field        | Type      | Required | Description                                                                                                                                         |
| ------------ | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`       | string    | Yes      | Must be `"subscribe"`                                                                                                                               |
| `channel`    | string    | Yes      | Must be `"candles"`                                                                                                                                 |
| `symbols`    | string\[] | Yes      | List of instrument symbols                                                                                                                          |
| `timespan`   | string    | No       | Candle timespan: `"MINUTE"`, `"HOUR"`, or `"DAY"` (default: `"MINUTE"`)                                                                             |
| `multiplier` | Number    | No       | Candle multiplier: `1`, `4`, `5`, or `15` (default: `1`)                                                                                            |
| `from`       | Number    | No       | Start timestamp (Unix seconds) for historical data                                                                                                  |
| `to`         | Number    | No       | End timestamp (Unix seconds) for historical data                                                                                                    |
| `combined`   | boolean   | No       | Batch the historic backfill into a single array message instead of one message per candle (default: `false`). Only takes effect when `from` is set. |

## Unsubscription Request

```json
{
  "type": "unsubscribe",
  "channel": "candles",
  "symbols": ["BTC"]
}
```

## Response

```json
{
  "type": "marketData",
  "channel": "candles",
  "data": {
    "symbol": "BTC",
    "open": "45000.00",
    "high": "45100.00",
    "low": "44950.00",
    "close": "45050.00",
    "volume": "125.50",
    "vwa": "45025.00",
    "timeStamp": 1705596400
  }
}
```

| Field       | Type   | Description                                   |
| ----------- | ------ | --------------------------------------------- |
| `symbol`    | string | Instrument symbol                             |
| `open`      | string | Opening price of the interval                 |
| `high`      | string | Highest price during the interval             |
| `low`       | string | Lowest price during the interval              |
| `close`     | string | Closing price of the interval                 |
| `volume`    | string | Total volume during the interval              |
| `vwa`       | string | Volume weighted average price                 |
| `timeStamp` | Number | Unix timestamp (seconds) of candle start time |

## Combined (batched) historical response

By default, each historic candle in a `from`/`to` backfill is sent as its own `marketData` message, same as live updates. Setting `combined: true` on the subscription request instead batches the entire historic backfill into a single message. This is a one-shot response — no live updates follow on this subscription; subscribe again with a plain `candles` request (no `from`/`to`) for live ticks.

```json
{
  "type": "subscribe",
  "channel": "candles",
  "symbols": ["BTC"],
  "timespan": "MINUTE",
  "from": 1705500000,
  "to": 1705596400,
  "combined": true
}
```

```json
{
  "type": "marketData",
  "channel": "candles",
  "data": {
    "symbol": "BTC",
    "candles": [
      { "symbol": "BTC", "open": "45000.00", "high": "45100.00", "low": "44950.00", "close": "45050.00", "volume": "125.50", "vwa": "45025.00", "timeStamp": 1705500000 },
      { "symbol": "BTC", "open": "45050.00", "high": "45150.00", "low": "45000.00", "close": "45100.00", "volume": "98.20", "vwa": "45075.00", "timeStamp": 1705500060 }
    ]
  }
}
```

## Error Response

```json
{
  "type": "error",
  "message": "Invalid subscription request",
  "code": 400
}
```

Unknown symbols are rejected with a `400` that names them. See [Unknown symbols](/developer-tools/websocket-api/market-data#unknown-symbols).

## Notes

* **Timespans**: `MINUTE`, `HOUR`, or `DAY`
* **Multipliers**: `1`, `4`, `5`, or `15` (e.g., 1-minute, 4-minute, 5-minute, 15-minute candles)
* **Real-time subscription**: Omit `from` and `to` to receive streaming updates for new candles
* **Historical data**: Set both `from` and `to` timestamps to retrieve historical candles
* Updates are sent as new candles form or when the current candle updates
* Candle timestamps represent the start of the interval


# Rest API

## Base URL

**Testnet**: `https://api.staging.hello.trade/api`

**Mainnet**: `https://api.app.hello.trade/api`

***

## Request Format

All requests use standard HTTP methods with JSON payloads.

**Public Endpoints:**

```
GET /api/account_summary?wallet=0x...
```

**Authenticated Endpoints:**

```json
POST /api/margin/deposit
{
  "amount": "1000.00",
  "instrument": null,
  "marginMode": "Cross",
  "permitSignature": {
    "sig": "0x...",
    "payload": "0x05..."
  },
  "depositSignature": {
    "sig": "0x...",
    "payload": "0x07..."
  }
}
```

For signature construction, see [Signatures](/developer-tools/signatures).

***

## Authentication

Authenticated endpoints require EIP-712 signatures in the request body. See [Authentication & Account Model](/developer-tools/authentication-and-account-model) for details.

***

## Rate Limiting

Maximum request rate: **20 requests/second per wallet** (rolling 1-second window, orders included). Applies to wallet-authenticated requests; unauthenticated reads are limited per client IP — see [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits).

Same rate limit applies to both REST and WebSocket APIs. See [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits) for details.

***

## Error Responses

```json
{
  "error": "Error message"
}
```

| HTTP Status | Description                                                     |
| ----------- | --------------------------------------------------------------- |
| 400         | Bad Request (invalid parameters, signature verification failed) |
| 404         | Not Found (account/resource not found)                          |
| 500         | Internal Server Error                                           |

For detailed error descriptions, see [Error Codes](/developer-tools/error-codes).

***

## Available Endpoints

### Public Endpoints

| Endpoint                                 | Method | Description                                                                                                                                                        |
| ---------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `/api/account_summary`                   | GET    | Account balance and positions                                                                                                                                      |
| `/api/historic/orders`                   | GET    | Historical order data                                                                                                                                              |
| `/api/account/trades`                    | GET    | Trade history, including busted trades                                                                                                                             |
| `/api/user/access-status/:walletAddress` | GET    | `{ hasAppAccess: boolean }` — invite-gate predicate. See [authentication-and-account-model.md](/developer-tools/authentication-and-account-model#app-access-gate). |

See [Public Endpoints](/developer-tools/rest-api/public-endpoints) for details.

### Authenticated Endpoints

| Endpoint                 | Method | Description                              | Signature                      |
| ------------------------ | ------ | ---------------------------------------- | ------------------------------ |
| `/api/referral/apply`    | POST   | Apply an invite code to grant app access | Canonical-JSON `personal_sign` |
| `/api/margin/deposit`    | POST   | Deposit collateral                       | EIP-2612                       |
| `/api/margin/withdrawal` | POST   | Withdraw collateral                      | EIP-712                        |
| `/api/update_leverage`   | POST   | Update position leverage                 | EIP-712                        |

See [Authenticated Endpoints](/developer-tools/rest-api/authenticated-endpoints) for details.

***


# Public Endpoints

REST API endpoints accessible without authentication

Public endpoints do not require signatures or authentication.

**Base URL**: `https://api.app.hello.trade/api`

***

## GET /api/account\_summary

Get account balance, positions, and margin summary.

### Request

Query parameters:

| Parameter | Type    | Required | Description    |
| --------- | ------- | -------- | -------------- |
| `wallet`  | Address | Yes      | Wallet address |

**Example:**

```
GET /api/account_summary?wallet=0x742d35Cc6634C0532925a3b844Bc9e7595f12345
```

### Response

```json
{
  "assetPositions": [
    {
      "instrument": "NVDA",
      "marginMode": "Cross",
      "entryPx": "500.00",
      "leverage": 10,
      "liquidationPx": "450.00",
      "marginUsed": "100.00",
      "maxLeverage": 20,
      "positionValue": "1000.00",
      "returnOnEquity": "0.05",
      "szi": "2",
      "unrealizedPnl": "50.00"
    },
    {
      "instrument": "BTC",
      "marginMode": "Isolated",
      "entryPx": "65000.00",
      "leverage": 10,
      "liquidationPx": "59240.50",
      "marginUsed": "650.00",
      "maxLeverage": 40,
      "positionValue": "6500.00",
      "returnOnEquity": "0",
      "szi": "0.1",
      "unrealizedPnl": "0",
      "transferable": "0.00"
    }
  ],
  "crossMaintenanceMarginUsed": "25.00",
  "crossMarginSummary": {
    "accountValue": "10000.00",
    "totalMarginUsed": "100.00",
    "totalNtlPos": "1000.00",
    "totalRawUsd": "9950.00"
  },
  "maintenanceMarginUsed": "106.25",
  "marginSummary": {
    "accountValue": "10650.00",
    "totalMarginUsed": "750.00",
    "totalNtlPos": "7500.00",
    "totalRawUsd": "10600.00"
  },
  "time": 1705600000000,
  "withdrawable": "9900.00"
}
```

### Response Fields

**Top Level:**

| Field                        | Type   | Description                                |
| ---------------------------- | ------ | ------------------------------------------ |
| `assetPositions`             | Array  | Cross and isolated positions               |
| `crossMaintenanceMarginUsed` | string | Maintenance margin used (cross only)       |
| `crossMarginSummary`         | Object | Margin summary (cross only)                |
| `maintenanceMarginUsed`      | string | Maintenance margin used (cross + isolated) |
| `marginSummary`              | Object | Combined margin summary (cross + isolated) |
| `time`                       | i64    | Response timestamp (milliseconds)          |
| `withdrawable`               | string | Cross collateral available for withdrawal  |

**Asset Position:**

| Field            | Type   | Description                                                                      |
| ---------------- | ------ | -------------------------------------------------------------------------------- |
| `instrument`     | string | Instrument symbol                                                                |
| `marginMode`     | string | `Cross` or `Isolated`                                                            |
| `entryPx`        | string | Average entry price                                                              |
| `leverage`       | u32    | Position leverage (multiplier, e.g. 10 = 10×)                                    |
| `liquidationPx`  | string | Estimated liquidation price (omitted if not computable)                          |
| `marginUsed`     | string | Margin allocated to the position (for isolated: the position's dedicated margin) |
| `maxLeverage`    | u32    | Maximum allowed leverage (multiplier, e.g. 20 = 20×)                             |
| `positionValue`  | string | Notional position value                                                          |
| `returnOnEquity` | string | ROE percentage                                                                   |
| `szi`            | string | Position size (signed, positive = long)                                          |
| `unrealizedPnl`  | string | Unrealized PnL                                                                   |
| `transferable`   | string | Isolated collateral free to transfer to cross (isolated positions only)          |

**Margin Summary:**

| Field             | Type   | Description                                         |
| ----------------- | ------ | --------------------------------------------------- |
| `accountValue`    | string | Total account value                                 |
| `totalMarginUsed` | string | Total margin used                                   |
| `totalNtlPos`     | string | Total notional position value                       |
| `totalRawUsd`     | string | Total collateral balance (excluding unrealized PnL) |

***

## GET /api/open\_orders

Get the wallet's currently open (resting) orders. Like the other read endpoints, this is public — any wallet address may be queried (no ownership proof required).

### Request

Query parameters:

| Parameter     | Type    | Required | Description                                                                           |
| ------------- | ------- | -------- | ------------------------------------------------------------------------------------- |
| `wallet`      | Address | Yes      | Wallet address                                                                        |
| `mode`        | string  | No       | Margin scope: `combined` (default), `cross`, or `isolated`                            |
| `instruments` | string  | No       | Comma-separated symbols narrowing `mode=isolated` results (only valid with that mode) |

**Examples:**

```
GET /api/open_orders?wallet=0x742d35Cc6634C0532925a3b844Bc9e7595f12345
GET /api/open_orders?wallet=0x742d35Cc6634C0532925a3b844Bc9e7595f12345&mode=cross
GET /api/open_orders?wallet=0x742d35Cc6634C0532925a3b844Bc9e7595f12345&mode=isolated&instruments=BTC
```

### Response

```json
{
  "orders": [
    {
      "orderId": 123456789,
      "marginMode": "Cross",
      "traderOrderId": 1705600000001,
      "instrument": "NVDA",
      "orderType": "Limit",
      "side": "Buy",
      "quantity": "100",
      "price": "500.00",
      "timeInForce": "GTC",
      "status": "Active",
      "filledQuantity": "40",
      "filledPrice": "500.00",
      "remainingOpenQuantity": "60",
      "orderTimestamp": 1705600000000000000,
      "lastEventTimestamp": 1705600001000000000,
      "lastEventId": 987654321
    },
    {
      "orderId": 123456790,
      "marginMode": "Isolated",
      "traderOrderId": 1705600000002,
      "instrument": "BTC",
      "orderType": "Limit",
      "side": "Buy",
      "quantity": "0.05",
      "price": "64000.00",
      "timeInForce": "GTC",
      "status": "Active",
      "filledQuantity": "0",
      "filledPrice": "0",
      "remainingOpenQuantity": "0.05",
      "orderTimestamp": 1705600002000000000,
      "lastEventTimestamp": 1705600002000000000,
      "lastEventId": 987654322
    },
    {
      "orderId": 123456791,
      "marginMode": "Cross",
      "traderOrderId": 1705600000003,
      "instrument": "BTC",
      "orderType": "StopLoss",
      "side": "Sell",
      "quantity": "0.05",
      "stopPrice": "58000.00",
      "closePosition": true,
      "timeInForce": "GTC",
      "status": "Active",
      "filledQuantity": "0",
      "filledPrice": "0",
      "remainingOpenQuantity": "0.05",
      "orderTimestamp": 1705600003000000000,
      "lastEventTimestamp": 1705600003000000000,
      "lastEventId": 987654323
    }
  ]
}
```

### Response Fields

**Top Level:**

| Field    | Type  | Description         |
| -------- | ----- | ------------------- |
| `orders` | Array | List of open orders |

**Order:**

| Field                   | Type      | Description                                                                                                                                                                                 |
| ----------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `orderId`               | u64       | Exchange order ID                                                                                                                                                                           |
| `marginMode`            | string    | `Cross` or `Isolated`                                                                                                                                                                       |
| `traderOrderId`         | u64       | Client-supplied order ID (omitted for system-generated orders)                                                                                                                              |
| `instrument`            | string    | Instrument symbol                                                                                                                                                                           |
| `orderType`             | string    | `Limit`, `StopLimit`, `StopLoss`, `TakeProfit`; responses may also contain internal execution types (`LIT`) and retired types (`Market`, `Stop`, `MIT`, `MarketToLimit`) on historic orders |
| `side`                  | string    | `Buy` or `Sell`                                                                                                                                                                             |
| `quantity`              | string    | Order quantity                                                                                                                                                                              |
| `price`                 | string    | Limit price (omitted if not applicable)                                                                                                                                                     |
| `stopPrice`             | string    | Stop trigger price (omitted if not applicable)                                                                                                                                              |
| `closePosition`         | boolean   | SL/TP only: `true` = managed (auto-syncs to position size), `false` = unmanaged (fixed qty). Omitted for non-SL/TP orders                                                                   |
| `instructions`          | string\[] | `["PostOnly"]` for a post-only order (omitted otherwise)                                                                                                                                    |
| `timeInForce`           | string    | `GTC`, `GTD`, `IOC`, `FOK`, `DAY`                                                                                                                                                           |
| `expiryDate`            | i64       | Expiry timestamp for `GTD` (milliseconds; omitted otherwise)                                                                                                                                |
| `status`                | string    | `Active`, `Suspended`, `Executed`, `Cancelled`, `Pending`, `Ended`                                                                                                                          |
| `filledQuantity`        | string    | Cumulative filled quantity                                                                                                                                                                  |
| `filledPrice`           | string    | Weighted average filled price                                                                                                                                                               |
| `remainingOpenQuantity` | string    | Quantity still open                                                                                                                                                                         |
| `orderTimestamp`        | i64       | Order creation timestamp (Unix nanoseconds)                                                                                                                                                 |
| `lastEventTimestamp`    | i64       | Timestamp of the last event on this order (Unix nanoseconds)                                                                                                                                |
| `lastEventId`           | u64       | ID of the last event on this order                                                                                                                                                          |

***

## GET /api/historic/orders

Query historical orders.

### Request

Query parameters:

| Parameter       | Type     | Required | Description                                                                                            |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `wallet`        | Address  | Yes      | Wallet address                                                                                         |
| `dateFrom`      | DateTime | No       | Start date (ISO 8601)                                                                                  |
| `dateTo`        | DateTime | No       | End date (ISO 8601)                                                                                    |
| `instruments`   | string   | No       | Comma-separated instruments (e.g., `NVDA,AAPL`)                                                        |
| `traderOrderId` | u64      | No       | Filter by client order ID                                                                              |
| `orderId`       | u64      | No       | Filter by exchange order ID                                                                            |
| `orderBy`       | string   | No       | Sort as `<field>:<direction>`. Field: `createdAt` or `lastEventTimestamp`. Direction: `Asc` or `Desc`. |
| `limit`         | u32      | No       | Max results (default: 50)                                                                              |
| `offset`        | u64      | No       | Pagination offset                                                                                      |

**Example:**

```
GET /api/historic/orders?wallet=0x742d35Cc6634C0532925a3b844Bc9e7595f12345&instruments=NVDA&limit=50
```

**Example with sorting:**

```
GET /api/historic/orders?wallet=0x742d35Cc6634C0532925a3b844Bc9e7595f12345&orderBy=createdAt:Desc&limit=50
```

### Response

```json
{
  "orders": [
    {
      "orderId": 123456789,
      "traderOrderId": 1705600000001,
      "instrumentId": 8,
      "instrument": "NVDA",
      "status": "Executed",
      "orderType": "Limit",
      "side": "Buy",
      "price": "500.00",
      "quantity": "100",
      "timeInForce": "GTC",
      "expiryDate": null,
      "createdAt": 1705600000,
      "filledPrice": "500.00",
      "filledQuantity": "100",
      "remainingOpenQuantity": "0",
      "lastEventTimestamp": 1705600001
    }
  ],
  "count": 1
}
```

### Response Fields

**Top Level:**

| Field    | Type  | Description                                      |
| -------- | ----- | ------------------------------------------------ |
| `orders` | Array | Order records                                    |
| `count`  | u64   | Total matching records (omitted if not computed) |

**Order:**

| Field                   | Type           | Description                                                                                                                                              |
| ----------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `orderId`               | u64            | Exchange order ID                                                                                                                                        |
| `traderOrderId`         | u64            | Client-supplied order ID (omitted for system-generated orders)                                                                                           |
| `instrumentId`          | u32            | Numeric instrument identifier                                                                                                                            |
| `instrument`            | string         | Instrument symbol                                                                                                                                        |
| `status`                | string         | `Active`, `Suspended`, `Executed`, `Cancelled`, `Pending`, `Ended`                                                                                       |
| `orderType`             | string         | `Limit`, `StopLimit`, `StopLoss`, `TakeProfit`; responses may also contain internal (`LIT`) and retired (`Market`, `Stop`, `MIT`, `MarketToLimit`) types |
| `side`                  | string         | `Buy` or `Sell`                                                                                                                                          |
| `price`                 | string \| null | Limit price (`null` if not applicable)                                                                                                                   |
| `quantity`              | string         | Order quantity                                                                                                                                           |
| `timeInForce`           | string         | `GTC`, `GTD`, `IOC`, `FOK`, `DAY`                                                                                                                        |
| `expiryDate`            | i64 \| null    | Expiry timestamp for `GTD`, in **Unix seconds** (`null` otherwise)                                                                                       |
| `createdAt`             | i64            | Order creation timestamp, in **Unix seconds**                                                                                                            |
| `filledPrice`           | string         | Weighted average filled price                                                                                                                            |
| `filledQuantity`        | string         | Cumulative filled quantity                                                                                                                               |
| `remainingOpenQuantity` | string         | Quantity still open                                                                                                                                      |
| `lastEventTimestamp`    | i64            | Timestamp of the last event on this order, in **Unix seconds**                                                                                           |
| `stopPrice`             | string         | Stop trigger price (omitted if not applicable)                                                                                                           |
| `minQuantity`           | string         | Minimum order quantity (omitted if not set)                                                                                                              |
| `cancelReason`          | string         | Reason for cancellation (omitted unless cancelled)                                                                                                       |
| `instructions`          | string\[]      | `["PostOnly"]` for a post-only order (omitted otherwise)                                                                                                 |

***

## GET /api/account/trades

Query a wallet's trade history, newest first. Fills come from settlement on chain, with exchange identifiers attached; busted trades are included as `CANCELLED` rows.

### Request

Query parameters:

| Parameter       | Type     | Required | Description                                                                     |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------- |
| `wallet`        | Address  | Yes      | Wallet address                                                                  |
| `instrumentIds` | string   | No       | Comma-separated instrument IDs, e.g. `24,25`                                    |
| `kinds`         | string   | No       | Comma-separated `STANDARD`, `LIQUIDATION`, `ADL`, `MARKET_CLOSURE`, `CANCELLED` |
| `dateFrom`      | DateTime | No       | Start time, inclusive (ISO 8601)                                                |
| `dateTo`        | DateTime | No       | End time, inclusive (ISO 8601)                                                  |
| `limit`         | u32      | No       | Max results (default: 50, max: 50)                                              |
| `offset`        | u64      | No       | Pagination offset (max: 50)                                                     |

List values have no spaces.

**Paging:** past the first 100 rows, request the next page with `dateTo` set to the oldest `timestamp` received. `dateTo` is inclusive, so rows from that second repeat; deduplicate by `id`.

**Example:**

```
GET /api/account/trades?wallet=0x742d35Cc6634C0532925a3b844Bc9e7595f12345&instrumentIds=8&limit=50
```

### Response

```json
{
  "trades": [
    {
      "marketType": "PERPS",
      "id": "10143_61685035_20",
      "kind": "STANDARD",
      "role": "TAKER",
      "instrumentId": 8,
      "instrument": "NVDA",
      "side": "Buy",
      "price": "500.00",
      "quantity": "0.5",
      "timestamp": 1705600001,
      "marginMode": "Cross",
      "notional": "250.00",
      "fee": "0.05",
      "realizedPnl": "0",
      "orderId": 123456789,
      "traderOrderId": 1705600000001,
      "txHash": "0x4274a9cd…",
      "blockNumber": 61685035,
      "logIndex": 20,
      "tradeId": 987654321,
      "tradeDate": "2024-01-18"
    }
  ],
  "count": 1
}
```

### Response Fields

| Field           | Type   | Description                                                                   |
| --------------- | ------ | ----------------------------------------------------------------------------- |
| `marketType`    | string | `PERPS`                                                                       |
| `id`            | string | Unique row ID; deduplicate on this                                            |
| `kind`          | string | `STANDARD`, `LIQUIDATION`, `ADL`, `MARKET_CLOSURE`, or `CANCELLED` (bust)     |
| `role`          | string | `MAKER`, `TAKER`, `LIQUIDATOR`, `ADL_CLOSER`, `LIQUIDATEE` (omitted on busts) |
| `instrumentId`  | u64    | Instrument ID                                                                 |
| `instrument`    | string | Instrument symbol                                                             |
| `side`          | string | `Buy` or `Sell`                                                               |
| `price`         | string | USD per unit                                                                  |
| `quantity`      | string | Base units                                                                    |
| `timestamp`     | i64    | Block time, or bust time for busts, in **Unix seconds**                       |
| `marginMode`    | string | `Cross` or `Isolated` (fills only)                                            |
| `notional`      | string | USD (omitted on busts)                                                        |
| `fee`           | string | USD (fills only)                                                              |
| `realizedPnl`   | string | USD, negative on a loss                                                       |
| `counterparty`  | string | ADL counterparty (ADL only)                                                   |
| `orderId`       | u64    | Exchange order ID (omitted on ADL, manual liquidation and unaligned fills)    |
| `traderOrderId` | u64    | Client-supplied order ID (same availability as `orderId`)                     |
| `txHash`        | string | Settlement transaction (omitted on busts)                                     |
| `blockNumber`   | u64    | Settlement block (omitted on busts)                                           |
| `logIndex`      | u32    | Log index in the block (fills only)                                           |
| `tradeId`       | u64    | Exchange trade ID (shared by a bust and the trade it cancelled)               |
| `tradeDate`     | Date   | Exchange business day (same availability as `tradeId`)                        |

***

## GET /api/historic/funding

Query a wallet's historic funding payments. Cross-margin and isolated-margin funding are merged into a single list, sorted newest-first, with each entry tagged by `marginMode`.

### Request

Query parameters:

| Parameter  | Type     | Required | Description                        |
| ---------- | -------- | -------- | ---------------------------------- |
| `wallet`   | Address  | Yes      | Wallet address                     |
| `market`   | u64      | No       | Filter by instrument ID            |
| `dateFrom` | DateTime | No       | Start time, inclusive (ISO 8601)   |
| `dateTo`   | DateTime | No       | End time, inclusive (ISO 8601)     |
| `limit`    | u32      | No       | Max results (default: 50, max: 50) |
| `offset`   | u64      | No       | Pagination offset                  |

For large histories, prefer narrowing with `dateFrom`/`dateTo` over large `offset` values.

**Example:**

```
GET /api/historic/funding?wallet=0x742d35Cc6634C0532925a3b844Bc9e7595f12345&market=8&limit=50
```

### Response

```json
{
  "funding": [
    {
      "market": 8,
      "timestamp": 1705600001,
      "delta": "1.50",
      "marginMode": "Cross"
    },
    {
      "market": 8,
      "timestamp": 1705513601,
      "delta": "-0.25",
      "marginMode": "Isolated"
    }
  ],
  "count": 2
}
```

| Field     | Type  | Description                   |
| --------- | ----- | ----------------------------- |
| `funding` | Array | Funding transactions          |
| `count`   | u64   | Records returned in this page |

**Funding Transaction:**

| Field        | Type   | Description                                                  |
| ------------ | ------ | ------------------------------------------------------------ |
| `market`     | u64    | Instrument ID                                                |
| `timestamp`  | i64    | Funding event time (Unix seconds)                            |
| `delta`      | string | Funding amount in USD (positive = received, negative = paid) |
| `marginMode` | string | `Cross` or `Isolated`                                        |

***

## GET /api/user/access-status/:walletAddress

Returns whether a wallet has been granted app access. Mirrors the predicate the authentication gate uses, so clients can branch on it before prompting the user to sign anything.

### Request

Path parameter:

| Parameter       | Type    | Required | Description                                           |
| --------------- | ------- | -------- | ----------------------------------------------------- |
| `walletAddress` | Address | Yes      | Wallet to query (case-insensitive; server lowercases) |

**Example:**

```
GET /api/user/access-status/0x742d35Cc6634C0532925a3b844Bc9e7595f12345
```

### Response

```json
{
  "hasAppAccess": true
}
```

| Field          | Type    | Description                                                                                                 |
| -------------- | ------- | ----------------------------------------------------------------------------------------------------------- |
| `hasAppAccess` | boolean | `true` iff the wallet has applied any referral code (affiliate or system) or was granted access by an admin |

If `false`, prompt the user for an invite code and submit it via `POST /api/referral/apply` (see [Authenticated Endpoints](/developer-tools/rest-api/authenticated-endpoints#post-apireferralapply)).

***

## Common Errors

| HTTP Status | Description                      |
| ----------- | -------------------------------- |
| 400         | Bad Request (invalid parameters) |
| 404         | Not Found (account not found)    |
| 500         | Internal Server Error            |

Error response format:

```json
{
  "error": "Error message"
}
```


# Authenticated Endpoints

REST API endpoints requiring signature authentication

All authenticated endpoints require EIP-712, EIP-2612, or canonical-JSON `personal_sign` signatures depending on the endpoint.

**Base URL**: `https://api.app.hello.trade/api`

***

## POST /api/referral/apply

Apply an invite code to grant a wallet app access. Without app access, the wallet cannot authenticate against any other authed write endpoint — see [Authentication & Account Model — App Access Gate](/developer-tools/authentication-and-account-model#app-access-gate).

### Headers

| Header         | Description                                                                                            |
| -------------- | ------------------------------------------------------------------------------------------------------ |
| `content-type` | `application/json`                                                                                     |
| `x-signature`  | viem `personal_sign` over the canonical-JSON payload below                                             |
| `x-timestamp`  | The same `timestamp` value used in the signed payload (`Date.now()` ms)                                |
| `x-nonce`      | The same `nonce` value used in the signed payload — must be unique per request, `[A-Za-z0-9_-]{8,128}` |

### Signed Payload (canonical JSON)

```json
{
  "body":{
    "code":"HELLO12",
    "walletAddress":"0xabc..."
    },
  "method":"POST",
  "nonce":"<unique>",
  "path":"/referral/apply",
  "timestamp":1715500000000,
  "walletAddress":"0xabc..."
}
```

| Field           | Description                                                                                                                                                                               |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `body`          | The full request body, with keys sorted alphabetically. `null` for bodyless requests. Bodies must be flat JSON objects — nested objects are not deterministically ordered by this scheme. |
| `method`        | Literal `"POST"`                                                                                                                                                                          |
| `nonce`         | Matches the `x-nonce` header                                                                                                                                                              |
| `path`          | Literal `"/referral/apply"`                                                                                                                                                               |
| `timestamp`     | Matches `x-timestamp`; ms since epoch; server enforces a ±5 minute window                                                                                                                 |
| `walletAddress` | Lowercased wallet that is applying the code                                                                                                                                               |

Top-level keys must appear in alphabetical order with no whitespace; keys inside `body` must also be sorted alphabetically. The server canonicalizes identically before recovering the signer, so any tampering with body fields invalidates the signature.

### Request Body

```json
{
  "walletAddress": "0xabc...",
  "code": "FRIEND7"
}
```

| Field           | Type   | Description                                                                                            |
| --------------- | ------ | ------------------------------------------------------------------------------------------------------ |
| `walletAddress` | string | Lowercased wallet address (must match the `walletAddress` in the signed payload)                       |
| `code`          | string | Invite code, `[A-Z0-9-]{1,7}`. Must match the `code` field inlined under `body` in the signed payload. |

### Response — Success

HTTP status `201 Created`. `kind` is `"attributed"` for affiliate codes and `"system"` for admin-minted codes; the `"system"` branch omits `referrerAddress`.

```json
{
  "kind": "attributed",
  "refereeAddress": "0xabc...",
  "referrerAddress": "0xdef...",
  "code": "FRIEND7"
}
```

```json
{
  "kind": "system",
  "refereeAddress": "0xabc...",
  "code": "WAITLIST"
}
```

### Response — Errors

| HTTP  | Body shape                                                       | Notes                                                                                            |
| ----- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `400` | `{"error":"walletAddress and code are required"}`                | Malformed body                                                                                   |
| `401` | `{"error":"Unauthorized: <reason>"}`                             | Signature problem (missing headers, expired timestamp, replay, or signer mismatch)               |
| `422` | `{"error":"ALREADY_REFERRED"}`                                   | Wallet has already applied a referral code. Attribution is first-touch and immutable.            |
| `422` | `{"error":"CODE_NOT_FOUND"}`                                     | No referral code matches the submitted string                                                    |
| `422` | `{"error":"CODE_REVOKED"}`                                       | Code is valid but has been revoked. The affiliate likely rotated it; ask for a current code.     |
| `422` | `{"error":"CODE_EXHAUSTED"}`                                     | Code has reached its `maxUses` limit                                                             |
| `422` | `{"error":"SELF_REFERRAL"}`                                      | The signer owns the code they're trying to apply                                                 |
| `503` | `{"error":"Referral service unavailable, please retry"}`         | Transient transport failure between api-gateway and the referral service. Safe to retry.         |
| `504` | `{"error":"Referral service timed out, please try again later"}` | Upstream is reachable but slow. Back off before retrying — tight retry loops can worsen a wedge. |

### TypeScript example

```typescript
import { createWalletClient, custom } from "viem";

const wallet = createWalletClient({
  account: walletAddress as `0x${string}`,
  transport: custom(window.ethereum),
});

const timestamp = Date.now();
const nonce = crypto.randomUUID().replace(/-/g, "");

// The request body, with keys sorted alphabetically. The same object is
// inlined into the signed payload AND sent on the wire — any divergence
// invalidates the signature.
const body = {
  code: inviteCode,
  walletAddress: walletAddress.toLowerCase(),
};

const message = JSON.stringify({
  body,
  method: "POST",
  nonce,
  path: "/referral/apply",
  timestamp,
  walletAddress: walletAddress.toLowerCase(),
});

const signature = await wallet.signMessage({ message });

const res = await fetch("https://api.app.hello.trade/api/referral/apply", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-signature": signature,
    "x-timestamp": String(timestamp),
    "x-nonce": nonce,
  },
  body: JSON.stringify(body),
});
```

After a successful apply, re-check `GET /api/user/access-status/:walletAddress` if you want a positive read before issuing further signed requests.

***

## POST /api/margin/deposit

Deposit collateral using ERC-2612 permit and vault authorization.

### Request

```json
{
  "amount": "1000.00",
  "instrument": null,
  "marginMode": "Cross",
  "permitSignature": {
    "sig": "0x...",
    "payload": "0x05..."
  },
  "depositSignature": {
    "sig": "0x...",
    "payload": "0x07..."
  }
}
```

| Field              | Type           | Description                                                    |
| ------------------ | -------------- | -------------------------------------------------------------- |
| `amount`           | string         | Deposit amount (human-readable, e.g., "1000.50")               |
| `instrument`       | string \| null | Instrument symbol for isolated margin, `null` for cross margin |
| `marginMode`       | string         | `"Cross"` or `"Isolated"`                                      |
| `permitSignature`  | Object         | ERC-2612 permit signature (type `0x05`)                        |
| `depositSignature` | Object         | Vault deposit authorization signature (type `0x07`)            |

**Important Notes:**

* Requires **two signatures**: Permit (token approval) and Deposit (vault authorization)
* `permitSignature.payload` contains encoded Permit struct (token nonce must be fetched from token contract)
* `depositSignature.payload` contains encoded Deposit struct (includes `market`, `flags`, `deadline`)
* **Cross margin**: `instrument: null`, `marginMode: "Cross"`, and in the Deposit signature `market: 0`, `flags` bit 0 = `0`
* **Isolated margin**: `instrument` set to the market's symbol, `marginMode: "Isolated"`, and in the Deposit signature `market` = the instrument ID, `flags` bit 0 = `1`. The collateral lands in that market's isolated margin.

For signature construction, see [Signatures - Deposits](/developer-tools/signatures#deposits).

### Response

```json
{
  "command_id": 100001,
  "status": "Submitted"
}
```

| Field        | Type   | Description               |
| ------------ | ------ | ------------------------- |
| `command_id` | u64    | Command ID for tracking   |
| `status`     | string | `Submitted` or `Rejected` |

Track deposit completion via `DepositMargin` execution report on WebSocket.

***

## POST /api/margin/withdrawal

Withdraw collateral.

### Request

```json
{
  "amount": "500.00",
  "instrument": null,
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x03..."
  }
}
```

| Field        | Type           | Description                                                    |
| ------------ | -------------- | -------------------------------------------------------------- |
| `amount`     | string         | Withdrawal amount (human-readable, e.g., "500.25")             |
| `instrument` | string \| null | Instrument symbol for isolated margin, `null` for cross margin |
| `marginMode` | string         | `"Cross"` or `"Isolated"`                                      |
| `signature`  | Object         | EIP-712 withdrawal signature (type `0x03`)                     |

**Important Notes:**

* `signature.payload` contains encoded Withdrawal struct with `owner`, `amount`, `market`, `flags`, `nonce`, `deadline`
* **Cross margin**: `instrument: null`, `marginMode: "Cross"`, and in the signature `market: 0`, `flags` bit 0 = `0`
* **Isolated margin**: `instrument` set to the market's symbol, `marginMode: "Isolated"`, and in the signature `market` = the instrument ID, `flags` bit 0 = `1`. Withdraws from that market's isolated margin; alternatively transfer to cross first and withdraw from there.

For signature construction, see [Signatures - Withdrawals](/developer-tools/signatures#withdrawals).

### Response

```json
{
  "command_id": 100002,
  "status": "Submitted"
}
```

Track withdrawal completion via `WithdrawMargin` execution report on WebSocket.

***

## POST /api/margin/transfer

Move collateral between cross margin and a market's isolated margin. No tokens move on- or off-chain — the transfer reallocates collateral within the account.

### Request

```json
{
  "amount": "250.00",
  "instrument": "BTC",
  "direction": "crossToIsolated",
  "signature": {
    "sig": "0x...",
    "payload": "0x08..."
  }
}
```

| Field        | Type   | Description                                      |
| ------------ | ------ | ------------------------------------------------ |
| `amount`     | string | Transfer amount (human-readable, e.g., "250.00") |
| `instrument` | string | Instrument symbol of the isolated market         |
| `direction`  | string | `"crossToIsolated"` or `"isolatedToCross"`       |
| `signature`  | Object | EIP-712 margin transfer signature (type `0x08`)  |

**Important Notes:**

* `signature.payload` contains the encoded MarginTransfer struct with `owner`, `amount`, `market`, `flags`, `nonce`, `deadline`
* `market` in the signature is the instrument ID of the isolated market (both directions)
* `flags` bit 0 carries the **source** account's margin mode — `0` for `crossToIsolated`, `1` for `isolatedToCross` — and must match `direction`
* `crossToIsolated` draws from cross **withdrawable** (free collateral); `isolatedToCross` draws from the market's free isolated margin

For signature construction, see [Signatures - Margin Transfers](/developer-tools/signatures#margin-transfers).

### Response

```json
{
  "command_id": 100004,
  "status": "Submitted"
}
```

Track transfer settlement via `TransferMargin` execution report on WebSocket. The same operation is also available over the trading WebSocket as a `transferMargin` message (acked with `marginTransferred`).

***

## POST /api/update\_leverage

Update leverage for an instrument.

### Request

```json
{
  "leverage": 1000,
  "instrument": "NVDA",
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x04..."
  }
}
```

| Field        | Type   | Description                                     |
| ------------ | ------ | ----------------------------------------------- |
| `leverage`   | u32    | Leverage × 100 (e.g., 1000 = 10x)               |
| `instrument` | string | Instrument symbol                               |
| `marginMode` | string | `"Cross"` or `"Isolated"`                       |
| `signature`  | Object | EIP-712 leverage update signature (type `0x04`) |

**Important Notes:**

* `signature.payload` contains encoded LeverageUpdate struct with `account`, `market`, `leverage`, `flags`, `nonce`, `deadline`
* `flags` bit 0 must match `marginMode` (`0` = Cross, `1` = Isolated)
* Leverage is tracked per margin mode: the same instrument can hold different leverage cross vs isolated

For signature construction, see [Signatures - Leverage Updates](/developer-tools/signatures#leverage-updates).

### Response

```json
{
  "command_id": 100003,
  "status": "Submitted"
}
```

Track leverage update completion via `UpdateLeverage` execution report on WebSocket.

***

## Common Errors

| HTTP Status | Description                                                     |
| ----------- | --------------------------------------------------------------- |
| 400         | Bad Request (invalid parameters, signature verification failed) |
| 404         | Not Found (account not found)                                   |
| 500         | Internal Server Error                                           |

Error response format:

```json
{
  "error": "Error message"
}
```

For detailed error descriptions, see [Error Codes](/developer-tools/error-codes).


# Error Codes

Complete error code reference for WebSocket and REST APIs

## Error Response Format

### WebSocket

```json
{
  "type": "error",
  "code": 4001,
  "message": "Signature verification failed"
}
```

### REST API

```json
{
  "error": "Signature verification failed"
}
```

REST API errors return standard HTTP status codes (400, 403, 404, 500, 503) with error message in response body.

***

## Error Codes

### Authentication Errors (4000-4019)

| Code | Error              | Description                                                                                         |
| ---- | ------------------ | --------------------------------------------------------------------------------------------------- |
| 4000 | INVALID\_REQUEST   | Malformed request, missing required fields, or a rejected reduce-only / SL-TP placement (see below) |
| 4001 | INVALID\_SIGNATURE | Signature verification failed                                                                       |
| 4002 | INVALID\_NONCE     | Nonce validation failed (expired, replay, or out of window)                                         |
| 4003 | NOT\_AUTHENTICATED | Connection not authenticated                                                                        |
| 4004 | WALLET\_MISMATCH   | Signed wallet doesn't match authenticated wallet                                                    |
| 4015 | USER\_NOT\_INVITED | Wallet has not been granted app access. Apply a referral code via `POST /api/referral/apply`.       |

### Validation Errors (4020-4049)

| Code | Error                            | Description                                                                                         |
| ---- | -------------------------------- | --------------------------------------------------------------------------------------------------- |
| 4029 | RATE\_LIMIT\_EXCEEDED            | Request rate limit exceeded (20 req/s per authenticated wallet)                                     |
| 4030 | INVALID\_ORDER\_TYPE             | Invalid order type                                                                                  |
| 4031 | INVALID\_TIME\_IN\_FORCE         | Invalid time in force value                                                                         |
| 4032 | INVALID\_ORDER\_PRICE            | Invalid or missing price for order type                                                             |
| 4033 | ALREADY\_SUBSCRIBED              | Connection already subscribed to trading stream                                                     |
| 4034 | INVALID\_ORDER\_QUANTITY         | Invalid order quantity                                                                              |
| 4035 | SUSTAINED\_RATE\_LIMIT\_EXCEEDED | Signed request budget exhausted (see [Nonce & Rate Limits](/developer-tools/nonce-and-rate-limits)) |
| 4040 | ACCOUNT\_NOT\_FOUND              | Trading account not found                                                                           |
| 4041 | ORDER\_NOT\_FOUND                | Cancel/replace target already filled, cancelled, or expired — benign race, safe to ignore           |

#### Reduce-Only and SL/TP Placement (4000)

These are returned as `4000` with a descriptive message:

| Message contains                                     | Meaning                                                                                          | Required action                                                                                        |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| `requires an open position`                          | A reduce-only order, or an unmanaged SL/TP, was placed with no position to reduce                | Open the position first                                                                                |
| `requires an open position or a resting entry order` | A managed SL/TP had nothing to attach to                                                         | Place the entry order first, or open the position                                                      |
| `exceeds position size`                              | The order alone would close more than you hold                                                   | Reduce the quantity                                                                                    |
| `in one sweep would close`                           | Your reduce-only orders reachable by the same price move would together close more than you hold | Cancel or resize one of them — see [Reduce-Only Orders](/about/trading/order-types#reduce-only-orders) |
| `must reduce position (opposite side required)`      | The order is on the same side as the position                                                    | Use the opposite side                                                                                  |

### Business Logic Errors (4100-4199)

| Code | Error                   | Description                                                                                                                             |
| ---- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| 4100 | INSUFFICIENT\_MARGIN    | Insufficient margin for operation                                                                                                       |
| 4101 | ORDER\_TOO\_SMALL       | Order notional size below minimum ($5)                                                                                                  |
| 4102 | MAX\_LEVERAGE\_EXCEEDED | Leverage exceeds instrument maximum                                                                                                     |
| 4103 | OI\_CAP\_EXCEEDED       | Order would exceed open interest cap                                                                                                    |
| 4107 | UNSAFE\_LIMIT\_PRICE    | A reduce-only or stop-loss/take-profit limit would fill below maintenance margin; `safeLimitPrice` carries the worst limit that settles |

### Server Errors (5000-5099)

| Code | Error                    | Description                          |
| ---- | ------------------------ | ------------------------------------ |
| 5000 | INTERNAL\_ERROR          | Internal server error                |
| 5001 | EXCHANGE\_ERROR          | Exchange matching engine error       |
| 5002 | LEDGER\_UNAVAILABLE      | Risk engine unavailable              |
| 5003 | EXCHANGE\_ACCOUNT\_ERROR | Account creation or retrieval failed |

***

***

## HTTP Status Codes

| Status | Description           | Typical Cause                                                                                                    |
| ------ | --------------------- | ---------------------------------------------------------------------------------------------------------------- |
| 200    | Success               | Request completed successfully                                                                                   |
| 400    | Bad Request           | Invalid parameters or signature validation failed                                                                |
| 403    | Forbidden             | Wallet not invited. Body starts with `USER_NOT_INVITED:` — apply a referral code via `POST /api/referral/apply`. |
| 404    | Not Found             | Resource does not exist                                                                                          |
| 500    | Internal Server Error | Server-side error                                                                                                |
| 503    | Service Unavailable   | Transient — auth-service unreachable, safe to retry                                                              |

***

## Error Handling

### Retryable Errors

| Code | Retry Strategy                                 |
| ---- | ---------------------------------------------- |
| 5000 | Retry with exponential backoff                 |
| 5002 | Retry after delay (service may be unavailable) |
| 503  | Retry after delay                              |

### Non-Retryable Errors

| Code | Required Action                         |
| ---- | --------------------------------------- |
| 4001 | Fix signature generation                |
| 4002 | Generate fresh nonce                    |
| 4041 | None — order already gone; do not retry |
| 4100 | Deposit margin or reduce order size     |
| 4101 | Increase order quantity                 |
| 4102 | Reduce leverage or position size        |
| 4103 | Reduce order size                       |

### Rate Limit Handling

When receiving error code 4029:

* Stop sending requests immediately
* Wait for rate limit window to reset (1 second)
* Implement exponential backoff for subsequent requests

When receiving error code 4035 (sustained budget exhausted):

* Reduce your sustained request rate — the budget frees as older signed requests age past the 5-minute nonce window
* Cancels are granted 2x budget headroom and keep working, so always prefer cancelling resting orders over waiting


# SDK

> **⚠️ Under Development**
>
> Our official SDK is currently under development. The SDK will provide:
>
> * Simplified signature creation for all operations
> * Automatic payload encoding with type discriminators
> * API clients for WebSocket and REST endpoints
> * Helper functions for order flags, margin modes, and precision handling
> * Comprehensive examples and documentation
>
> In the meantime, please refer to our [Signatures](/developer-tools/signatures) documentation for manual integration details.

## Planned Features

* **Signature Management**: Automatic EIP-712 and EIP-191 signature creation
* **Payload Encoding**: Built-in support for V2 signature format with type discriminators
* **API Clients**: WebSocket and REST clients with automatic reconnection and error handling
* **Utilities**: Helpers for conversions, precision handling, and validation

## Current Integration

For now, developers can integrate using the raw API endpoints. See:

* [Signatures](/developer-tools/signatures) - Complete signature specifications
* [WebSocket API](/developer-tools/websocket-api) - Real-time trading operations
* [REST API](/developer-tools/rest-api) - HTTP endpoints for margin and account management


