Skip to content

Order Event

Each time you call place_order, update_order, or cancel_order, you will receive an order or deal event from the exchange. We also provide an interface to handle these events, which is extremely helpful if you are building your own trading system.

Note

Reserved orders (placed pre-market) do not trigger event callbacks at placement time. Reserved orders are dispatched at 08:30 each trading day, and event callbacks fire only after dispatch.

Handle Order and Deal Events

You can use set_order_callback to handle order/deal events. The example below shows a custom callback function (order_cb) that prints my_order_callback first and then the order/deal event payload.

Set Order Callback

# Option 1: decorator
@api.on_order
def order_cb(stat, msg):
    print('my_order_callback')
    print(stat, msg)

# Option 2: traditional
def order_cb(stat, msg):
    print('my_order_callback')
    print(stat, msg)
api.set_order_callback(order_cb)

Place Order

# Contract
contract = api.contracts.get("2890")
# Order
order = sj.StockOrder(
    action=sj.Action.Buy,
    price=27.1,
    quantity=2,
    price_type=sj.StockPriceType.LMT,
    order_type=sj.OrderType.ROD,
    order_lot=sj.StockOrderLot.Common,
    order_cond=sj.StockOrderCond.Cash,
    account=api.stock_account,
)
# Place
trade = api.place_order(contract, order)

Order Event

Out

my_order_callback
<OrderState.StockOrder: 'SORDER'> {
    'event_id': 'v1:SO:A1EGVO30H:BOnKZSD:4',
    'operation': {
        'op_type': 'New',
        'op_code': '00',
        'op_msg': ''
    },
    'order': {
        'id': '892f730b',
        'seqno': '361840',
        'ordno': 'Y23CL',
        'account': {
            'account_type': 'S',
            'person_id': '',
            'broker_id': 'YOUR_BROKER_ID',
            'account_id': 'YOUR_ACCOUNT_ID',
            'signed': True,
            'username': ''
        },
        'action': 'Buy',
        'price': 26.85,
        'quantity': 1,
        'order_type': 'ROD',
        'price_type': 'LMT',
        'order_cond': 'Cash',
        'order_lot': 'Common',
        'custom_field': ''
    },
    'status': {
        'id': '892f730b',
        'exchange_ts': 1779333919.92,
        'modified_price': 0.0,
        'cancel_quantity': 0,
        'order_quantity': 1,
        'web_id': '137'
    },
    'contract': {
        'exchange': 'TSE',
        'code': '2890',
        'security_type': 'STK',
        'symbol': '',
        'name': '',
        'currency': 'TWD'
    }
}

Deal Event

Out

my_order_callback
<OrderState.StockDeal: 'SDEAL'> {
    'event_id': 'v1:SD:A1EGVO30H:BOnKZSD:7',
    'trade_id': '9c6ae2eb',
    'seqno': '269866',
    'ordno': 'IN497',
    'exchange_seq': '669915',
    'broker_id': 'YOUR_BROKER_ID',
    'account_id': 'YOUR_ACCOUNT_ID',
    'action': 'Buy',
    'code': '2890',
    'order_cond': 'Cash',
    'order_lot': 'Common',
    'price': 27.1,
    'quantity': 2,
    'web_id': '137',
    'custom_field': '',
    'ts': 1779333920.0
}

Subscribe Order/Deal Events

shioaji auth subscribe-trade --account-type S

Note

The server does not subscribe automatically on startup. Subscribe each account once after the server starts; to receive both stock and futures events, subscribe with --account-type S and --account-type F separately. See Subscribe Trade for parameters.

Receive Order/Deal Events

shioaji order events
This command streams order/deal events continuously. Press Ctrl+C to stop.

Place Order (run in another terminal)

shioaji order place \
  --code 2890 \
  --action buy \
  --price 27.1 \
  --quantity 2 \
  --price-type lmt \
  --order-type rod \
  --order-lot common \
  --account YOUR_BROKER_ID-YOUR_ACCOUNT_ID

Order Event

Out

{
  "state": "StockOrder",
  "data": {
    "StockOrder": {
      "operation": {"op_type": "New", "op_code": "00", "op_msg": ""},
      "order": {
        "id": "892f730b",
        "seqno": "361840",
        "ordno": "Y23CL",
        "account": {
          "account_type": "S",
          "person_id": "",
          "broker_id": "YOUR_BROKER_ID",
          "account_id": "YOUR_ACCOUNT_ID",
          "signed": true,
          "username": ""
        },
        "action": "Buy",
        "price": 26.85,
        "quantity": 1,
        "order_type": "ROD",
        "price_type": "LMT",
        "order_cond": "Cash",
        "order_lot": "Common",
        "custom_field": ""
      },
      "status": {
        "id": "892f730b",
        "exchange_ts": 1779333919.92,
        "modified_price": 0.0,
        "cancel_quantity": 0,
        "order_quantity": 1,
        "web_id": "137"
      },
      "contract": {
        "exchange": "TSE",
        "code": "2890",
        "security_type": "STK",
        "symbol": "",
        "name": "",
        "currency": "TWD"
      }
    }
  }
}

Deal Event

Out

{
  "state": "StockDeal",
  "data": {
    "StockDeal": {
      "trade_id": "9c6ae2eb",
      "seqno": "269866",
      "ordno": "IN497",
      "exchange_seq": "669915",
      "broker_id": "YOUR_BROKER_ID",
      "account_id": "YOUR_ACCOUNT_ID",
      "action": "Buy",
      "code": "2890",
      "order_cond": "Cash",
      "order_lot": "Common",
      "price": 27.1,
      "quantity": 2,
      "web_id": "137",
      "custom_field": "",
      "ts": 1779333920.0
    }
  }
}

Subscribe Order/Deal Events

curl -X POST http://localhost:8080/api/v1/auth/subscribe_trade \
  -H "Content-Type: application/json" \
  -d '{"broker_id": "YOUR_BROKER_ID", "account_id": "YOUR_ACCOUNT_ID", "account_type": "S"}'

Note

The server does not subscribe automatically on startup. Subscribe each account once after the server starts; without it, the SSE stream only emits heartbeats. Stock and futures accounts must be subscribed separately. See Subscribe Trade.

Receive Order/Deal Events

curl -N http://localhost:8080/api/v1/stream/data/order_event
This command streams order/deal events continuously. Press Ctrl+C to stop.

Place Order (run in another terminal)

curl -X POST http://localhost:8080/api/v1/order/place_order \
  -H 'Content-Type: application/json' \
  -d '{
    "contract": {"security_type": "STK", "exchange": "TSE", "code": "2890"},
    "stock_order": {
      "action": "Buy",
      "price": 27.1,
      "quantity": 2,
      "price_type": "LMT",
      "order_type": "ROD",
      "order_lot": "Common",
      "order_cond": "Cash",
      "account": {
        "broker_id": "YOUR_BROKER_ID",
        "account_id": "YOUR_ACCOUNT_ID"
      }
    }
  }'

Order Event

Out

event:order_event
data:{
  "state": "StockOrder",
  "data": {
    "StockOrder": {
      "operation": {"op_type": "New", "op_code": "00", "op_msg": ""},
      "order": {
        "id": "892f730b",
        "seqno": "361840",
        "ordno": "Y23CL",
        "account": {
          "account_type": "S",
          "person_id": "",
          "broker_id": "YOUR_BROKER_ID",
          "account_id": "YOUR_ACCOUNT_ID",
          "signed": true,
          "username": ""
        },
        "action": "Buy",
        "price": 26.85,
        "quantity": 1,
        "order_type": "ROD",
        "price_type": "LMT",
        "order_cond": "Cash",
        "order_lot": "Common",
        "custom_field": ""
      },
      "status": {
        "id": "892f730b",
        "exchange_ts": 1779333919.92,
        "modified_price": 0.0,
        "cancel_quantity": 0,
        "order_quantity": 1,
        "web_id": "137"
      },
      "contract": {
        "exchange": "TSE",
        "code": "2890",
        "security_type": "STK",
        "symbol": "",
        "name": "",
        "currency": "TWD"
      }
    }
  }
}

Deal Event

Out

event:order_event
data:{
  "state": "StockDeal",
  "data": {
    "StockDeal": {
      "trade_id": "9c6ae2eb",
      "seqno": "269866",
      "ordno": "IN497",
      "exchange_seq": "669915",
      "broker_id": "YOUR_BROKER_ID",
      "account_id": "YOUR_ACCOUNT_ID",
      "action": "Buy",
      "code": "2890",
      "order_cond": "Cash",
      "order_lot": "Common",
      "price": 27.1,
      "quantity": 2,
      "web_id": "137",
      "custom_field": "",
      "ts": 1779333920.0
    }
  }
}

Maintaining Your Own State (1.7.6+)

If you want to keep order state in your own data structure, for example with your own computed fields, update it when a callback arrives.

The built-in Trade is read-only

It updates automatically from reports, but cannot be written to:

trade.status.status = "Filled"
# AttributeError: attribute 'status' of 'builtins.OrderStatusInfo' objects is not writable

Keep fields you need to write in your own data structure.

Duplicates β€” The same event_id (msg["event_id"]) means the same report was delivered again; skip it. Callbacks do not filter them for you.

Missed reports β€” The ID ends with an increasing sequence number. When the leading part matches, compare whether they are consecutive:

"v1:SO:A1EGVO30H:BOnKZSD:4"
"v1:SO:A1EGVO30H:BOnKZSD:6"   # 5 has not arrived yet; it may still be late

A gap does not mean the report is confirmed lost. If you would rather not track this yourself, use trade_cache_health instead.