Subscription.positions
Reads the latest positions that a private subscription maintains from pushed updates. Applies only to subscriptions whose channel is private.
positions()Examples
Subscribe to the private stream and print all positions once the snapshot is ready and on every position change.
javascript
function main() {
var acct = ctx.subscribe(exchange, {channel: "private"})
while (true) {
var ev = ctx.poll([], 1000)
if (!ev || ev.stream !== acct.id) {
continue
}
var ready = ev.kind === 35 && ev.prefillStatus === 1
if (ready || (ev.channel === "positions" && !ev.snapshot)) {
var positions = acct.positions()
if (positions === null) {
continue // Account snapshot not ready yet
}
positions.forEach(function(p) {
Log(p.symbol, p.posSide, "qty:", p.qty, "avgPx:", p.avgPx, "profit:", p.profit)
})
}
}
}
python
def main():
acct = ctx.subscribe(exchange, {"channel": "private"})
while True:
ev = ctx.poll([], 1000)
if ev is None or ev.stream != acct.id:
continue
ready = ev.kind == 35 and ev.prefillStatus == 1
if ready or (ev.channel == "positions" and not ev.snapshot):
positions = acct.positions()
if positions is None:
continue # Account snapshot not ready yet
for p in positions:
# Amount fields the exchange did not push are absent from the dict; read them with get
profit = p.get("profit")
Log(p.symbol, p.posSide, "qty:", p.qty, "avgPx:", p.avgPx, "profit:", profit)
rust
use sdk::ctx::EventExt;
fn main() {
let acct = ctx::subscribe(exchange, ctx::SubOpts::private()).unwrap();
loop {
match ctx::poll(&[], Some(1000)) {
ctx::Polled::Event(ev) if ev.stream() == acct.id() => {
if ev.channel() != "private" && ev.channel() != "positions" {
continue;
}
match acct.positions() {
Ok(Some(rows)) => {
for p in rows {
Log!(p.symbol, p.pos_side, "qty:", p.qty, "avgPx:", p.avg_px);
}
}
Ok(None) => {} // Account snapshot not ready yet
Err(e) => panic!("positions failed: {e}"),
}
}
ctx::Polled::Stopped => break,
_ => {}
}
}
}Returns
| Type | Description |
array / null value | An array of positions. Each element holds the current full state of one position slot, sorted by symbol and position slot. Returns a null value while the account snapshot is not ready yet. |
See Also
Remarks
Fields of each array element:
| Field | Type | Description |
|---|---|---|
| symbol | string | Symbol code, e.g. BTC_USDT.swap |
| side | string | buy (long) or sell (short) |
| posSide | string | Position slot: long or short in hedge (two-way) position mode, net in one-way position mode |
| qty | number | Position quantity, in the same unit as exchange.GetPositions |
| avgPx | number | Average entry price of the position |
| profit、margin、liqPx、markPx、leverage | number | Unrealized PnL, margin, liquidation price, mark price and leverage; a field is left out when the exchange does not push it |
| time | number | Exchange update time (milliseconds); left out when the exchange does not provide it |
Closed positions (quantity 0) do not appear in the array.
A null return means the state is unknown, not that there are no positions. After subscribing, data is available only once the event stream delivers the initialization status event with channel set to private and prefillStatus set to 1. If the most recent snapshot failed (for example, the snapshot request failed after a reconnect), it also returns a null value until the next snapshot is ready. Spot accounts have no positions and return an empty array once ready.
The docker updates this state from pushed position events (kind is 17), so you need to keep calling ctx.poll to receive events. Whether the real-time feed includes positions depends on the exchange. Some exchanges send a snapshot only at subscription time and after a reconnect; see each exchange's page for details. For reliable up-to-date positions you can still query exchange.GetPositions, which always calls the REST API and does not depend on pushed updates.
Calling it on a non-private subscription, or after the subscription object has been unsubscribed, throws an exception.
In Rust it is sub.positions() -> Result<Option<Vec<ctx::PositionRow>>, String>, where Ok(None) means the snapshot is not ready yet. The fields of PositionRow are symbol, side, pos_side, qty, avg_px, profit, margin, liq_px, mark_px, leverage and time (the amount and time fields are Option types). In Python it is sub.positions(), which returns a list of dicts (same fields as above, attribute access also supported) and returns None while the snapshot is not ready yet.