JSPM

@balancesphere/sdk

0.3.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 7
  • Score
    100M100P100Q69792F
  • License MIT

TypeScript SDK for the balanceSphere portfolio tracking platform. Send events from your strategy services, or fetch read-only portfolio stats (NAV / PPS / drawdown) via the public_read scope.

Package Exports

  • @balancesphere/sdk

Readme

@balancesphere/sdk

TypeScript SDK for the balanceSphere event-driven portfolio tracking platform.

Full documentation: SDK Integration Guide | Event Types Reference | API Reference

Installation

npm install @balancesphere/sdk
# or
pnpm add @balancesphere/sdk
# or
yarn add @balancesphere/sdk

Requires Node.js 18+.

From inside the monorepo, use the workspace alias:

pnpm add @balancesphere/sdk@workspace:*

Quick Start

import { BalanceSphereClient } from '@balancesphere/sdk';

const client = new BalanceSphereClient({
  apiKey: 'bsk_live_...',
  // baseUrl: 'https://api.balancesphere.io/api/v1', // default
});

// Send a balance snapshot
await client.events.send('balance.update', {
  idempotencyKey: 'snap-2024-01-15-001',
  payload: {
    assets: [
      { symbol: 'BTC', amount: 1.5, price_usd: 95000 },
      { symbol: 'ETH', amount: 20, price_usd: 3200, chain: 'ethereum' },
      { symbol: 'USDC', amount: 50000, price_usd: 1.0, protocol: 'aave-v3' },
    ],
    snapshot_type: 'full',
  },
});

Read-Only Portfolio Stats (public_read scope)

If your API key was issued with the public_read scope, you can fetch pre-calculated NAV / PPS / drawdown for the bound portfolio. These keys are only allowed on the methods below — every other endpoint returns 403.

const summary = await client.portfolio.getSummary();
// {
//   portfolio: { id, name, inceptionAt, reportingMode, feesEnabled, crystallizationCadence },
//   asOf, totalNavUsd,
//   series: [{ base, navBase, navUsd, ppsNet, ppsGross, hwm,
//              returnSinceInceptionPct, returnSinceLastCrystPct,
//              drawdownPct, accruedFeesBase, latestSnapshotAt }],
// }

const history = await client.portfolio.getNavHistory({
  base: 'USDC',
  period: '30d',     // '7d' | '30d' | '90d' | 'ytd' | 'all'
  resolution: 'daily', // 'hourly' | 'daily'
});
// [{ at, ppsNet, navBase, navUsd, drawdownPct }, ...]

Full field reference: docs/public-read-sdk.md.

Portfolio Allocations & Assets (default scope)

With a default-scope key you can fetch presentation aggregates for a given portfolio UUID — per-strategy allocation (in USDT) + APR, and the material exposure symbol list. These derive from the portfolio's latest confirmed snapshot.

const pid = '8ba659a0-a2ba-4ece-adf2-ae4b06802e6d';

const alloc = await client.portfolios.getAllocations(pid);
// { allocations: [{ name: 'Fluid reUSDGHO', allocation_in_usdt: '229288.84', apr: '23.6525' }, ...],
//   created_at: 1780368117860 }

const symbols = await client.portfolios.getAssets(pid);
// ['reUSD', 'GHO', 'sUSDai', 'USDC', ...]

Note: amounts are reported "in USDT" (legacy behaviour); this may change to plain USD in a future version.

Event Types

balance.update

Report current asset balances for a strategy.

await client.events.send('balance.update', {
  idempotencyKey: 'bal-001',
  payload: {
    assets: [{ symbol: 'BTC', amount: 1.5, price_usd: 95000 }],
    snapshot_type: 'full', // 'full' replaces all, 'delta' updates specific assets
  },
});

trade.executed

Report a single executed trade.

await client.events.send('trade.executed', {
  idempotencyKey: 'trade-001',
  payload: {
    symbol: 'BTC/USDT',
    side: 'buy',
    quantity: 0.5,
    price: 95000,
    fee: 23.75,
    venue: 'binance',
  },
});

trade.sync

Bulk sync trades from an exchange.

await client.events.send('trade.sync', {
  idempotencyKey: 'sync-binance-2024-01-15',
  payload: {
    trades: [
      { trade_id: 'T001', symbol: 'BTC/USDT', side: 'buy', quantity: 0.5, price: 95000 },
      { trade_id: 'T002', symbol: 'ETH/USDT', side: 'sell', quantity: 5, price: 3200 },
    ],
    sync_source: 'binance',
    sync_range: { start: '2024-01-14T00:00:00Z', end: '2024-01-15T00:00:00Z' },
  },
});

position.opened / position.closed

Report position lifecycle events.

// Open
await client.events.send('position.opened', {
  idempotencyKey: 'pos-open-001',
  payload: {
    symbol: 'BTC/USDT',
    side: 'long',
    entry_price: 95000,
    quantity: 0.5,
    leverage: 3,
    venue: 'binance',
  },
});

// Close
await client.events.send('position.closed', {
  idempotencyKey: 'pos-close-001',
  payload: {
    position_id: 'pos_abc123',
    exit_price: 98000,
    realized_pnl_usd: 1500,
  },
});

position.sync

Bulk sync open positions from an exchange.

await client.events.send('position.sync', {
  idempotencyKey: 'pos-sync-001',
  payload: {
    positions: [
      {
        symbol: 'BTC/USDT',
        side: 'long',
        quantity: 0.5,
        avg_price: 95000,
        unrealized_pnl_usd: 500,
      },
    ],
    sync_source: 'binance',
  },
});

deposit / withdrawal

Report cash flow events.

await client.events.send('deposit', {
  idempotencyKey: 'dep-001',
  payload: { symbol: 'USDT', amount: 10000, price_usd: 1.0, chain: 'ethereum' },
});

await client.events.send('withdrawal', {
  idempotencyKey: 'wd-001',
  payload: { symbol: 'USDT', amount: 5000, price_usd: 1.0 },
});

pnl.snapshot

Report an external PnL calculation.

await client.events.send('pnl.snapshot', {
  idempotencyKey: 'pnl-001',
  payload: {
    total_balance_usd: 150000,
    total_pnl_usd: 12000,
    apy_percent: 18.5,
  },
});

Batch Events

Send up to 100 events in a single request:

await client.events.sendBatch([
  {
    event_type: 'trade.executed',
    idempotency_key: 'batch-t1',
    source: { strategy_id: 'strat_abc' },
    payload: { symbol: 'BTC/USDT', side: 'buy', quantity: 1, price: 95000 },
  },
  {
    event_type: 'trade.executed',
    idempotency_key: 'batch-t2',
    source: { strategy_id: 'strat_abc' },
    payload: { symbol: 'ETH/USDT', side: 'sell', quantity: 10, price: 3200 },
  },
]);

Note: The server uses the API key's bound strategy_id for routing, not the envelope's source.strategy_id.

Configuration

const client = new BalanceSphereClient({
  apiKey: 'bsk_live_...', // Required
  baseUrl: 'https://...', // Default: https://api.balancesphere.io/api/v1
  timeout: 30000, // Request timeout in ms (default: 30000)
  maxRetries: 3, // Retry count on 5xx/timeout (default: 3)
});

Error Handling

import { ApiError } from '@balancesphere/sdk';

try {
  await client.events.send('balance.update', { ... });
} catch (err) {
  if (err instanceof ApiError) {
    console.error(`API error ${err.status}: ${err.code}${err.message}`);
  }
}

Idempotent events return status: 'duplicate' (HTTP 202) instead of throwing.