Exchange Routing
The ZeroQuant SDK provides an agnostic order validation and routing layer for centralized exchanges (CEX). This allows AI agents to safely execute trades on major exchanges while respecting permission limits.
Overview
The exchange router:
- Accepts orders in a unified format (Minimum Viable Order)
- Validates against agent permission limits
- Routes valid orders to the target exchange with original credentials
- Returns unified response format
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Agent │────▶│ Router │────▶│ Exchange │
│ Order │ │ (Validate) │ │ (Binance) │
└─────────────┘ └──────────────┘ └─────────────┘
│
┌──────┴──────┐
│ Reject │
│ (if invalid)│
└─────────────┘
Supported Exchanges
- Binance - Spot trading
- Coinbase - Advanced Trade API
- Kraken - Spot trading
Quick Start
Basic Usage
import { ExchangeRouter } from '@zeroquant/sdk';
const router = new ExchangeRouter();
const result = await router.routeOrder({
order: {
symbol: 'ETH/USDC',
side: 'buy',
originalQuantityBase: 1.5,
originalPrice: 2000,
timeInForce: 'gtc',
reduceOnly: false,
},
exchange: 'binance',
credentials: {
apiKey: process.env.BINANCE_API_KEY!,
apiSecret: process.env.BINANCE_API_SECRET!,
},
});
if (result.success) {
console.log('Order placed:', result.exchangeResponse?.exchangeOrderId);
} else {
console.log('Order failed:', result.error);
}
With Agent Limits
import { createAgentRouter } from '@zeroquant/sdk';
// Create router with agent-specific limits
const router = createAgentRouter({
dailyLimitUsd: 50000,
perTxLimitUsd: 10000,
allowedPairs: ['ETH/USDC', 'BTC/USDC', 'SOL/USDC'],
});
// Orders exceeding limits will be rejected
const result = await router.routeOrder({
order: {
symbol: 'ETH/USDC',
side: 'buy',
originalQuantityBase: 100, // 100 ETH
originalPrice: 2000, // $200,000 order value
},
exchange: 'binance',
credentials: { apiKey: '...', apiSecret: '...' },
});
// result.success = false
// result.validation.errors = [{ code: 'EXCEEDS_TX_LIMIT', ... }]
Validation Rules
The router validates orders against configurable rules:
interface ValidationRules {
// Value limits
maxOrderValueUsd?: number; // Maximum single order value
perTxLimitUsd?: number; // Per-transaction USD limit
dailyLimitUsd?: number; // Daily spending limit
dailySpentUsd?: number; // Track daily spent amount
// Position limits
maxPositionSize?: number; // Max position in base asset
maxLeverage?: number; // Maximum leverage allowed
// Asset restrictions
allowedPairs?: string[]; // Whitelist of trading pairs
blockedPairs?: string[]; // Blacklist of trading pairs
allowedOrderTypes?: string[]; // Allowed order types
// Order restrictions
requireReduceOnly?: boolean; // Force reduce-only orders
// Custom validation
customValidator?: (order: MinimumViableOrder) => ValidationResult;
}
Example with Full Validation
const router = new ExchangeRouter({
defaultValidationRules: {
// Limits
maxOrderValueUsd: 10000,
perTxLimitUsd: 5000,
dailyLimitUsd: 50000,
// Restrictions
allowedPairs: ['ETH/USDC', 'BTC/USDC'],
allowedOrderTypes: ['market', 'limit'],
blockedPairs: ['DOGE/USDC'],
// Custom validator
customValidator: (order) => {
const errors = [];
if (order.side === 'sell' && !order.reduceOnly) {
errors.push({
code: 'SELL_REQUIRES_REDUCE_ONLY',
message: 'Sell orders must be reduce-only',
});
}
return { valid: errors.length === 0, errors, warnings: [] };
},
},
});
Exchange Adapters
Binance
import { BinanceAdapter } from '@zeroquant/sdk';
const binance = new BinanceAdapter({
testnet: true, // Use testnet
});
// Direct adapter usage
const result = await binance.routeOrder({
order: { symbol: 'ETHUSDC', side: 'buy', ... },
credentials: { apiKey: '...', apiSecret: '...' },
});
Coinbase
import { CoinbaseAdapter } from '@zeroquant/sdk';
const coinbase = new CoinbaseAdapter({
testnet: true, // Use sandbox
});
const result = await coinbase.routeOrder({
order: { symbol: 'ETH-USDC', side: 'buy', ... },
credentials: {
apiKey: '...',
apiSecret: '...',
},
});
Kraken
import { KrakenAdapter } from '@zeroquant/sdk';
const kraken = new KrakenAdapter();
const result = await kraken.routeOrder({
order: { symbol: 'ETHUSDC', side: 'buy', ... },
credentials: {
apiKey: '...',
apiSecret: '...', // Base64 encoded
},
});
Dry Run Mode
Validate orders without submitting:
const result = await router.routeOrder({
order: { ... },
exchange: 'binance',
credentials: { ... },
dryRun: true, // Validate only
});
if (result.validation.valid) {
console.log('Order would be accepted');
} else {
console.log('Validation errors:', result.validation.errors);
}
Event Handling
Subscribe to routing events:
router.subscribe((event) => {
switch (event.type) {
case 'order_validated':
console.log('Order validated:', event.result.valid);
break;
case 'order_submitted':
console.log('Order submitted to', event.exchange);
break;
case 'order_response':
console.log('Exchange response:', event.response);
break;
case 'order_rejected':
console.log('Order rejected:', event.errors);
break;
case 'order_error':
console.log('Error:', event.error);
break;
case 'rate_limited':
console.log('Rate limited on', event.exchange);
break;
}
});
Batch Orders
Route multiple orders efficiently:
const results = await router.routeOrdersBatch([
{
order: { symbol: 'ETH/USDC', side: 'buy', ... },
exchange: 'binance',
credentials: binanceCredentials,
},
{
order: { symbol: 'BTC/USD', side: 'buy', ... },
exchange: 'coinbase',
credentials: coinbaseCredentials,
},
]);
// Results array in same order as requests
results.forEach((result, i) => {
console.log(`Order ${i}: ${result.success ? 'success' : 'failed'}`);
});
Cancel Orders
const cancelResult = await router.cancelOrder({
orderId: 'internal-order-id',
exchangeOrderId: '12345',
symbol: 'ETH/USDC',
exchange: 'binance',
credentials: { apiKey: '...', apiSecret: '...' },
});
if (cancelResult.success) {
console.log('Order cancelled');
}
Query Orders
// Query open orders
const openOrders = await router.queryOrders({
exchange: 'binance',
credentials: { apiKey: '...', apiSecret: '...' },
symbol: 'ETH/USDC',
openOnly: true,
});
// Query all orders
const allOrders = await router.queryOrders({
exchange: 'binance',
credentials: { apiKey: '...', apiSecret: '...' },
limit: 100,
});
Statistics
Track router performance:
const stats = router.getStats();
console.log({
totalOrders: stats.totalOrders,
successfulOrders: stats.successfulOrders,
failedOrders: stats.failedOrders,
rejectedOrders: stats.rejectedOrders,
ordersByExchange: stats.ordersByExchange,
averageRoutingTime: stats.averageRoutingTime,
});
// Reset stats
router.resetStats();
Custom Exchange Adapters
Extend support for additional exchanges:
import { BaseExchangeAdapter } from '@zeroquant/sdk';
class MyExchangeAdapter extends BaseExchangeAdapter {
get exchange() { return 'myexchange' as const; }
get displayName() { return 'My Exchange'; }
toExchangeFormat(order: MinimumViableOrder): unknown {
// Convert to exchange-specific format
}
fromExchangeFormat(response: unknown): ExchangeOrderResponse {
// Convert response to standard format
}
// ... implement other abstract methods
}
// Register with router
const router = new ExchangeRouter({
customAdapters: [new MyExchangeAdapter({ exchange: 'myexchange' })],
});
Security Considerations
- Credentials are never stored - Pass-through only
- Validate before routing - All orders are checked against rules
- Rate limiting - Respect exchange rate limits
- Error handling - Graceful failure without exposing secrets
// Credentials are used only for the API call
// and never logged or stored
const result = await router.routeOrder({
order: { ... },
exchange: 'binance',
credentials: {
apiKey: process.env.API_KEY!, // From secure env
apiSecret: process.env.API_SECRET!, // From secure env
},
});
Integration with TEE
Combine with TEE attestation for elevated limits:
import { AMDSEVClient, createAgentRouter } from '@zeroquant/sdk';
// Get TEE-elevated limits
const teeClient = new AMDSEVClient(w3, verifierAddr, permissionAddr);
const limits = await teeClient.getEffectiveLimits(agentAddress);
// Create router with TEE limits
const router = createAgentRouter({
dailyLimitUsd: limits.usingTEELimits
? limits.dailyLimit / 1e6 // 6 decimals
: limits.dailyLimit / 1e6 / 3, // Standard limits
perTxLimitUsd: limits.perTxLimit / 1e6,
});
Best Practices
- Always use validation - Don't skip unless testing
- Monitor events - Track order lifecycle
- Handle errors gracefully - Exchanges can fail
- Use dry runs - Test order validity first
- Implement rate limiting - Avoid exchange bans
- Secure credentials - Use environment variables