Skip to content

Get Positions (WebSocket)

Retrieve position information for the authenticated subaccount through the WebSocket connection with time-based filtering and pagination capabilities.

Request-Response vs Subscriptions

This method provides on-demand snapshots of current positions. For real-time updates when positions change, use SubAccount Updates Subscription.

MethodPurposeWhen to Use
getPositionsComprehensive position queryingInitial load, position history, filtered searches, reconciliation
User Updates SubscriptionReal-time notificationsLive PnL updates, liquidation alerts

Endpoint

ws.send() wss://api.synthetix.io/v1/ws/trade

Request

Request Format

{
  "id": "getpositions-1",
  "method": "post",
  "params": {
    "action": "getPositions",
    "subAccountId": "1867542890123456789",
    "status": "open",
    "symbol": "BTC-USDT",
    "startTime": 1704067200000,
    "endTime": 1704153600000,
    "limit": 50,
    "offset": 0,
    "sortBy": "createdAt",
    "sortOrder": "desc",
    "expiresAfter": 1704067500,
    "signature": {
      "v": 28,
      "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
      "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
    }
  }
}

Parameters

Request Parameters

ParameterTypeRequiredDescription
idstringYesClient-generated unique request identifier
methodstringYesMust be "post"
paramsobjectYesContains all parameters for the request

Params Object

ParameterTypeRequiredDescription
actionstringYesMust be "getPositions"
subAccountIdstringYesSubaccount identifier
statusstringNoFilter by position status: "open", "close", or "update"
symbolstringNoFilter by trading symbol (e.g., "BTC-USDT")
startTimeintegerNoStart timestamp in milliseconds (inclusive)
endTimeintegerNoEnd timestamp in milliseconds (inclusive)
fromTimeintegerNoDeprecated — use startTime instead
toTimeintegerNoDeprecated — use endTime instead
limitintegerNoMaximum number of positions to return (default: 50)
offsetintegerNoNumber of positions to skip for pagination (default: 0)
sortBystringNoField to sort results by
sortOrderstringNoSort direction: "asc" or "desc"
expiresAfterintegerNoOptional expiration timestamp in seconds
signatureobjectYesEIP-712 signature using SubAccountAction type
Important Notes:
  • This endpoint uses SubAccountAction EIP-712 type (no nonce required, only expiresAfter is optional)
  • Returns all positions for the authenticated subaccount
  • Use time range filters to limit results to specific time periods

EIP-712 Signature

const domain = {
  name: "Synthetix",
  version: "1",
  chainId: 1,
  verifyingContract: "0x0000000000000000000000000000000000000000"
};
 
const types = {
  SubAccountAction: [
    { name: "subAccountId", type: "uint256" },
    { name: "action", type: "string" },
    { name: "expiresAfter", type: "uint256" }
  ]
};
 
const message = {
  subAccountId: "1867542890123456789",
  action: "getPositions",
  expiresAfter: 0
};
 
const signature = await signer._signTypedData(domain, types, message);

Response Format

Response Structure

FieldTypeDescription
idstringClient-provided request identifier (legacy field, echoed from request)
requestIdstringClient-provided request identifier (current field, echoed from request)
statusintegerHTTP status code (200 for success, 400/500 for errors)
resultarrayArray of position objects matching filter criteria (omitted when error occurs)
errorobjectError details (only present when an error occurs)

Success Response Examples

Multiple Positions (Open and Closed)

{
  "id": "getpositions-1",
  "requestId": "getpositions-1",
  "status": 200,
  "result": [
    {
      "adlBucket": 3,
      "positionId": "pos_12345",
      "subAccountId": "1867542890123456789",
      "symbol": "ETH-USDT",
      "side": "long",
      "quantity": "2.5",
      "entryPrice": "2400.00",
      "realizedPnl": "50.00",
      "unrealizedPnl": "125.00",
      "usedMargin": "612.50",
      "maintenanceMargin": "306.25",
      "liquidationPrice": "2160.00",
      "status": "open",
      "netFunding": "15.50",
      "takeProfitOrders": [
        {
          "venueId": "2026771048053084163",
          "clientId": "cli-tp_001"
        }
      ],
      "takeProfitOrderIds": ["2026771048053084163"],
      "stopLossOrders": [
        {
          "venueId": "2026771048053084165",
          "clientId": "cli-sl_002"
        }
      ],
      "stopLossOrderIds": ["2026771048053084165"],
      "createdAt": 1735680000000,
      "updatedAt": 1735689900000
    },
    {
      "adlBucket": 1,
      "positionId": "pos_12346",
      "subAccountId": "1867542890123456789",
      "symbol": "BTC-USDT",
      "side": "short",
      "quantity": "0.1",
      "entryPrice": "42000.00",
      "realizedPnl": "200.00",
      "unrealizedPnl": "0.00",
      "usedMargin": "0.00",
      "maintenanceMargin": "0.00",
      "liquidationPrice": "0.00",
      "status": "close",
      "netFunding": "-8.50",
      "takeProfitOrders": [],
      "takeProfitOrderIds": [],
      "stopLossOrders": [],
      "stopLossOrderIds": [],
      "createdAt": 1735685000000,
      "updatedAt": 1735689800000
    }
  ]
}

Open Positions Only

{
  "id": "getpositions-2",
  "requestId": "getpositions-2",
  "status": 200,
  "result": [
    {
      "adlBucket": 2,
      "positionId": "pos_12347",
      "subAccountId": "1867542890123456789",
      "symbol": "BTC-USDT",
      "side": "long",
      "quantity": "0.2",
      "entryPrice": "43500.00",
      "realizedPnl": "0.00",
      "unrealizedPnl": "100.00",
      "usedMargin": "1760.00",
      "maintenanceMargin": "880.00",
      "liquidationPrice": "41000.00",
      "status": "open",
      "netFunding": "2.25",
      "takeProfitOrders": [
        {
          "venueId": "2026771048053084166",
          "clientId": "cli-tp_003"
        }
      ],
      "takeProfitOrderIds": ["2026771048053084166"],
      "stopLossOrders": [
        {
          "venueId": "2026771048053084167",
          "clientId": "cli-sl_004"
        }
      ],
      "stopLossOrderIds": ["2026771048053084167"],
      "createdAt": 1735689000000,
      "updatedAt": 1735689900000
    }
  ]
}

Empty Result Set

{
  "id": "getpositions-3",
  "requestId": "getpositions-3",
  "status": 200,
  "result": []
}

Position Status Types

StatusDescriptionCharacteristics
openActive positionHas unrealized PnL, margin requirements, liquidation price
closeClosed positionOnly realized PnL, no margin requirements
updatePosition being updatedTransitional state during modifications

Error Response

{
  "id": "getpositions-1",
  "requestId": "getpositions-1",
  "status": 400,
  "error": {
    "code": 400,
    "message": "Invalid time range: startTime must be less than or equal to endTime"
  }
}

Implementation Example

async function getPositions(ws, subAccountId, signature, options = {}) {
  const {
    status = null,
    symbol = null,
    startTime = null,
    endTime = null,
    limit = 50,
    offset = 0,
    sortBy = null,
    sortOrder = null,
    expiresAfter = 0
  } = options;
 
  const requestId = `getpositions-${Date.now()}`;
 
  const params = {
    action: "getPositions",
    subAccountId,
    limit,
    offset,
    expiresAfter,
    signature
  };
 
  if (status) params.status = status;
  if (symbol) params.symbol = symbol;
  if (startTime) params.startTime = startTime;
  if (endTime) params.endTime = endTime;
  if (sortBy) params.sortBy = sortBy;
  if (sortOrder) params.sortOrder = sortOrder;
 
  const request = { id: requestId, method: "post", params };
 
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => reject(new Error("Request timeout")), 10000);
 
    const handler = (event) => {
      const response = JSON.parse(event.data);
      if (response.id === requestId || response.requestId === requestId) {
        clearTimeout(timeout);
        ws.removeEventListener("message", handler);
        if (response.error) {
          reject(new Error(response.error.message));
        } else {
          resolve(response.result); // Array of position objects
        }
      }
    };
 
    ws.addEventListener("message", handler);
    ws.send(JSON.stringify(request));
  });
}
 
// Usage: get all open positions
const openPositions = await getPositions(ws, subAccountId, signature, {
  status: "open"
});
 
// Usage: get positions for a specific market
const btcPositions = await getPositions(ws, subAccountId, signature, {
  symbol: "BTC-USDT",
  startTime: Date.now() - 7 * 24 * 60 * 60 * 1000 // last 7 days
});
 
// Display open positions
for (const pos of openPositions) {
  console.log(`${pos.symbol} ${pos.side}: qty=${pos.quantity}, unrealizedPnl=${pos.unrealizedPnl}`);
}

Implementation Notes

  • This endpoint uses SubAccountAction EIP-712 type (no nonce required)
  • Time range filters must specify valid Unix millisecond timestamps
  • Use pagination (limit/offset) for large result sets

Position Object Structure

class PositionMonitor {
  constructor(query, subAccountId) {
    this.query = query;
    this.subAccountId = subAccountId;
    this.positions = new Map();
    this.callbacks = new Map();
    this.monitoring = false;
    this.riskAlerts = new Map();
  }
 
  async startMonitoring(interval = 1000) {
    this.monitoring = true;
 
    // Initial fetch
    await this.refreshPositions();
 
    // Set up periodic refresh
    this.monitoringInterval = setInterval(async () => {
      if (this.monitoring) {
        await this.refreshPositions();
      }
    }, interval);
  }
 
  stopMonitoring() {
    this.monitoring = false;
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval);
    }
  }
 
  async refreshPositions() {
    try {
      const result = await this.query.getOpenPositions({
        subAccountId: this.subAccountId
      });
 
      const newPositions = new Map();
      const changes = [];
 
      // Process current positions
      for (const position of result.positions) {
        const symbol = position.symbol;
        const previousPosition = this.positions.get(symbol);
 
        newPositions.set(symbol, position);
 
        if (!previousPosition) {
          // New position
          changes.push({
            type: 'opened',
            symbol,
            position
          });
        } else if (this.hasPositionChanged(previousPosition, position)) {
          // Position updated
          changes.push({
            type: 'updated',
            symbol,
            previousPosition,
            position,
            changes: this.getPositionChanges(previousPosition, position)
          });
        }
      }
 
      // Find closed positions
      for (const [symbol, position] of this.positions) {
        if (!newPositions.has(symbol)) {
          changes.push({
            type: 'closed',
            symbol,
            position
          });
        }
      }
 
      // Update stored positions
      this.positions = newPositions;
 
      // Check risk alerts
      this.checkRiskAlerts(result);
 
      // Notify callbacks of changes
      for (const change of changes) {
        this.notifyCallbacks(change);
      }
 
    } catch (error) {
      console.error('Position monitoring refresh failed:', error);
    }
  }
 
  hasPositionChanged(previous, current) {
    return previous.size !== current.size ||
           previous.unrealizedPnl !== current.unrealizedPnl ||
           previous.markPrice !== current.markPrice ||
           previous.marginRatio !== current.marginRatio;
  }
 
  getPositionChanges(previous, current) {
    const changes = [];
 
    if (previous.size !== current.size) {
      changes.push({
        field: 'size',
        from: previous.size,
        to: current.size,
        delta: parseFloat(current.size) - parseFloat(previous.size)
      });
    }
 
    if (previous.unrealizedPnl !== current.unrealizedPnl) {
      changes.push({
        field: 'unrealizedPnl',
        from: previous.unrealizedPnl,
        to: current.unrealizedPnl,
        delta: parseFloat(current.unrealizedPnl) - parseFloat(previous.unrealizedPnl)
      });
    }
 
    if (previous.markPrice !== current.markPrice) {
      changes.push({
        field: 'markPrice',
        from: previous.markPrice,
        to: current.markPrice,
        delta: parseFloat(current.markPrice) - parseFloat(previous.markPrice)
      });
    }
 
    return changes;
  }
 
  onPositionChange(callback) {
    const id = Date.now() + Math.random();
    this.callbacks.set(id, callback);
    return () => this.callbacks.delete(id);
  }
 
  notifyCallbacks(change) {
    for (const callback of this.callbacks.values()) {
      try {
        callback(change);
      } catch (error) {
        console.error('Position change callback error:', error);
      }
    }
  }
 
  // Risk management
  setRiskAlert(symbol, alertConfig) {
    this.riskAlerts.set(symbol, alertConfig);
  }
 
  checkRiskAlerts(result) {
    for (const position of result.positions) {
      const alert = this.riskAlerts.get(position.symbol);
      if (!alert) continue;
 
      const marginRatio = parseFloat(position.marginRatio);
      const pnlPct = parseFloat(position.unrealizedPnl) / parseFloat(position.margin);
 
      // Check margin ratio alert
      if (alert.marginRatioThreshold && marginRatio >= alert.marginRatioThreshold) {
        this.triggerAlert('marginRatio', position, { marginRatio, threshold: alert.marginRatioThreshold });
      }
 
      // Check PnL percentage alert
      if (alert.pnlThreshold && pnlPct <= alert.pnlThreshold) {
        this.triggerAlert('pnlThreshold', position, { pnlPct, threshold: alert.pnlThreshold });
      }
 
      // Check liquidation distance
      const markPrice = parseFloat(position.markPrice);
      const liqPrice = parseFloat(position.liquidationPrice);
      const liqDistance = Math.abs(markPrice - liqPrice) / markPrice;
 
      if (alert.liquidationDistance && liqDistance <= alert.liquidationDistance) {
        this.triggerAlert('liquidationRisk', position, { liqDistance, threshold: alert.liquidationDistance });
      }
    }
  }
 
  triggerAlert(type, position, data) {
    const alert = {
      type,
      symbol: position.symbol,
      position,
      data,
      timestamp: Date.now()
    };
 
    console.warn(`🚨 RISK ALERT [${type}] for ${position.symbol}:`, data);
 
    // Notify callbacks with alert
    this.notifyCallbacks({
      type: 'alert',
      alert
    });
  }
}
 
// Usage
const monitor = new PositionMonitor(query, subAccountId);
 
// Set up risk alerts
monitor.setRiskAlert('BTC-USDT', {
  marginRatioThreshold: 0.8,    // 80% margin ratio
  pnlThreshold: -0.1,           // -10% PnL
  liquidationDistance: 0.05     // 5% from liquidation
});
 
// Listen for position changes
const unsubscribe = monitor.onPositionChange((change) => {
  switch (change.type) {
    case 'opened':
      console.log('Position opened:', change.position);
      break;
    case 'updated':
      console.log('Position updated:', change.symbol);
      for (const fieldChange of change.changes) {
        console.log(`  ${fieldChange.field}: ${fieldChange.from} → ${fieldChange.to}`);
      }
      break;
    case 'closed':
      console.log('Position closed:', change.symbol);
      break;
    case 'alert':
      console.log('Risk alert:', change.alert);
      break;
  }
});
 
await monitor.startMonitoring();

Portfolio Analytics

class PositionAnalytics {
  constructor(query, subAccountId) {
    this.query = query;
    this.subAccountId = subAccountId;
  }
 
  async getPortfolioSummary() {
    const result = await this.query.getOpenPositions({
      subAccountId: this.subAccountId
    });
 
    const positions = result.positions;
    const summary = {
      totalPositions: positions.length,
      longPositions: 0,
      shortPositions: 0,
      totalNotional: 0,
      totalMargin: 0,
      totalUnrealizedPnl: 0,
      avgLeverage: 0,
      marginUtilization: 0,
      riskMetrics: {
        maxSinglePositionRisk: 0,
        correlationRisk: 'low', // simplified
        liquidationRisk: 'low'
      }
    };
 
    let totalLeverageWeighted = 0;
    let maxPositionSize = 0;
    let closeToLiquidation = 0;
 
    for (const position of positions) {
      if (position.side === 'long') {
        summary.longPositions++;
      } else {
        summary.shortPositions++;
      }
 
      const notional = Math.abs(parseFloat(position.size)) * parseFloat(position.markPrice);
      const margin = parseFloat(position.margin);
      const pnl = parseFloat(position.unrealizedPnl);
      const leverage = parseFloat(position.leverage);
 
      summary.totalNotional += notional;
      summary.totalMargin += margin;
      summary.totalUnrealizedPnl += pnl;
      totalLeverageWeighted += leverage * margin;
 
      // Risk metrics
      if (notional > maxPositionSize) {
        maxPositionSize = notional;
      }
 
      // Check liquidation risk
      const markPrice = parseFloat(position.markPrice);
      const liqPrice = parseFloat(position.liquidationPrice);
      const liqDistance = Math.abs(markPrice - liqPrice) / markPrice;
 
      if (liqDistance < 0.1) { // Within 10% of liquidation
        closeToLiquidation++;
      }
    }
 
    if (summary.totalMargin > 0) {
      summary.avgLeverage = totalLeverageWeighted / summary.totalMargin;
      summary.marginUtilization = summary.totalMargin / parseFloat(result.summary.accountEquity);
    }
 
    summary.riskMetrics.maxSinglePositionRisk = maxPositionSize / summary.totalNotional;
    summary.riskMetrics.liquidationRisk = closeToLiquidation > 0 ? 'high' : 'low';
 
    return summary;
  }
 
  async getPositionMetrics() {
    const result = await this.query.getOpenPositions({
      subAccountId: this.subAccountId
    });
 
    return result.positions.map(position => {
      const size = parseFloat(position.size);
      const markPrice = parseFloat(position.markPrice);
      const entryPrice = parseFloat(position.entryPrice);
      const margin = parseFloat(position.margin);
      const unrealizedPnl = parseFloat(position.unrealizedPnl);
      const liqPrice = parseFloat(position.liquidationPrice);
 
      const notional = Math.abs(size) * markPrice;
      const pnlPct = (unrealizedPnl / margin) * 100;
      const priceMovePct = ((markPrice - entryPrice) / entryPrice) * 100;
      const liqDistance = Math.abs(markPrice - liqPrice) / markPrice * 100;
 
      return {
        symbol: position.symbol,
        side: position.side,
        size,
        notional,
        pnlPct,
        priceMovePct,
        liquidationDistance: liqDistance,
        marginRatio: parseFloat(position.marginRatio) * 100,
        leverage: parseFloat(position.leverage),
        riskLevel: this.calculateRiskLevel(position)
      };
    });
  }
 
  calculateRiskLevel(position) {
    const marginRatio = parseFloat(position.marginRatio);
    const markPrice = parseFloat(position.markPrice);
    const liqPrice = parseFloat(position.liquidationPrice);
    const liqDistance = Math.abs(markPrice - liqPrice) / markPrice;
 
    if (marginRatio > 0.8 || liqDistance < 0.05) {
      return 'high';
    } else if (marginRatio > 0.6 || liqDistance < 0.1) {
      return 'medium';
    } else {
      return 'low';
    }
  }
 
  async getDiversificationMetrics() {
    const result = await this.query.getOpenPositions({
      subAccountId: this.subAccountId
    });
 
    const assetExposure = new Map();
    let totalNotional = 0;
 
    for (const position of result.positions) {
      const asset = position.symbol.split('-')[0]; // Extract base asset
      const notional = Math.abs(parseFloat(position.size)) * parseFloat(position.markPrice);
 
      totalNotional += notional;
 
      const currentExposure = assetExposure.get(asset) || 0;
      assetExposure.set(asset, currentExposure + notional);
    }
 
    // Calculate concentration ratios
    const exposureArray = Array.from(assetExposure.values()).sort((a, b) => b - a);
    const concentrationRatios = {
      top1: totalNotional > 0 ? exposureArray[0] / totalNotional : 0,
      top3: totalNotional > 0 ? exposureArray.slice(0, 3).reduce((sum, val) => sum + val, 0) / totalNotional : 0,
      herfindahl: totalNotional > 0 ? exposureArray.reduce((sum, val) => sum + Math.pow(val / totalNotional, 2), 0) : 0
    };
 
    return {
      assetExposure: Object.fromEntries(assetExposure),
      concentrationRatios,
      diversificationScore: 1 - concentrationRatios.herfindahl // Higher = more diversified
    };
  }
}

Position Risk Manager

class PositionRiskManager {
  constructor(query, subAccountId) {
    this.query = query;
    this.subAccountId = subAccountId;
    this.riskLimits = new Map();
  }
 
  setRiskLimits(limits) {
    // limits = {
    //   maxPositionSize: 100000,
    //   maxLeverage: 20,
    //   maxMarginUtilization: 0.8,
    //   maxSingleAssetExposure: 0.3
    // }
    this.riskLimits = new Map(Object.entries(limits));
  }
 
  async checkRiskCompliance() {
    const result = await this.query.getOpenPositions({
      subAccountId: this.subAccountId
    });
 
    const violations = [];
    const warnings = [];
 
    // Check individual position limits
    for (const position of result.positions) {
      const notional = Math.abs(parseFloat(position.size)) * parseFloat(position.markPrice);
      const leverage = parseFloat(position.leverage);
      const marginRatio = parseFloat(position.marginRatio);
 
      // Max position size
      const maxSize = this.riskLimits.get('maxPositionSize');
      if (maxSize && notional > maxSize) {
        violations.push({
          type: 'positionSize',
          symbol: position.symbol,
          value: notional,
          limit: maxSize,
          severity: 'high'
        });
      }
 
      // Max leverage
      const maxLev = this.riskLimits.get('maxLeverage');
      if (maxLev && leverage > maxLev) {
        violations.push({
          type: 'leverage',
          symbol: position.symbol,
          value: leverage,
          limit: maxLev,
          severity: 'medium'
        });
      }
 
      // Margin ratio warning
      if (marginRatio > 0.7) {
        warnings.push({
          type: 'marginRatio',
          symbol: position.symbol,
          value: marginRatio,
          threshold: 0.7,
          severity: marginRatio > 0.8 ? 'high' : 'medium'
        });
      }
    }
 
    // Portfolio-level checks
    const portfolioMetrics = await this.calculatePortfolioRisk(result);
 
    const maxMarginUtil = this.riskLimits.get('maxMarginUtilization');
    if (maxMarginUtil && portfolioMetrics.marginUtilization > maxMarginUtil) {
      violations.push({
        type: 'marginUtilization',
        value: portfolioMetrics.marginUtilization,
        limit: maxMarginUtil,
        severity: 'high'
      });
    }
 
    return {
      compliant: violations.length === 0,
      violations,
      warnings,
      portfolioMetrics
    };
  }
 
  async calculatePortfolioRisk(result) {
    const positions = result.positions;
    const summary = result.summary;
 
    const totalNotional = positions.reduce((sum, pos) => {
      return sum + Math.abs(parseFloat(pos.size)) * parseFloat(pos.markPrice);
    }, 0);
 
    const marginUtilization = parseFloat(summary.totalMargin) / parseFloat(summary.accountEquity);
 
    return {
      totalNotional,
      marginUtilization,
      totalUnrealizedPnl: parseFloat(summary.totalUnrealizedPnl),
      accountEquity: parseFloat(summary.accountEquity),
      freeCollateral: parseFloat(summary.freeCollateral)
    };
  }
 
  async generateRiskReport() {
    const compliance = await this.checkRiskCompliance();
    const analytics = new PositionAnalytics(this.query, this.subAccountId);
    const diversification = await analytics.getDiversificationMetrics();
    const positionMetrics = await analytics.getPositionMetrics();
 
    return {
      timestamp: Date.now(),
      compliance,
      diversification,
      positions: positionMetrics,
      riskScore: this.calculateOverallRiskScore(compliance, diversification, positionMetrics)
    };
  }
 
  calculateOverallRiskScore(compliance, diversification, positions) {
    let score = 100; // Start with perfect score
 
    // Deduct for violations
    score -= compliance.violations.length * 15;
    score -= compliance.warnings.length * 5;
 
    // Deduct for concentration
    score -= (1 - diversification.diversificationScore) * 20;
 
    // Deduct for high-risk positions
    const highRiskPositions = positions.filter(p => p.riskLevel === 'high').length;
    score -= highRiskPositions * 10;
 
    return Math.max(0, Math.min(100, score));
  }
}

Position Object Structure

Position Fields

Important: All numeric fields in position objects are represented as strings to ensure decimal precision. Timestamps are integers containing Unix milliseconds.

FieldTypeDescription
adlBucketintegerADL (auto-deleveraging) priority bucket (1–5, where 5 = highest risk)
positionIdstringUnique position identifier
subAccountIdstringSubaccount identifier
symbolstringMarket symbol (e.g., "BTC-USDT")
sidestringPosition side: "long" or "short"
quantitystringPosition size (always positive)
entryPricestringVolume-weighted average entry price
realizedPnlstringRealized profit/loss
unrealizedPnlstringCurrent unrealized profit/loss
usedMarginstringMargin currently allocated to position
maintenanceMarginstringMinimum margin for position maintenance
liquidationPricestringPrice at which position will be liquidated
statusstringPosition status: "open", "close", or "update"
netFundingstringNet funding payments received/paid
takeProfitOrdersobject[]Canonical take-profit order identifiers (venueId, optional clientId)
takeProfitOrderIdsstring[]Deprecated take-profit venue identifiers
stopLossOrdersobject[]Canonical stop-loss order identifiers (venueId, optional clientId)
stopLossOrderIdsstring[]Deprecated stop-loss venue identifiers
createdAtintegerPosition creation timestamp (Unix milliseconds)
updatedAtintegerLast update timestamp (Unix milliseconds)

Migration Note: Use takeProfitOrders[*].venueId and stopLossOrders[*].venueId as canonical references. takeProfitOrderIds and stopLossOrderIds are deprecated compatibility fields.

Error CodeDescriptionRetryable
UNAUTHORIZEDEIP-712 signature validation failedNo
VALIDATION_ERRORRequest validation failedNo
MISSING_REQUIRED_FIELDRequired field is missingNo
INVALID_FORMATField format is invalidNo
INVALID_VALUEInvalid parameter valueNo
RATE_LIMIT_EXCEEDEDToo many requests in time windowYes
INSUFFICIENT_MARGINNot enough margin for tradeNo
ORDER_NOT_FOUNDOrder does not existNo
OPERATION_TIMEOUTOperation timed outYes

Next Steps