15. Vị thế, PPMMR & Max mua/bán
Đọc vị thế cổ phiếu (T1/T2), sức mua PPMMR, vị thế phái sinh và khối lượng mua/bán tối đa
Mục tiêu
Đọc các dữ liệu danh mục và sức mua của tài khoản mà không đặt lệnh: vị thế cổ phiếu kèm khối lượng chờ về T1/T2, PPMMR (sức mua, tỷ lệ margin, tiền mua/bán chờ thanh toán), vị thế và PPMMR phái sinh, và khối lượng mua/bán tối đa của một mã. Tương ứng Sample 15 trong repo tutorials (SDK v3.3.2). Cần OTP khi authenticate.
Sample chỉ đọc dữ liệu nên an toàn để chạy, nhưng kết quả là dữ liệu tài khoản thật. Không dán nguyên output vào ticket, chat hay tài liệu.
Luồng xử lý
Auth → Vị thế cổ phiếu → PPMMR cổ phiếu → (Phái sinh) → Max mua/bánget_equity_positions(account): danh sách vị thế, kèm KL mua/bán chờ về T1/T2get_equity_ppmmr(account): sức mua, tỷ lệ margin, tiền mua/bán T0/T1/T2- Nếu có tài khoản phái sinh:
get_derivative_positionsvàget_derivative_ppmmr get_max_buy_sell(account, symbol, price)vàget_max_buy_sell_at_market_price(account, symbol)
Các mẫu trong repo
| Ngôn ngữ | File |
|---|---|
| Python | python/sample_15_positions_ppmmr.py, python/sample_15_positions_ppmmr_async.py |
| Node.js | node/sample_15_positions_ppmmr.js |
| .NET | dotnet/Sample15PositionsPpmmr.cs |
| Go | go/sample_15_positions_ppmmr.go |
Trang này minh họa bằng Python. Repo: ssi-fastconnect-v3-tutorials.
Chuẩn bị
- Cấu hình credentials và số tài khoản (
equity_account) trongconfig.json, xem Tutorial 1. Tài khoản phái sinh là tùy chọn (derivative_account). Sample repo đọc cấu hình quaconfig.py; ở đây dùng placeholder để code gọn. auth_helper.py(token cache, Tutorial 1) và OTP cho lần đăng nhập đầu.helpers.pytrong repo, gồmmask_account(che số tài khoản khi in) vàrequire_account(dừng nếu thiếu tài khoản).
Sample Code — Sync
Code dưới đây rút gọn từ sample trong repo, giữ nguyên luồng. Các lời gọi danh mục có thể trả None khi tài khoản không có dữ liệu, nên code kiểm tra None trước khi duyệt.
from decimal import Decimal
from ssi_sdk import Auth, Data, Trading, Config
from auth_helper import ensure_auth
from helpers import mask_account, require_account
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>"
DERIVATIVE_ACCOUNT_NO = None # đặt số tài khoản phái sinh nếu có
SYMBOL = "SSI"
def money(value) -> str:
return "-" if value is None else f"{value:,.0f}"
require_account(ACCOUNT_NO)
with Auth(config) as auth:
ensure_auth(auth, otp="<your_otp>")
with Trading(auth) as trading, Data(auth) as data:
# Bước 1: Vị thế cổ phiếu
print(f"--- Vị thế cổ phiếu ({mask_account(ACCOUNT_NO)}) ---")
positions = trading.portfolio.get_equity_positions(ACCOUNT_NO) or []
print(f"{'Mã':<8}{'SL':>9}{'Bán được':>10}{'T1 mua':>9}{'T2 mua':>9}{'T1 bán':>9}{'T2 bán':>9}{'Giá vốn':>12}")
for pos in positions:
print(
f"{pos.symbol:<8}{pos.quantity:>9}{pos.sellable_quantity:>10}"
f"{pos.t1_buy_quantity:>9}{pos.t2_buy_quantity:>9}"
f"{pos.t1_sell_quantity:>9}{pos.t2_sell_quantity:>9}{pos.cost_price:>12,.0f}"
)
if not positions:
print(" (không có vị thế)")
# Bước 2: PPMMR tài khoản cổ phiếu
print("\n--- PPMMR cổ phiếu ---")
eq = trading.portfolio.get_equity_ppmmr(ACCOUNT_NO)
if eq is None:
print(" (không có dữ liệu PPMMR)")
else:
print(f" Sức mua (purchasing_power) : {money(eq.purchasing_power)}")
print(f" Tỷ lệ margin : {eq.margin_ratio}")
print(f" Tổng tài sản / Tổng nợ : {money(eq.total_assets)} / {money(eq.total_debt)}")
print(f" Tiền mua T0/T1/T2 : {money(eq.buy_t0)} / {money(eq.buy_t1)} / {money(eq.buy_t2)}")
print(f" Tiền bán T0/T1/T2 : {money(eq.sell_t0)} / {money(eq.sell_t1)} / {money(eq.sell_t2)}")
# Bước 3: Tài khoản phái sinh (tùy chọn)
if DERIVATIVE_ACCOUNT_NO:
print(f"\n--- Phái sinh ({mask_account(DERIVATIVE_ACCOUNT_NO)}) ---")
all_pos = trading.portfolio.get_derivative_positions(DERIVATIVE_ACCOUNT_NO) or []
for group in all_pos:
for p in group.open_positions:
print(f" MỞ {p.symbol:<12} long={p.long} short={p.short} net={p.net} floating P/L={money(p.floating_pl)}")
for p in group.closed_positions:
print(f" ĐÓNG {p.symbol:<12} long={p.long} short={p.short} net={p.net} trading P/L={money(p.trading_pl)}")
dv = trading.portfolio.get_derivative_ppmmr(DERIVATIVE_ACCOUNT_NO)
if dv is not None:
print(f" Số dư: {money(dv.account_balance)} | Tổng P/L: {money(dv.total_pl)} | Rút được: {money(dv.withdrawable)}")
print(f" Ký quỹ yêu cầu SSI: {money(dv.margin_req_ssi)} | Tỷ lệ TK SSI: {dv.account_ratio_ssi}")
else:
print("\n(Bỏ qua phái sinh: chưa cấu hình derivative_account)")
# Bước 4: Max mua / bán
print(f"\n--- Max mua/bán {SYMBOL} ---")
master = next((m for m in data.market_data.get_master_data() if m.symbol == SYMBOL), None)
if master is not None and master.ref_price > 0:
price = Decimal(str(master.ref_price))
mx = trading.trading.get_max_buy_sell(ACCOUNT_NO, SYMBOL, price)
print(f" @ giá tham chiếu {price}: mua {mx.max_buy_quantity} | bán {mx.max_sell_quantity}"
f" | sức mua {mx.purchasing_power} | margin {mx.margin_ratio}")
mx_mkt = trading.trading.get_max_buy_sell_at_market_price(ACCOUNT_NO, SYMBOL)
print(f" @ giá thị trường: mua {mx_mkt.max_buy_quantity} | bán {mx_mkt.max_sell_quantity}")Sample Code — Async
Async dùng cùng luồng; chỉ khác AsyncAuth / AsyncData / AsyncTrading và await.
import asyncio
from decimal import Decimal
from ssi_sdk import AsyncAuth, AsyncData, AsyncTrading, Config
from auth_helper import ensure_auth_async
from helpers import mask_account, require_account
config = Config(...)
ACCOUNT_NO = "<your_account_no>"
SYMBOL = "SSI"
async def main():
require_account(ACCOUNT_NO)
async with AsyncAuth(config) as auth:
await ensure_auth_async(auth, otp="<your_otp>")
async with AsyncTrading(auth) as trading, AsyncData(auth) as data:
print(f"--- Vị thế cổ phiếu ({mask_account(ACCOUNT_NO)}) ---")
positions = await trading.portfolio.get_equity_positions(ACCOUNT_NO) or []
for pos in positions:
print(f"{pos.symbol:<8} {pos.quantity:>9} T1 mua={pos.t1_buy_quantity} T1 bán={pos.t1_sell_quantity}")
eq = await trading.portfolio.get_equity_ppmmr(ACCOUNT_NO)
if eq is not None:
print(f"Sức mua: {eq.purchasing_power} | margin: {eq.margin_ratio}")
master_data = await data.market_data.get_master_data()
master = next((m for m in master_data if m.symbol == SYMBOL), None)
if master is not None and master.ref_price > 0:
price = Decimal(str(master.ref_price))
mx = await trading.trading.get_max_buy_sell(ACCOUNT_NO, SYMBOL, price)
print(f"Max mua {mx.max_buy_quantity} | Max bán {mx.max_sell_quantity}")
asyncio.run(main())Các trường dễ nhầm
| Trường | Đơn vị | Ý nghĩa |
|---|---|---|
EquityPosition.t1_buy_quantity, t2_buy_quantity | Cổ phiếu | Khối lượng mua đang chờ về ở T1 / T2 |
EquityPosition.t1_sell_quantity, t2_sell_quantity | Cổ phiếu | Khối lượng bán đang chờ thanh toán ở T1 / T2 |
EquityPPMMR.buy_t0/t1/t2, sell_t0/t1/t2 | VND | Số tiền mua / bán chờ thanh toán |
purchasing_power (PPMMR, Max mua/bán) | VND | Sức mua. Thay cho purchase_power (deprecated) |
margin_ratio | Tỷ lệ | Tỷ lệ margin. Với tài khoản phái sinh, purchasing_power và margin_ratio là None chứ không phải 0 |
Lưu ý
- Vị thế cổ phiếu chỉ có T1/T2: vị thế cổ phiếu không có trường T0. Số tiền mua/bán T0 chỉ nằm trong PPMMR và số dư tiền.
Nonekhác danh sách rỗng: khi tài khoản không có vị thế (hoặc server trả204),get_equity_positionsvàget_derivative_positionstrảNone.get_equity_ppmmr,get_derivative_ppmmr,get_equity_balance,get_derivative_balancecũng có thể trảNone. Luôn kiểm tra trước khi duyệt hoặc đọc thuộc tính.get_open_derivative_positions/get_closed_derivative_positions: ở SDK 3.3.2 có thể raiseAttributeErrorkhi không có vị thế. Để an toàn, gọiget_derivative_positionsvà kiểm traNonenhư sample.- Dòng tổng hợp vị thế (
query_summary) không có ở REST, chỉ có qua Trading WebSocket, xem Trading WebSocket. - Max mua/bán theo giá:
get_max_buy_selltính theo giá bạn truyền vào (sample dùng giá tham chiếu);get_max_buy_sell_at_market_pricetính theo giá thị trường. Đây là số ước tính tại thời điểm gọi, không phải cam kết khớp lệnh. - Bảo mật dữ liệu: dùng
mask_accountkhi in số tài khoản, không log nguyên response, và không đưa số tài khoản hay số dư thật vào tài liệu. - Mô tả đầy đủ model và method: PortfolioService (Python). Số dư cơ bản và sức mua trước khi đặt lệnh: Số dư tài khoản.
- Cần xử lý lỗi khi gọi các API này (rate limit, lỗi server, validate): xem Xử lý lỗi & Rate limit.