Skip to content

Update Leverage (WebSocket)

Adjust leverage settings for trading markets through the WebSocket connection for rapid risk management and position scaling.

Endpoint

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

Request

Request Format

{
  "id": "leverage-1",
  "method": "post",
  "params": {
    "action": "updateLeverage",
    "symbol": "BTC-USDT",
    "leverage": "20",
    "isCross": true,
    "subAccountId": "1867542890123456789",
    "nonce": 1704067200000,
    "expiresAfter": 1704067300000,
    "signature": {
      "v": 28,
      "r": "0x...",
      "s": "0x..."
    }
  }
}

Parameters

Request Parameters

ParameterTypeRequiredDescription
idstringYesClient-generated unique request identifier
methodstringYesMust be "post"
paramsobjectYesContains the trading request payload with all fields flattened

Params Object Fields

ParameterTypeRequiredDescription
params.actionstringYesMust be "updateLeverage"
params.symbolstringYesMarket symbol (e.g., "BTC-USDT")
params.leveragestringYesDesired leverage multiplier
params.isCrossbooleanYestrue for cross margin, false for isolated
params.subAccountIdstringYesSubaccount identifier
params.nonceintegerYesIncrementing nonce (Unix ms timestamp as number)
params.expiresAfterintegerNoOptional expiration timestamp (Unix ms as number)
params.signatureobjectYesEIP-712 signature

Response Format

Success Response

{
  "id": "leverage-1",
  "status": 200,
  "result": {
    "symbol": "BTC-USDT",
    "previousLeverage": "10",
    "newLeverage": "20",
    "isCross": true,
    "maxLeverage": "50",
    "marginRequirementChange": "+500.00",
    "timestamp": 1704067200500
  }
}

Error Response

{
  "id": "leverage-1",
  "status": 400,
  "result": null,
  "error": {
    "code": 400,
    "message": "Leverage exceeds maximum allowed"
  }
}

Implementation Example

class LeverageManager {
  constructor(ws, signer) {
    this.ws = ws;
    this.signer = signer;
    this.requestId = 0;
    this.leverageSettings = new Map();
  }
 
  async updateLeverage(subAccountId, symbol, leverage, isCross = true) {
    // Generate nonce
    const nonce = Date.now();
 
    // Create leverage signature
    const signature = await this.signUpdateLeverage(subAccountId, symbol, leverage, isCross, nonce);
 
    // Build request
    const request = {
      type: "request",
      requestId: `update-leverage-${++this.requestId}`,
      method: "updateLeverage",
      params: {
        action: {
          type: "updateLeverage",
          symbol,
          leverage: leverage.toString(),
          isCross
        },
        subAccountId,
        nonce,
        signature,
        expiresAfter: nonce + 30000 // 30 second expiry
      }
    };
 
    // Send and wait for response
    const result = await this.sendRequest(request);
 
    // Update local tracking
    this.leverageSettings.set(symbol, {
      leverage: parseFloat(result.newLeverage),
      isCross,
      lastUpdated: Date.now()
    });
 
    return result;
  }
 
  async signUpdateLeverage(subAccountId, symbol, leverage, isCross, nonce) {
    const domain = {
      name: "Synthetix",
      version: "1",
      chainId: 1,
      verifyingContract: "0x0000000000000000000000000000000000000000"
    };
 
    const types = {
      UpdateLeverageRequest: [
        { name: "subAccountId", type: "uint64" },
        { name: "symbol", type: "string" },
        { name: "leverage", type: "string" },
        { name: "isCross", type: "bool" },
        { name: "nonce", type: "uint256" },
        { name: "expiresAfter", type: "uint256" }
      ]
    };
 
    const message = {
      subAccountId: BigInt(subAccountId),
      symbol,
      leverage: leverage.toString(),
      isCross,
      nonce: BigInt(nonce),
      expiresAfter: BigInt(nonce + 30000)
    };
 
    const signature = await this.signer._signTypedData(domain, types, message);
    return ethers.utils.splitSignature(signature);
  }
 
  sendRequest(request) {
    return new Promise((resolve, reject) => {
      this.pendingRequests.set(request.requestId, { resolve, reject });
      this.ws.send(JSON.stringify(request));
 
      setTimeout(() => {
        if (this.pendingRequests.has(request.requestId)) {
          this.pendingRequests.delete(request.requestId);
          reject(new Error('Request timeout'));
        }
      }, 15000); // 15 second timeout
    });
  }
 
  getCurrentLeverage(symbol) {
    return this.leverageSettings.get(symbol);
  }
 
  getAllLeverageSettings() {
    return Object.fromEntries(this.leverageSettings);
  }
}
 
// Usage Examples
async function increaseRiskTolerance(manager, subAccountId, symbol) {
  try {
    const current = manager.getCurrentLeverage(symbol);
    const currentLeverage = current ? current.leverage : 10;
    const newLeverage = Math.min(currentLeverage * 1.5, 50); // Max 50x
 
    const result = await manager.updateLeverage(subAccountId, symbol, newLeverage, true);
    console.log(`Increased ${symbol} leverage from ${result.previousLeverage}x to ${result.newLeverage}x`);
 
    return result;
  } catch (error) {
    console.error(`Failed to increase leverage for ${symbol}:`, error);
    throw error;
  }
}
 
async function setConservativeLeverage(manager, subAccountId, symbols) {
  const results = [];
 
  for (const symbol of symbols) {
    try {
      const result = await manager.updateLeverage(subAccountId, symbol, 5, true); // Conservative 5x
      results.push(result);
      console.log(`Set conservative leverage for ${symbol}: 5x`);
    } catch (error) {
      console.error(`Failed to set conservative leverage for ${symbol}:`, error);
      results.push({ symbol, error: error.message });
    }
  }
 
  return results;
}

Important Behavior

How Leverage Settings Work

When you update leverage for a market:

  • The new leverage setting applies to all future positions you open in that market
  • Existing positions retain their original leverage - they are NOT affected
  • The setting persists until you explicitly change it again
Example:
1. You have an open BTC position at 10x leverage
2. You call updateLeverage(subAccountId, "BTC-USDT", 25, true)
3. Your existing position REMAINS at 10x
4. Any NEW BTC positions will open at 25x

Can You Change Leverage on Existing Positions?

No, you cannot directly change leverage on existing positions. However:

  • For Isolated Positions: Use updateIsolatedMargin (REST API only) to add/remove margin, which changes effective leverage
  • For Cross Margin Positions: Leverage is fixed at position entry

Error Handling

Common Errors

Error CodeMessageDescription
-32000Invalid leverageLeverage exceeds maximum allowed for symbol
-32001Position existsCannot change leverage with open positions
-32002Invalid symbolMarket symbol does not exist
-32003Margin insufficientNot enough margin for new leverage setting

Signing

All trading methods are signed using EIP-712. Each successful trading request will contain:

  • A piece of structured data that includes the sender address
  • A signature of the hash of that structured data, signed by the sender

For detailed information on EIP-712 signing, see EIP-712 Signing.

Nonce Management

The nonce system prevents replay attacks and ensures order uniqueness:

  • Use current timestamp in milliseconds as nonce
  • Each nonce must be greater than the previous one
  • Recommended: Use Date.now() or equivalent
  • If nonce conflicts occur, increment by 1 and retry
ErrorDescription
Invalid signatureEIP-712 signature validation failed
Invalid market symbolMarket symbol not recognized
Nonce already usedNonce must be greater than previous value
Rate limit exceededToo many requests in time window
Request expiredexpiresAfter timestamp has passed

Next Steps