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

# Perp Trading Example

> End-to-end example: deposit, fund a parent margin account, open a 5x long, attach TP/SL, and close

This example walks through the full perp lifecycle on Monaco using `@0xmonaco/core`: depositing collateral, funding a parent margin account, opening a leveraged position, attaching TP/SL conditional orders, and closing.

For concepts, see [Margin](/trading-mechanics/perps), [Account Vault](/developers/vault#deposit-directly-to-perps-collateral), and [Positions](/developers/positions). For the spot-only walkthrough, see [Complete Integration Example](/examples/complete-flow).

## Installation

```bash theme={null}
npm install @0xmonaco/core viem
```

## Complete Example

```typescript theme={null}
import { MonacoSDK } from "@0xmonaco/core";
import { createWalletClient, custom, parseUnits } from "viem";
import { seiTestnet } from "viem/chains";

async function perpFlowExample() {
  // ---------------------------------------------------------------------------
  // 1. Connect wallet and initialize SDK
  // ---------------------------------------------------------------------------
  const [walletAccount] = await window.ethereum.request({
    method: "eth_requestAccounts",
  });

  const walletClient = createWalletClient({
    account: walletAccount,
    chain: seiTestnet,
    transport: custom(window.ethereum),
  });

  const sdk = new MonacoSDK({
    walletClient,
    network: "staging",
    seiRpcUrl: "https://evm-rpc-testnet.sei-apis.com", // public testnet RPC — use a private provider in production
  });

  // ---------------------------------------------------------------------------
  // 2. Authenticate
  // ---------------------------------------------------------------------------
  const auth = await sdk.login("your-client-id");
  console.log("Authenticated as", auth.user.address);

  // ---------------------------------------------------------------------------
  // 3. Deposit USDC into Monaco's vault (skip if collateral is already on-chain)
  // ---------------------------------------------------------------------------
  // Get USDC asset metadata from any pair that uses it as quote
  const ethUsdcPair = await sdk.market.getTradingPairBySymbol("ETH/USDC");
  if (!ethUsdcPair) throw new Error("ETH/USDC pair not found");

  const usdcAssetId = ethUsdcPair.quoteAssetId;
  const usdcDecimals = ethUsdcPair.quoteDecimals;

  // Approve and deposit 5,000 USDC into the vault
  const depositAmount = parseUnits("5000", usdcDecimals);
  if (await sdk.vault.needsApproval(usdcAssetId, depositAmount)) {
    await sdk.vault.approve(usdcAssetId, depositAmount);
  }
  await sdk.vault.deposit(usdcAssetId, depositAmount);

  // ---------------------------------------------------------------------------
  // 4. Find a perp trading pair
  // ---------------------------------------------------------------------------
  // Perp pairs are distinguished by marketType === "MARGIN"
  const { tradingPairs } = await sdk.market.getPaginatedTradingPairs({
    pageSize: 100,
  });
  const perpPair = tradingPairs.find(
    (p) => p.marketType === "MARGIN" && p.baseToken === "ETH",
  );
  if (!perpPair) throw new Error("No ETH perp pair available");

  const tradingPairId = perpPair.id;
  console.log(`Trading on ${perpPair.symbol} (${tradingPairId})`);

  // ---------------------------------------------------------------------------
  // 5. Fund the parent margin account with 1,000 USDC.
  // ---------------------------------------------------------------------------
  await sdk.marginAccounts.transferCollateralToParentMarginAccount({
    asset: "USDC",
    amount: "1000",
  });

  // Verify funding succeeded without storing a margin account id.
  const summary = await sdk.marginAccounts.getParentMarginAccountSummary();
  console.log(`Equity: ${summary.equity}, free: ${summary.freeCollateral}`);

  // ---------------------------------------------------------------------------
  // 6. Pre-flight the order with simulateRiskBucketOrderRisk
  // ---------------------------------------------------------------------------
  // Open a 5x long ETH at $2,000 with 1.5 ETH size
  const orderQuantity = "1.5";
  const limitPrice = "2000";
  const leverage = "5";

  const risk = await sdk.marginAccounts.simulateRiskBucketOrderRisk({
    tradingPairId,
    side: "BUY",
    orderType: "LIMIT",
    price: limitPrice,
    quantity: orderQuantity,
    leverage,
  });

  if (!risk.accepted) {
    console.error("Order would be rejected:", risk.rejectReason);
    return;
  }
  console.log(
    `Estimated liquidation: ${risk.estimatedLiquidationPrice}, ` +
      `free after: ${risk.freeCollateralAfter}`,
  );

  // ---------------------------------------------------------------------------
  // 7. Place the perp order
  // ---------------------------------------------------------------------------
  const order = await sdk.trading.placeLimitOrder(
    tradingPairId,
    "BUY",
    orderQuantity,
    limitPrice,
    {
      tradingMode: "MARGIN",
      leverage,
    },
  );
  console.log(`Order ${order.orderId} submitted`);

  // ---------------------------------------------------------------------------
  // 8. Wait for the position to open via websocket
  // ---------------------------------------------------------------------------
  await sdk.ws.connect();

  const positionId: string = await new Promise((resolve, reject) => {
    const timeout = setTimeout(
      () => reject(new Error("Order didn't fill in 30s")),
      30_000,
    );

    const unsubscribe = sdk.ws.userOrders((evt) => {
      if (evt.orderId !== order.orderId) return;
      if (evt.eventType === "OrderFilled" || evt.eventType === "OrderPartiallyFilled") {
        clearTimeout(timeout);
        unsubscribe();
        // Position lookup happens after a small settle delay
        setTimeout(async () => {
          const { positions } = await sdk.positions.listPositions({
            tradingPairId: tradingPairId,
            status: "OPEN",
          });
          const pos = positions[0];
          if (!pos) reject(new Error("Position not found after fill"));
          else resolve(pos.positionId);
        }, 500);
      } else if (evt.eventType === "OrderRejected" || evt.eventType === "OrderCancelled") {
        clearTimeout(timeout);
        unsubscribe();
        reject(new Error(`Order ${evt.eventType}: ${(evt.data as any).reason}`));
      }
    });
  });
  console.log(`Position opened: ${positionId}`);

  // ---------------------------------------------------------------------------
  // 9. Inspect live risk
  // ---------------------------------------------------------------------------
  const positionRisk = await sdk.positions.getPositionRisk(positionId);
  console.log({
    mark: positionRisk.markPrice,
    liquidation: positionRisk.liquidationPrice || "unavailable",
    pnl: positionRisk.unrealizedPnl,
    marginRatio: positionRisk.marginRatio,
  });

  // ---------------------------------------------------------------------------
  // 10. Attach TP and SL (OCO group)
  // ---------------------------------------------------------------------------
  const tpSl = await sdk.positions.attachPositionTpSl(positionId, {
    takeProfit: { triggerPrice: "2200", orderType: "MARKET" },
    stopLoss: { triggerPrice: "1900", orderType: "MARKET" },
    oco: true,
  });
  console.log(
    `TP: ${tpSl.takeProfitOrderId}, SL: ${tpSl.stopLossOrderId}`,
  );

  // Subscribe to conditional-order lifecycle (optional — useful for UI updates)
  const unsubConditionals = sdk.ws.conditionalOrders((evt) => {
    console.log(
      `Conditional ${evt.data.conditionalOrderId} → ${evt.data.state} ` +
        `(${evt.data.reason})`,
    );
  });

  // ---------------------------------------------------------------------------
  // 11. Manual close (if you don't want to wait for TP/SL)
  // ---------------------------------------------------------------------------
  // Half close at limit
  await sdk.positions.closePosition(positionId, {
    closeType: "LIMIT",
    limitPrice: "2050",
    quantity: "0.75",
  });

  // Or full market close
  // await sdk.positions.closePosition(positionId, { closeType: "MARKET" });

  // ---------------------------------------------------------------------------
  // 12. Cleanup
  // ---------------------------------------------------------------------------
  unsubConditionals();
  sdk.ws.disconnect();
}

perpFlowExample().catch(console.error);
```

## What this example covers

1. **Deposit** — using the standard spot vault flow; collateral has to land on Monaco before it can be transferred to a margin account.
2. **Trading pair lookup** — perps are filtered by `marketType === "MARGIN"`.
3. **Parent margin account funding** — one collateral asset per account; USDC is the default.
4. **Preflight via `simulateRiskBucketOrderRisk`** — never place a non-trivial perp order without checking acceptance and projected liquidation.
5. **Order placement with perp options** — `tradingMode: "MARGIN"` and `leverage` are the key perp options; `side` sets the one-way direction.
6. **Position lookup after fill** — order events stream over `userOrders`; the position itself appears in `listPositions` shortly after.
7. **Live risk inspection** — `getPositionRisk` is the right call for live UI updates (cheaper than `getPosition`).
8. **TP/SL via `attachPositionTpSl`** — pass `oco: true` so the two legs cancel each other on trigger.
9. **Conditional order lifecycle subscription** — `sdk.ws.conditionalOrders` streams TP/SL state changes.
10. **Close** — partial or full, MARKET or LIMIT.

## Common variations

* **Reduce-only manual close.** Instead of `closePosition`, use `placeMarketOrder` with `reduceOnly: true` — useful when you want to apply slippage protection or specific TIF.
* **Withdraw collateral.** After closing all positions, use `transferCollateralFromParentMarginAccount` to move USDC back to the wallet, then `sdk.vault.withdraw(...)` to leave Monaco entirely.

## Reference links

* [Margin](/trading-mechanics/perps)
* [Account Vault](/developers/vault#deposit-directly-to-perps-collateral)
* [Positions](/developers/positions)
* [Margin Accounts SDK reference](/sdk/typescript/margin-accounts)
* [Positions SDK reference](/sdk/typescript/positions)
* [WebSockets reference](/sdk/typescript/websockets)
* [v0.7.0 release notes](/releases/v0.7.0)


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