8. Trạng thái lệnh
Theo dõi tiến trình khớp lệnh bằng cơ chế polling
Mục tiêu
Theo dõi tiến trình khớp của một lệnh cụ thể bằng cách polling sổ lệnh theo chu kỳ.
Luồng xử lý
Order Monitor Service → Trading API (Order Detail) → Execution- Đặt lệnh và lưu
orderId - Polling
get_today_orderstheo chu kỳ ngắn (3 giây) - Kiểm tra trạng thái kết thúc:
FILLED,CANCELLED,REJECTED,EXPIRED,PARTIAL_CANCELLED - Nếu chưa hoàn tất → tiếp tục polling
- Khi đạt trạng thái kết thúc → đóng vòng theo dõi
Trạng thái lệnh
| Status | Ý nghĩa |
|---|---|
PENDING (PD) | Đã nhận, chưa xử lý |
PENDING_APPROVAL | Chờ duyệt |
READY / SENT / QUEUED | Đang chờ khớp |
PARTIAL_FILLED | Khớp một phần |
PARTIAL_CANCELLED (FFPC) | Khớp một phần, phần còn lại đã hủy |
FILLED | Khớp toàn bộ |
PENDING_MODIFY / PENDING_CANCEL | Chờ xác nhận sửa / hủy |
CANCELLED | Đã hủy |
REJECTED | Bị từ chối |
EXPIRED | Hết hạn |
SDK còn khai báo thêm các mã khác mà server có thể trả về (PRE_SESSION, REQUESTED, ERROR...). Từ SDK 3.3.0, Order.status có thể là enum OrderStatus, một str (mã mới mà SDK chưa biết) hoặc None: dùng helpers.enum_value (xem trang 1) để in an toàn.
Sample Code — Sync
import time
from ssi_sdk import Auth, Trading, Config
from ssi_sdk.enums import OrderSide, OrderStatus
from auth_helper import ensure_auth
from helpers import enum_value
config = Config(
client_id="<your_client_id>",
api_key="<your_api_key>",
api_secret="<your_api_secret>",
private_key="<your_private_key>",
)
ACCOUNT_NO = "<your_account_no>"
TERMINAL_STATUSES = {
OrderStatus.FILLED, OrderStatus.CANCELLED,
OrderStatus.REJECTED, OrderStatus.EXPIRED,
OrderStatus.PARTIAL_CANCELLED,
}
with Auth(config) as auth:
ensure_auth(auth, otp="<your_otp>")
with Trading(auth) as trading:
# Bước 1: Đặt lệnh
result = trading.trading.place_limit_order(
account_no=ACCOUNT_NO, symbol="SSI",
side=OrderSide.BUY, quantity=100, price=26000,
)
print(f" Kết quả: {result}")
# Bước 2-5: Polling trạng thái
max_polls = 10
poll_interval = 3 # giây
for i in range(1, max_polls + 1):
orders = trading.portfolio.get_today_orders(ACCOUNT_NO)
if not orders:
time.sleep(poll_interval)
continue
latest = orders[-1]
remaining = latest.quantity - latest.filled_quantity - latest.cancel_quantity
print(
f" Poll {i}: OrderID={latest.order_id} | "
f"Status={enum_value(latest.status)} | "
f"Khớp={latest.filled_quantity}/{latest.quantity} | Còn={remaining}"
)
if latest.status in TERMINAL_STATUSES:
print(f" → Lệnh kết thúc: {enum_value(latest.status)}")
if latest.filled_quantity > 0:
print(f" Đã khớp: {latest.filled_quantity} CP @ {latest.avg_price:,.0f}")
break
time.sleep(poll_interval)
else:
print(f" Hết {max_polls} lần poll, lệnh vẫn đang mở.")Sample Code — Async
import asyncio
from ssi_sdk import AsyncAuth, AsyncTrading, Config
from ssi_sdk.enums import OrderSide, OrderStatus
from auth_helper import ensure_auth_async
from helpers import enum_value
config = Config(...)
ACCOUNT_NO = "<your_account_no>"
TERMINAL_STATUSES = {
OrderStatus.FILLED, OrderStatus.CANCELLED,
OrderStatus.REJECTED, OrderStatus.EXPIRED,
OrderStatus.PARTIAL_CANCELLED,
}
async def main():
async with AsyncAuth(config) as auth:
await ensure_auth_async(auth, otp="<your_otp>")
async with AsyncTrading(auth) as trading:
result = await trading.trading.place_limit_order(
account_no=ACCOUNT_NO, symbol="SSI",
side=OrderSide.BUY, quantity=100, price=26000,
)
for i in range(1, 11):
orders = await trading.portfolio.get_today_orders(ACCOUNT_NO)
if orders and orders[-1].status in TERMINAL_STATUSES:
print(f" Lệnh kết thúc: {enum_value(orders[-1].status)}")
break
await asyncio.sleep(3)
asyncio.run(main())Lưu ý
- Polling phù hợp cho kiểm tra nhanh. Cho real-time, dùng WebSocket Trading.
- Giới hạn số lần poll và thêm timeout để tránh vòng lặp vô hạn.