<!--
  Sourced from Lumerin-protocol/derivatives-marketplace@main:docs/gitbook/03.Trading-Guide.md
  Regenerated on every hashpower.io build — do not edit by hand.
  Canonical source: https://github.com/Lumerin-protocol/derivatives-marketplace/blob/main/docs/gitbook/03.Trading-Guide.md
-->
# Trading Guide

This guide covers how to trade on Hash Power Perps: the order book model,
placing and matching orders, and managing their lifecycle.

---

## Prerequisites

1. **Collateral deposited** in the shared vault — see
   [Collateral & Accounts](./02.Collateral-and-Accounts.md).
2. **Wallet** connected to the correct network.
3. Enough **free margin** to cover the Initial Margin of the order you place.

---

## The order book model

Hash Power Perps is a fully on-chain **central limit order book (CLOB)**:

- Orders rest on a **fixed price grid**. Every price must be a multiple of
  `minimumPriceIncrement` (the tick size); off-grid prices revert
  `InvalidPrice`.
- Each side keeps **sorted price levels** — bids high-to-low, asks
  low-to-high — and within a level orders queue **FIFO** (price-time
  priority).
- Quantities use `QUANTITY_DECIMALS = 6` and are **signed**: positive = long
  (buy), negative = short (sell).

There is a single entry point for trading — `createOrder` — which behaves as
both a marketable (aggressive) order and a resting (passive) order depending
on the book.

## Placing an order

```solidity
createOrder(uint256 price, int256 quantity, TimeInForce tif)
```

- `price` — your **limit** price (must be on the tick grid).
- `quantity` — signed size. Positive buys/longs, negative sells/shorts.
- `tif` — how long the order lives: `GTC` rests the unfilled remainder, `IOC`
  cancels it, `FOK` requires the whole size to fill at once.

What happens, in order:

1. Global funding is refreshed and your funding is settled.
2. The order **matches immediately** against the opposite side, walking
   prices from best toward your limit:
   - A **buy** fills against asks priced **at or below** your limit.
   - A **sell** fills against bids priced **at or above** your limit.
3. Any quantity left after matching **rests** on the book at your limit price
   (subject to the limits below).
4. Your Initial Margin is checked; insufficient free margin reverts. A
   *reduce-only* order has a narrower check (see below).

Each fill must also be paid for: if a fill's realized loss is more than your
vault balance, the whole order reverts with `InsufficientMarginBalance`.

To trade like a **market order**, submit an aggressive limit price (e.g. far
through the book); it fills against everything up to that price and rests the
remainder. To trade **passively**, price it away from the top of book so it
rests as a maker order.

### Simulating first

`simulateOrder(price, quantity)` is a read-only preview that returns the
`filledQuantity`, the volume-weighted `averageFillPrice`, and the
`remainingQuantity` that would rest — without sending a transaction.

## How matching works

Orders match when they cross:

1. **Crossing price** — buy limit ≥ ask, or sell limit ≤ bid.
2. **Opposite direction** — buys take from the ask queue, sells from the bid
   queue.
3. **FIFO within a level** — the oldest maker order at a price fills first.

Trades execute at the **maker's price**, so an aggressive taker gets price
improvement when the resting order is better than its limit.

```mermaid
flowchart TD
    A["createOrder(price, quantity, tif)"] --> B["Refresh and settle funding"]
    B --> C{"Crosses the book?<br/>buy: ask at or below limit<br/>sell: bid at or above limit"}
    C -->|Yes| D["Fill best levels first, FIFO within a level,<br/>at the maker's price"]
    D --> E{"Quantity left?"}
    C -->|No| E
    E -->|Yes| F["Rest remainder on the book at your limit"]
    E -->|No| G["Fully filled"]
    F --> H{"Reduce-only?"}
    G --> H
    H -->|No| I["Check Initial Margin (revert if short)"]
    H -->|Yes| J["Revert only if IM rises or<br/>the MM shortfall grows"]
```

Example book depth (asks high→low, bids high→low; each level is a FIFO queue):

| Side | Price | Resting queue (oldest first) |
| ---- | ----- | ---------------------------- |
| Ask  | 4.15  | Seller A                     |
| Ask  | 4.14  | Seller B                     |
| Ask  | 4.12  | Seller C                     |
| Bid  | 4.11  | Buyer Y, Buyer Z             |
| Bid  | 4.10  | Buyer W                      |

- New **buy @ 4.12** → fills Seller C at 4.12.
- New **sell @ 4.11** → fills Buyer Y first (oldest in the queue).

`getBestBidPrice()` / `getBestAskPrice()` return the top of book, and
`getQuantityAtPrice(price, isBid)` the resting size at a level.

## Positions from fills

Each fill updates your single **net position** rather than creating separate
lots:

- Adding in the same direction updates your **weighted-average entry price**.
- Trading the opposite direction **reduces** the position (realizing PnL on
  the closed part), and if it exceeds your size, **flips** you to the other
  side at the new price.

See [Positions & Funding](./04.Positions-and-Funding.md) for the details.

## Reduce-only orders may leave you below Initial Margin

If a `createOrder` is on the opposite side of your current position and does
not exceed its size, it is treated as **reduce-only**. It may leave you below
Initial Margin — you can always de-risk, even when close to your margin
limits — as long as it does not:

- raise your portfolio Initial Margin requirement, or
- widen your shortfall below Maintenance Margin.

A reduce near the mark passes both. Selling further below the mark than the
maintenance shock (or buying that far above it) realizes more loss than the
margin it releases, so it reverts if it would leave you below Maintenance
Margin, or deeper below it. Batch calls (`createOrders`,
`updateOrders`) always require full Initial Margin.

## Closing or reducing a position

There is no expiry and no settlement step. To close, submit an opposite-side
order:

```
Open:  +1.0 long  @ 4.10
Close: -1.0 sell  @ 4.14

Realized PnL = (4.14 − 4.10) × 1.0 = 0.04 per unit (before fees/funding)
```

Fully offsetting flattens the position; partially offsetting leaves a smaller
position at the **same** entry price.

## Cancelling resting orders

```solidity
cancelOrder(bytes32 orderId)
```

Removes your unmatched order from the book and releases the margin it
reserved. Only the order's owner can cancel it (`OrderNotBelongToSender`).

## Order limits

| Limit                       | Value / rule                                                    |
| --------------------------- | -------------------------------------------------------------- |
| Price grid                  | Multiple of `minimumPriceIncrement` (else `InvalidPrice`)      |
| Max resting orders per user | `MAX_ORDERS_PER_PARTICIPANT = 100` (`MaxOrdersPerParticipantReached`) |
| Max price levels per side   | `MAX_PRICE_LEVELS_PER_SIDE = 200` (`MaxPriceLevelsReached`)     |
| Margin                      | Portfolio IM computed by the PME                               |
| Quantity                    | Non-zero, `QUANTITY_DECIMALS = 6` (`InvalidSize`)              |

`minimumMarginPerOrder`, its setter/event, and `OrderMarginTooLow` are deprecated compatibility ABI only; the configured value is not enforced.

## Useful views

| View                                | Returns                                              |
| ----------------------------------- | ---------------------------------------------------- |
| `simulateOrder(price, qty)`         | Preview fill / average price / resting remainder     |
| `getBestBidPrice()` / `getBestAskPrice()` | Top of book                                    |
| `getQuantityAtPrice(price, isBid)`  | Resting quantity at a price level                    |
| `getOrder(orderId)`                 | A single order                                       |
| `getUserOrders(user)`               | A user's open order ids                              |

---

## Read next

- [Positions & Funding](./04.Positions-and-Funding.md) — what happens after a fill.
- [Fees](./06.Fees.md) — what each trade costs.
