npm package: @pear-protocol/symm-core
Description: TypeScript SDK for Symm Engine API - trading positions, TP/SL orders, and real-time WebSocket streams
Author: Symm
Latest version: 0.1.12

# @pear-protocol/symm-core

TypeScript SDK for Symm Engine API - trading positions, TP/SL config, and real-time WebSocket streams.

## Features

- ✅ **TypeScript** - Full type safety with comprehensive type definitions
- ✅ **REST API Client** - Complete API coverage for positions, orders, and TP/SL config
- ✅ **WebSocket Client** - Real-time data streaming with automatic reconnection
- ✅ **Multi-chain** - Support for Arbitrum and Base networks
- ✅ **Tree-shakeable** - Modular exports for optimal bundle size
- ✅ **Zero dependencies** - Only peer dependency on `viem` for type compatibility

## Installation

```bash
# npm
npm install @pear-protocol/symm-core viem

# yarn
yarn add @pear-protocol/symm-core viem

# pnpm
pnpm add @pear-protocol/symm-core viem

# bun
bun add @pear-protocol/symm-core viem
```

## Quick Start

### Basic Usage

```typescript
import { createSymmSDK } from '@pear-protocol/symm-core';

// Create SDK instance
const { client, ws } = createSymmSDK({
  apiUrl: 'https://api.symm.io',
  wsUrl: 'wss://ws.symm.io',
  defaultChainId: 42161 // Arbitrum
});

// Open a basket (single, pair, or multi-leg)
const result = await client.openBasketPosition({
  accountAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  authToken: 'your-siwe-token',
  longPositions: [
    { symbol: 'BTC', quantity: '1.0', leverage: 10 }
  ],
  slippage: 'auto'
});

console.log('Position opened:', result.data);
```

### Response Shape (camelCase by default)

The SDK transforms responses to camelCase by default. To opt out and keep raw snake_case:

```typescript
const sdk = createSymmSDK({
  apiUrl: 'https://api.symm.io',
  responseAdapter: false
});
```

Raw types are also exported:
`RawPosition`, `RawClosePositionResponse`, `RawPendingInstantOpenItem`, `RawAssetLeg`.

### Auth Provider + Retry

```typescript
const sdk = createSymmSDK({
  apiUrl: 'https://api.symm.io',
  authProvider: async () => 'your-siwe-token',
  retry: { retries: 3, backoffMs: 250 }
});
```

### WebSocket Streaming

```typescript
import { createSymmWebSocket } from '@pear-protocol/symm-core';

const ws = createSymmWebSocket({
  url: 'wss://ws.symm.io',
  autoReconnect: true
});

// Connect
await ws.connect();

// Subscribe to position updates
ws.subscribeToPositions(
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  42161, // Arbitrum
  (data) => {
    console.log('Position update:', data);
  }
);

// Subscribe to open orders
ws.subscribeToOpenOrders(
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  42161,
  (data) => {
    console.log('Open orders:', data);
  }
);
```

## API Reference

### REST Client

#### Position Management

```typescript
// Open basket position
await client.openBasketPosition(request);

// Close position
await client.closePosition({
  positionId: 'pos_123',
  quantityToClose: '1.0',
  authToken: 'token'
});

// Cancel close
await client.cancelClose({
  quoteId: '123',
  authToken: 'token'
});

// Advanced: open basket with polling/status updates
await sdk.positions.openBasket(request, {
  pollPending: true,
  onStatus: (status) => console.log(status),
  onLegUpdate: (leg) => console.log(leg),
});

// Advanced: close with polling/status updates
await sdk.positions.close(
  { positionId: 'pos_123', authToken: 'token' },
  {
    pollPending: true,
    accountAddress: '0x...',
    chainId: 42161,
    onStatus: (status) => console.log(status),
  },
);

// Get open positions
await client.getOpenPositions({
  // Use accountAddress for a specific subaccount, or address for a main wallet
  accountAddress: '0x...',
  chainId: 42161
});

// Get positions (on-chain)
await client.getPositions({
  address: '0x...'
});

// Get trade history
await client.getTradeHistory({
  // Use accountAddress (subaccount) or mainAddress (all subaccounts)
  accountAddress: '0x...',
  limit: 100,
  includeQuoteTimeline: true
});

// Get user stats (positions analytics)
await client.getPositionStats('0x...');

// Get quote details
await client.getQuote(quoteId);

// Get pending IDs
await client.getPendingIds({
  accountAddress: '0x...'
});

// Cancel pending open
await client.cancelOpen({
  quoteId: '123',
  authToken: 'token'
});

// Get pending instant opens
await client.getPendingInstantOpens({
  accountAddress: '0x...',
  authToken: 'token'
});
```

#### TP/SL Management

```typescript
// Set TP/SL config
await client.setTpsl({
  positionId: 'pos_123',
  takeProfit: { triggerType: 'price', triggerValue: '55000' },
  stopLoss: { triggerType: 'price', triggerValue: '47500' },
  authToken: 'token'
});

// Cancel TP/SL config
await client.cancelTpsl({
  positionId: 'pos_123',
  chainId: 42161,
  authToken: 'token'
});

// Get TP/SL orders
await client.getTpslOrders({
  // Use address (subaccount) or mainAddress (all subaccounts)
  address: '0x...',
  status: 'active'
});

// Cancel TP/SL config
await client.cancelTpsl({
  positionId: 'pos_123',
  type: 'take_profit',
  authToken: 'token'
});
```

#### Orders

```typescript
// Get orders (address or mainAddress)
await client.getOrders({ address: '0x...', status: 'open' });

// TWAP orders
await client.getTwapOrders({ address: '0x...' });
await client.getTwapOrder('order_123');
await client.cancelTwapOrder('order_123');

// Trigger config
await client.getTriggerConfig('order_123');
await client.upsertTriggerConfig('order_123', { config: { /* trigger config */ } });
await client.clearTriggerConfig('order_123');
```

#### Accounts

```typescript
await client.getAccounts({ chainId: 42161, userAddress: '0x...' });
await client.getAccountsLength({ chainId: 42161, userAddress: '0x...' });
await client.getBalanceInfo({ chainId: 42161, userAddress: '0x...' });
await client.getAccountSummary({ chainId: 42161, userAddress: '0x...' });
await client.getAccountData({ chainId: 42161, address: '0x...' });
await client.getAccountsWithPositions({ chainId: 42161, userAddress: '0x...' });
```

#### Markets

```typescript
// Market groups (basket pairs with ratios, volume, funding)
await client.getMarkets({ active: 'true', sort: 'volume:desc', pageSize: '50' });
await client.getMarkets({ searchText: 'BTC', netFunding: 'positive' });

// Hedger markets (individual symbol data)
await client.getHedgerMarkets({ chainId: 42161, searchText: 'BTC' });

await client.getLockedParams({ chainId: 42161, marketName: 'BTCUSDT', leverage: 10 });
await client.refreshMarketsCache(42161);
```

#### Funding

```typescript
await client.getFundingRates({ chainId: 42161, limit: 50 });
await client.getFundingPayments({ address: '0x...', chainId: 42161 });
```

#### Metrics (Legacy Support)

```typescript
await client.getMetrics();
await client.getWeeklyVolume();
await client.getUserStats({ address: '0x...' });
await client.getDailyStats({ period: '7D' });
```

### WebSocket Client

#### Connection Management

```typescript
const ws = createSymmWebSocket({
  url: 'wss://ws.symm.io',
  autoReconnect: true,
  reconnectInterval: 5000,
  pingInterval: 30000
});

// Connect
await ws.connect();

// Disconnect
ws.disconnect();

// Check connection status
const connected = ws.isConnected();
```

#### Subscriptions

```typescript
// Positions (5s interval)
ws.subscribeToPositions(address, chainId, handler);

// Open orders (5s interval)
ws.subscribeToOpenOrders(address, chainId, handler);

// Trades (10s interval)
ws.subscribeToTrades(address, chainId, handler);

// Executions (2s interval)
ws.subscribeToExecutions(address, chainId, handler);

// TP/SL updates (5s interval)
ws.subscribeToTpsl(address, chainId, handler);

// Notifications (10s interval)
ws.subscribeToNotifications(address, chainId, handler);

// TWAP orders (5s interval)
ws.subscribeToTwapOrders(address, chainId, handler);

// Trigger orders (5s interval)
ws.subscribeToTriggerOrders(address, chainId, handler);

// Prediction triggers (5s interval)
ws.subscribeToPredictionTriggers(address, chainId, handler);

// Unsubscribe
ws.unsubscribe('positions', address, chainId, handler);

// Unsubscribe all
ws.unsubscribeAll();
```

#### Event Handlers

```typescript
// Connection events
ws.onConnect(() => console.log('Connected'));
ws.onDisconnect(() => console.log('Disconnected'));
ws.onError((error) => console.error('Error:', error));
```

### Utilities

```typescript
import {
  isValidAddress,
  normalizeAddress,
  formatWei,
  applySlippage,
  calculatePnlPercent,
  getUnixTimestamp,
  createDeadline,
  isSupportedChainId,
  getChainName
} from '@pear-protocol/symm-core';

// Address validation
if (isValidAddress('0x...')) {
  const normalized = normalizeAddress('0x...');
}

// Number formatting
const readable = formatWei('1000000000000000000', 18, 4); // "1.0000"

// Price utilities
const priceWithSlippage = applySlippage('50000', 0.5, true); // 0.5% slippage
const pnl = calculatePnlPercent('50000', '55000', true); // 10%

// Time utilities
const now = getUnixTimestamp();
const deadline = createDeadline(300); // 5 minutes from now

// Chain utilities
if (isSupportedChainId(42161)) {
  const chainName = getChainName(42161); // "Arbitrum One"
}
```

### Constants

```typescript
import {
  ARBITRUM_CHAIN_ID,
  BASE_CHAIN_ID,
  DIAMOND_ADDRESS,
  CHAIN_CONFIGS,
  WS_CHANNELS
} from '@pear-protocol/symm-core';

console.log(ARBITRUM_CHAIN_ID); // 42161
console.log(BASE_CHAIN_ID); // 8453
console.log(DIAMOND_ADDRESS[42161]); // "0x8F06..."
console.log(CHAIN_CONFIGS[42161]); // { chainId, name, rpcUrl, explorerUrl }
```

## Advanced Usage

### Custom Configuration

```typescript
import { SymmClient, SymmWebSocket } from '@pear-protocol/symm-core';

// Custom REST client
const client = new SymmClient({
  baseUrl: 'https://api.symm.io',
  defaultChainId: 42161,
  timeout: 30000,
  headers: {
    'X-API-Key': 'your-key'
  }
});

// Custom WebSocket client
const ws = new SymmWebSocket({
  url: 'wss://ws.symm.io',
  autoReconnect: true,
  reconnectInterval: 5000,
  maxReconnectAttempts: 10,
  pingInterval: 30000
});
```

### Error Handling

```typescript
import { isApiError, getErrorMessage } from '@pear-protocol/symm-core';

try {
  await client.openBasketPosition(request);
} catch (error) {
  if (isApiError(error)) {
    console.error('API Error:', error.error);
    console.error('Details:', error.details);
  } else {
    console.error('Error:', getErrorMessage(error));
  }
}
```

### TypeScript Types

```typescript
import type {
  OpenBasketPositionRequest,
  OpenPositionDto,
  AssetLeg,
  TpslOrder,
  TpslOrderType,
  SupportedChainId,
  NotificationCategory
} from '@pear-protocol/symm-core';

const request: OpenBasketPositionRequest = {
  chainId: 42161,
  accountAddress: '0x...',
  authToken: 'token',
  longPositions: [{ symbol: 'BTC', quantity: '1.0', leverage: 10 }]
};

const handlePosition = (position: OpenPositionDto) => {
  console.log(position.positionId, position.entryRatio);
  console.log(position.longAssets, position.shortAssets);
};

const firstLeg: AssetLeg = {
  symbolId: 1,
  symbol: 'BTC',
  quantity: '0.1',
  openedPrice: '50000'
};

const category: NotificationCategory = NotificationCategory.POSITION_LIQUIDATED;
```

## Modular Imports

Import only what you need for optimal bundle size:

```typescript
// Client only
import { createSymmClient } from '@pear-protocol/symm-core/client';

// WebSocket only
import { createSymmWebSocket } from '@pear-protocol/symm-core/websocket';

// Types only
import type { Position, TpslOrder } from '@pear-protocol/symm-core/types';

// Utils only
import { isValidAddress, formatWei } from '@pear-protocol/symm-core/utils';

// Constants only
import { ARBITRUM_CHAIN_ID, DIAMOND_ADDRESS } from '@pear-protocol/symm-core/constants';
```

## Examples

### Complete Trading Flow

```typescript
import { createSymmSDK } from '@pear-protocol/symm-core';

const { client, ws } = createSymmSDK({
  apiUrl: 'https://api.symm.io',
  wsUrl: 'wss://ws.symm.io'
});

// 1. Connect WebSocket
await ws.connect();

// 2. Subscribe to executions
ws.subscribeToExecutions('0x...', 42161, (data) => {
  console.log('Trade executed:', data);
});

// 3. Open position with TP/SL
const result = await client.openBasketPosition({
  accountAddress: '0x...',
  authToken: 'token',
  tpslConfig: {
    takeProfit: {
      triggerType: 'price',
      triggerValue: '55000', // 10% profit
      quantityToClose: '1.0'
    },
    stopLoss: {
      triggerType: 'price',
      triggerValue: '47500', // 5% loss
      quantityToClose: '1.0'
    }
  },
  longPositions: [
    {
      symbol: 'BTC',
      quantity: '1.0',
      leverage: 10
    }
  ]
});

console.log('Position opened with TP/SL:', result);
```

## Node.js Usage

For Node.js environments, you may need to provide a WebSocket implementation:

```typescript
import { createSymmWebSocket } from '@pear-protocol/symm-core';
import WebSocket from 'ws';

const ws = createSymmWebSocket({
  url: 'wss://ws.symm.io',
  WebSocket: WebSocket as any
});
```

## License

MIT

## Support

- Documentation: https://docs.symm.io
- GitHub: https://github.com/symm/symm-engine
- Discord: https://discord.gg/symm