Skip to main content

Overview

The Orders API allows you to place trading orders on events. Orders are processed asynchronously through a Redis queue and matched by the trading engine.

Initiate Order

Endpoint: POST /order/initiatePlaces a new trading order on an event. Orders are queued and processed asynchronously.

Request Body

All request body parameters are validated using Zod schema defined in packages/zod/src/index.ts:9-19.
event_id
string
required
The event ID to place the order on.Example: “3169798”
l1_expected_price
number
required
The expected price per contract in INR. Must be at least 0.1.Validation: Minimum value of 0.1Example: 5.5
l1_order_quantity
number
required
The quantity of contracts to trade. Must be at least 0.1.Validation: Minimum value of 0.1Example: 100
offer_type
string
required
The side of the order.Allowed values: "buy" or "sell"Example: "buy"
order_type
string
required
The type of order.Allowed values:
  • "LO" - Limit Order
  • "MO" - Market Order
Example: "LO"
userid
string
required
The user ID placing the order.Example: “user_12345”

Request Validation

The endpoint uses the initiateOrderValidator middleware which validates the request body against the following Zod schema:
export const intiateOrderZodSchema = z.object({
  event_id: z.string(),
  l1_expected_price: z.number().min(0.1, {
    message: "Order price must be a positive integer",
  }),
  l1_order_quantity: z.number().min(0.1, {
    message: "Order quantity must be a positive integer",
  }),
  offer_type: z.enum(["buy", "sell"]),
  order_type: z.enum(["LO", "MO"]),
});

Response

status
boolean
required
Always true for successfully queued orders
code
number
required
HTTP status code: 201
message
string
required
Success message: “Order placed successfully”

Example Request

curl -X POST https://api.opinix.trade/order/initiate \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "3169798",
    "l1_expected_price": 5.5,
    "l1_order_quantity": 100,
    "offer_type": "buy",
    "order_type": "LO",
    "userid": "user_12345"
  }'

Example Response

201 - Success
{
  "status": true,
  "code": 201,
  "message": "Order placed successfully"
}
422 - Validation Error (Invalid Price)
{
  "status": false,
  "code": 422,
  "name": "Unprocessable entity",
  "message": "l1_expected_price: Order price must be a positive integer"
}
422 - Validation Error (Invalid Offer Type)
{
  "status": false,
  "code": 422,
  "name": "Unprocessable entity",
  "message": "offer_type: Invalid enum value. Expected 'buy' | 'sell', received 'invalid'"
}
422 - Validation Error (Invalid Order Type)
{
  "status": false,
  "code": 422,
  "name": "Unprocessable entity",
  "message": "order_type: Invalid enum value. Expected 'LO' | 'MO', received 'INVALID'"
}

Order Processing Flow

1

Validation

Request body is validated against Zod schema. Invalid requests return 422 error.
2

Queue

Valid orders are added to Redis order queue with type CREATE_ORDER and a unique UUID.
3

Engine Processing

The matching engine processes orders from the queue and attempts to match them.
4

WebSocket Notification

Order updates are broadcast via WebSocket to subscribed clients.

Order Types

Based on the source code at packages/types/src/index.ts, here are the order-related type definitions:

Order Status Enum

export enum EOrderStatus {
  PENDING = "PENDING",
  PLACED = "PLACED",
}

Order Sides Enum

export enum sides {
  YES = "yes",
  NO = "no",
}

Order Type

export type TOrder = {
  id: string;
  orderBookId: string;
  price: number;
  quantity: number;
  status: "PLACED" | "PENDING";
  createdAt: Date;
};

Order Interface (Engine)

export interface Order {
  price: number;
  quantity: number;
  orderId: string;
  filled: number;
  side: "yes" | "no";
  userId: string;
}

Message Types

When orders are placed, they are queued with the following message structure:

CREATE_ORDER Message

{
  type: "CREATE_ORDER",
  data: {
    market: string;      // event_id
    price: number;       // l1_expected_price
    quantity: number;    // l1_order_quantity
    side: "yes" | "no"; // mapped from offer_type
    userId: string;      // userid
  }
}

ORDER_PLACED Response

After successful matching, the engine responds with:
{
  type: "ORDER_PLACED",
  payload: {
    orderId: string;
    executedQty: number;
    fills: {
      price: number;
      qty: number;
      tradeId: string;
    }[];
  }
}

Implementation Notes

Source Code Reference:
  • Order routes: apps/server/src/router/orderRouter.ts
  • Order handlers: apps/server/src/controllers/order/index.ts:16-38
  • Validation middleware: apps/server/src/middlewares/validators/initiate.validator.ts
  • Zod schema: packages/zod/src/index.ts:9-19
  • Type definitions: packages/types/src/index.ts

Asynchronous Processing

Orders are processed asynchronously through a Redis queue. The API endpoint returns immediately after queueing the order. To receive order execution updates, subscribe to WebSocket events (see WebSocket API).

Order Queue Structure

Each order in the queue has the following structure (from apps/server/src/controllers/order/index.ts:22-32):
{
  type: uuid4(),           // Unique message ID
  data: {
    market: event_id,
    price: l1_expected_price,
    type: "CREATE_ORDER",
    quantity: l1_order_quantity,
    side: offer_type,
    userId: userid.toString()
  }
}

Limit Orders vs Market Orders

  • Limit Order (LO): Order will only execute at the specified price or better
  • Market Order (MO): Order will execute at the best available price immediately
Market orders may execute at a worse price than expected during high volatility.