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/sdkRequires 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.