> ## Documentation Index
> Fetch the complete documentation index at: https://docs.outcome.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# HIP-4 SDK Utilities: Pricing, Coins, and Orderbook

> Pure utility functions from @outcome.xyz/hip4 for tick-aligned pricing, market discovery, coin name encoding, cost estimation, and price feed streams.

The SDK exports a set of standalone utility functions alongside the main adapter. These functions have no side effects and no dependency on an initialized adapter instance - import and call them directly. They cover four areas: tick-aligned price formatting, market discovery and classification, coin name encoding, and orderbook cost estimation. Two stream constructors for live chart feeds are also covered here.

## Pricing utilities

These functions handle Hyperliquid's magnitude-based decimal precision and minimum order size constraints.

### `computeTickSize(price)`

Returns the tick size for a given price using 5 significant figures. Returns `0.00001` for a price of 0 or less.

```typescript theme={null}
import { computeTickSize } from "@outcome.xyz/hip4";

computeTickSize(0.55);    // 0.00001
computeTickSize(1.25);    // 0.0001
computeTickSize(100);     // 0.01
```

### `roundToTick(price)`

Rounds a price to the nearest tick boundary (5 significant figures).

```typescript theme={null}
import { roundToTick } from "@outcome.xyz/hip4";

roundToTick(0.551234);   // 0.55123 (rounded to the nearest 0.00001)
roundToTick(123.456);    // 123.46
```

### `formatPrice(price)`

Formats a price as a tick-aligned string (5 significant figures) with trailing zeros stripped. Below 0.1 it can return 6 or more decimals, which HIP-4 outcome orders don't accept, so use `formatOutcomePrice` for outcome prices. `placeOrder` uses `formatOutcomePrice` for limit prices.

```typescript theme={null}
import { formatPrice } from "@outcome.xyz/hip4";

formatPrice(0.5500);     // "0.55"
formatPrice(0.6512);     // "0.6512"
formatPrice(1.20);       // "1.2"
formatPrice(0.0123456);  // "0.012346"
```

### `formatOutcomePrice(price)`

Formats a HIP-4 outcome price for the order wire: at most 5 significant figures and at most 5 decimals (a tick of `0.00001`), with trailing zeros stripped. Accepts a number or a string, so user input never has to pass through a float. `placeOrder` and `placeOrders` apply it to limit prices, so you only need it to show or store the price that will be sent. Available from 1.3.0-beta.0.

```typescript theme={null}
import { formatOutcomePrice } from "@outcome.xyz/hip4";

formatOutcomePrice("0.550012");   // "0.55001"
formatOutcomePrice("0.0123456");  // "0.01235"
formatOutcomePrice(0.65);         // "0.65"
```

### `stripZeros(str)`

Removes trailing zeros from a numeric string.

```typescript theme={null}
import { stripZeros } from "@outcome.xyz/hip4";

stripZeros("0.65000");   // "0.65"
stripZeros("1.0000");    // "1"
```

### `getMinShares(markPx, minNotional?)`

Returns the minimum number of shares required to meet the minimum notional at a given mark price. It divides by the cheaper side's price, clamped to at least 0.01: `ceil(minNotional / max(0.01, min(markPx, 1 - markPx)))`. `minNotional` defaults to `MIN_NOTIONAL` (\$1).

```typescript theme={null}
import { getMinShares } from "@outcome.xyz/hip4";

getMinShares(0.65);      // 3   (ceil(1 / 0.35))
getMinShares(0.10);      // 10  (ceil(1 / 0.10))
getMinShares(0.65, 5);   // 15  (ceil(5 / 0.35))
```

### `MIN_NOTIONAL`

The minimum order notional in USD. Currently `1`. The SDK rejects limit orders below this threshold (or below the adapter's higher `minOrderNotional`, if set) before submission.

```typescript theme={null}
import { MIN_NOTIONAL } from "@outcome.xyz/hip4";

console.log(MIN_NOTIONAL);   // 1
```

<Note>
  Want a stricter floor than the protocol minimum? Don't try to override `MIN_NOTIONAL` itself -
  it always reflects Hyperliquid's real minimum. Instead, pass `minOrderNotional` to
  `createHIP4Adapter` to raise the client-side pre-submission floor for that adapter instance. See
  [`minOrderNotional`](/sdk/reference/trading-adapter#minordernotional) in the Trading Adapter
  reference.
</Note>

## Market discovery

These functions help you find and describe recurring HIP-4 markets from raw metadata.

### `parseDescription(desc)`

Parses a pipe-delimited recurring market description string into a structured object. Returns `null` if the string isn't a valid `priceBinary` description.

```typescript theme={null}
import { parseDescription } from "@outcome.xyz/hip4";

parseDescription(
  "class:priceBinary|underlying:BTC|expiry:20260311-0300|targetPrice:69070|period:1d"
);
// {
//   class: "priceBinary",
//   underlying: "BTC",
//   expiry: Date,         // parsed UTC Date
//   targetPrice: 69070,
//   period: "1d"
// }
```

### `discoverPriceBinaryMarkets(meta, mids)`

Scans raw `outcomeMeta` and returns the active recurring price binary markets as `PriceBinaryMarket` objects. A market is included when its description parses as `priceBinary`, its expiry is in the future, and `mids` has a price for its underlying.

```typescript theme={null}
import { discoverPriceBinaryMarkets } from "@outcome.xyz/hip4";

const outcomeMeta = await hip4.client.fetchOutcomeMeta();
const mids = await hip4.client.fetchAllMids();

const markets = discoverPriceBinaryMarkets(outcomeMeta, mids);
// [{ outcomeId, underlying, targetPrice, expiry, period, yesCoin, noCoin, yesAsset, noAsset, ... }]
```

### `periodMinutes(period)`

Converts a period string to its equivalent number of minutes. Unrecognized strings return `15`.

```typescript theme={null}
import { periodMinutes } from "@outcome.xyz/hip4";

periodMinutes("1d");    // 1440
periodMinutes("1h");    // 60
periodMinutes("15m");   // 15
```

### `formatMarketLabel(market)`

Returns a short human-readable label for a `PriceBinaryMarket`, combining the underlying asset and period.

```typescript theme={null}
import { formatMarketLabel } from "@outcome.xyz/hip4";

formatMarketLabel(market);    // "BTC-1d"
```

### `timeToExpiry(market)`

Returns the number of minutes until a `PriceBinaryMarket` expires, as a number. Negative means the market has already expired.

```typescript theme={null}
import { timeToExpiry } from "@outcome.xyz/hip4";

const [market] = discoverPriceBinaryMarkets(outcomeMeta, mids);
timeToExpiry(market);    // e.g. 154.2 (minutes)
```

## Market classification

These functions classify raw HIP-4 outcomes into typed `HIP4Market` objects.

### `classifyOutcome(outcome, questions, precomputedIndex?, templates?)`

Classifies a single outcome from the raw API response. Returns a `HIP4Market` with the appropriate `type` discriminant. When you classify many outcomes in a loop, build the question index once with `buildQuestionIndex(questions)` and pass it as the third argument. `name`, `sides[].name`, and `questionName` are always the names Hyperliquid sends. Pass the `outcomeTemplates` registry (`hip4.client.fetchOutcomeTemplates()`) as the optional fourth argument to render readable `parsedName`, `sides[].parsedName`, and `parsedQuestionName` for template markets, as `fetchMarkets` does. Without it, the parsed fields keep the wire names, except that plain side names lose their `template:` prefix (`"template:Yes"` reads `"Yes"`). The parsed fields are available from 1.3.0.

```typescript theme={null}
import { classifyOutcome } from "@outcome.xyz/hip4";

const market = classifyOutcome(outcome, questions);
// market.type === "defaultBinary" | "labelledBinary" | "multiOutcome" | "priceBucket"
```

### `classifyAllOutcomes(outcomes, questions, templates?)`

Classifies all outcomes from a full `outcomeMeta` response. Returns an array of `HIP4Market` objects. The optional `templates` argument works as in `classifyOutcome`.

```typescript theme={null}
import { classifyAllOutcomes } from "@outcome.xyz/hip4";

const markets = classifyAllOutcomes(outcomeMeta.outcomes, outcomeMeta.questions);
```

## Coin helpers

HIP-4 uses a specific coin naming convention for order book lookups and order placement. These helpers encode and decode those identifiers.

| Helper | Example output | Description |
| - | - | - |
| `sideCoin(outcomeId, sideIndex)` | `"#5160"` | Tradeable side coin string |
| `sideAssetId(outcomeId, sideIndex)` | `100005160` | Numeric asset ID for order wire format |
| `parseSideCoin(coin)` | `{ outcomeId: 516, sideIndex: 0 }` | Decode a side coin string |
| `outcomeCoin(outcomeId)` | `"@516"` | Outcome-level coin name (not used for prices or orders) |

```typescript theme={null}
import { sideCoin, sideAssetId, parseSideCoin, outcomeCoin } from "@outcome.xyz/hip4"
sideCoin(516, 0);          // "#5160"
sideCoin(516, 1);          // "#5161"
sideAssetId(516, 0);       // 100005160
parseSideCoin("#5160");    // { outcomeId: 516, sideIndex: 0 }
parseSideCoin("invalid");  // null
outcomeCoin(516);          // "@516"
```

<Note>
  Use `sideCoin` output (e.g. `"#5160"`) as the `outcome` field when calling `hip4.trading.placeOrder`. `hip4.marketData.fetchPrice` takes the outcome ID as a string (e.g. `"516"`), not an `@` coin.
</Note>

## Orderbook utilities

These functions estimate trade cost and potential return from token amounts and prices. You pass prices in cents (0 to 100).

### `computeEstimatedCost(tokenAmount, orderType, limitPriceCents, marketPriceCents?)`

Estimates the USDC cost for a given token amount. For limit orders, uses `limitPriceCents`. For market orders, uses `marketPriceCents` when provided, otherwise returns `tokenAmount` as a fallback (the worst case of 1 USDC per share).

```typescript theme={null}
import { computeEstimatedCost } from "@outcome.xyz/hip4";

computeEstimatedCost(100, "limit", 65, undefined);     // 65 USDC
computeEstimatedCost(100, "market", null, 65);          // 65 USDC
```

### `computeTradeCost(params)`

Returns a `TradeCostResult` with all three values needed to display a trade preview.

```typescript theme={null}
import { computeTradeCost } from "@outcome.xyz/hip4";
import type { TradeCostResult } from "@outcome.xyz/hip4";

const result: TradeCostResult = computeTradeCost({
  tokenAmount: 100,
  orderType: "limit",
  limitPriceCents: 65,
  marketPriceCents: undefined,
});
// {
//   estimatedCost: 65,       // USDC to spend
//   potentialReturn: 100,    // max payout (= tokenAmount)
//   displayShares: 100,      // shares to show in UI
// }
```

### `computePotentialReturn(tokenAmount)`

Returns the maximum payout for a given number of shares. Each share pays 1 USDC if the outcome resolves in your favor.

```typescript theme={null}
import { computePotentialReturn } from "@outcome.xyz/hip4";

computePotentialReturn(100);    // 100
computePotentialReturn(0);      // 0
```

## Price feed streams

These constructors create continuously-updating chart feeds by merging historical candle data with real-time WebSocket ticks.

### `createPriceFeed(marketData, marketId, onSnapshot, options?)`

Creates a live price feed for a single prediction market outcome. Returns an unsubscribe function.

```typescript theme={null}
import { createPriceFeed } from "@outcome.xyz/hip4";
import type { PriceFeedSnapshot, PriceFeedOptions } from "@outcome.xyz/hip4";

const options: PriceFeedOptions = {
  interval: "1h",          // candle interval; default "1h"
  lookbackMs: 7 * 24 * 60 * 60 * 1000,   // 7 days lookback; default 14 days
  sideIndex: 0,            // 0 = Yes side, 1 = No side; default 0
};

const unsub = createPriceFeed(
  hip4.marketData,
  "516",
  (snapshot: PriceFeedSnapshot) => {
    if (!snapshot.ready) return;
    renderChart(snapshot.candles);
    updateMidPrice(snapshot.currentMid);
  },
  options,
);

// Stop the feed when done
unsub();
```

`PriceFeedSnapshot` fields:

| Field | Type | Description |
| - | - | - |
| `marketId` | `string` | The market ID passed to the constructor |
| `candles` | `PriceFeedCandle[]` | Full candle array (historical + live updates) |
| `currentMid` | `number \| null` | Latest mid-price tick; may be newer than the last candle |
| `ready` | `boolean` | `false` until the initial historical candle fetch completes |

Historical candles always come from side 0. With `sideIndex: 1`, only the live ticks follow side 1.

### `createPerpPriceFeed(client, coin, onSnapshot, options?)`

Creates a live price feed for a perpetual market coin (e.g. `"BTC"`, `"ETH"`). Uses the `candle` WebSocket channel for authoritative OHLCV data and `allMids` for the initial mid-price.

```typescript theme={null}
import { createPerpPriceFeed } from "@outcome.xyz/hip4";
import type { PerpPriceFeedSnapshot } from "@outcome.xyz/hip4";

const unsub = createPerpPriceFeed(
  hip4.client, // the adapter's HIP4Client
  "BTC",
  (snapshot: PerpPriceFeedSnapshot) => {
    if (!snapshot.ready) return;
    renderChart(snapshot.candles);
  },
  { interval: "1h", lookbackMs: 14 * 24 * 60 * 60 * 1000 },
);

unsub();
```

`PerpPriceFeedSnapshot` differs from `PriceFeedSnapshot` in its `coin` field (instead of `marketId`).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.