aureon-os / scripts /traders /apiCapacityAnalysis.ts
GaryLeckey's picture
Deploy Aureon OS (deploy/selfhost stack: nginx edge + fusion API x2 + console + HNC organ + redis + postgres; paper mode) (part 19)
da0ce7b verified
Raw History Blame Contribute Delete
22 kB
/**
* 🎡 API CAPACITY & KEY REQUIREMENTS ANALYSIS 🎡
*
* Calculates exactly how many API keys needed per broker
* to sustain 48,000 trades/day (2,000 trades/hour Γ— 24 hours)
*/
interface BrokerAPISpec {
name: string;
emoji: string;
// Rate limits
requestsPerSecond: number;
requestsPerMinute: number;
ordersPerSecond: number;
ordersPerDay: number;
// API calls per trade
callsPerTrade: number; // price check + order + confirmation
// Costs
apiKeyLimit: number; // max keys per account
monthlyDataFee: number;
// Position limits
maxConcurrentPositions: number;
maxOrdersPerSecond: number;
}
const BROKERS: { [key: string]: BrokerAPISpec } = {
binance: {
name: 'Binance',
emoji: 'πŸͺ™',
requestsPerSecond: 20, // 1200/min = 20/sec
requestsPerMinute: 1200,
ordersPerSecond: 10,
ordersPerDay: 100000,
callsPerTrade: 3, // getPrice + createOrder + getOrder
apiKeyLimit: 30, // max 30 API keys per account
monthlyDataFee: 0,
maxConcurrentPositions: 200, // practical limit
maxOrdersPerSecond: 10,
},
capital: {
name: 'Capital.com',
emoji: 'πŸ“Š',
requestsPerSecond: 10,
requestsPerMinute: 600,
ordersPerSecond: 5,
ordersPerDay: 50000, // estimated
callsPerTrade: 4, // session + price + order + confirm
apiKeyLimit: 5, // limited API keys
monthlyDataFee: 0,
maxConcurrentPositions: 200,
maxOrdersPerSecond: 5,
},
alpaca: {
name: 'Alpaca',
emoji: 'πŸ¦™',
requestsPerSecond: 3.33, // 200/min = 3.33/sec
requestsPerMinute: 200,
ordersPerSecond: 3,
ordersPerDay: 500000, // very generous
callsPerTrade: 2, // order + confirmation (streaming prices)
apiKeyLimit: 1, // 1 key per account, need multiple accounts
monthlyDataFee: 0, // free tier
maxConcurrentPositions: 1000,
maxOrdersPerSecond: 3,
},
oanda: {
name: 'OANDA',
emoji: 'πŸ’±',
requestsPerSecond: 120, // very generous!
requestsPerMinute: 7200,
ordersPerSecond: 30,
ordersPerDay: 1000000, // essentially unlimited
callsPerTrade: 2, // streaming prices, just order + confirm
apiKeyLimit: 1, // 1 key per account
monthlyDataFee: 0,
maxConcurrentPositions: 1000,
maxOrdersPerSecond: 30,
},
};
// Our trading requirements
const REQUIREMENTS = {
tradesPerHour: 500, // per broker
hoursPerDay: 24,
brokersCount: 4,
// Derived
get tradesPerDay() { return this.tradesPerHour * this.hoursPerDay; },
get tradesPerMinute() { return this.tradesPerHour / 60; },
get tradesPerSecond() { return this.tradesPerHour / 3600; },
get totalDailyTrades() { return this.tradesPerDay * this.brokersCount; },
};
function analyzeApiCapacity() {
console.log(`
╔═══════════════════════════════════════════════════════════════════════════════╗
β•‘ β•‘
β•‘ πŸ”‘ API CAPACITY & KEY REQUIREMENTS ANALYSIS πŸ”‘ β•‘
β•‘ β•‘
β•‘ Trading Target: ${REQUIREMENTS.tradesPerHour} trades/hour per broker β•‘
β•‘ Daily Target: ${REQUIREMENTS.tradesPerDay.toLocaleString()} trades/day per broker β•‘
β•‘ Total Daily: ${REQUIREMENTS.totalDailyTrades.toLocaleString()} trades across all brokers β•‘
β•‘ β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
`);
console.log(' ═══════════════════════════════════════════════════════════════════════════════');
console.log(' πŸ“Š RATE LIMIT ANALYSIS');
console.log(' ═══════════════════════════════════════════════════════════════════════════════\n');
const results: any[] = [];
for (const [brokerId, spec] of Object.entries(BROKERS)) {
const tradesPerSecond = REQUIREMENTS.tradesPerSecond;
const tradesPerMinute = REQUIREMENTS.tradesPerMinute;
const tradesPerDay = REQUIREMENTS.tradesPerDay;
// API calls needed
const callsPerSecond = tradesPerSecond * spec.callsPerTrade;
const callsPerMinute = tradesPerMinute * spec.callsPerTrade;
const callsPerDay = tradesPerDay * spec.callsPerTrade;
// Capacity per key
const maxTradesPerSecPerKey = spec.ordersPerSecond;
const maxTradesPerMinPerKey = spec.requestsPerMinute / spec.callsPerTrade;
const maxTradesPerDayPerKey = spec.ordersPerDay;
// Keys needed (bottleneck calculation)
const keysForSecondLimit = Math.ceil(tradesPerSecond / maxTradesPerSecPerKey);
const keysForMinuteLimit = Math.ceil(tradesPerMinute / maxTradesPerMinPerKey);
const keysForDayLimit = Math.ceil(tradesPerDay / maxTradesPerDayPerKey);
const keysNeeded = Math.max(keysForSecondLimit, keysForMinuteLimit, keysForDayLimit);
const bottleneck = keysForSecondLimit >= keysForMinuteLimit && keysForSecondLimit >= keysForDayLimit
? 'orders/sec'
: keysForMinuteLimit >= keysForDayLimit
? 'requests/min'
: 'orders/day';
// Utilization with 1 key
const utilizationSec = (tradesPerSecond / maxTradesPerSecPerKey * 100).toFixed(1);
const utilizationMin = (tradesPerMinute / maxTradesPerMinPerKey * 100).toFixed(1);
const utilizationDay = (tradesPerDay / maxTradesPerDayPerKey * 100).toFixed(1);
// Can we do it with 1 key?
const singleKeyOk = keysNeeded <= 1;
// Accounts needed (if keys limited)
const accountsNeeded = Math.ceil(keysNeeded / spec.apiKeyLimit);
results.push({
brokerId,
spec,
callsPerSecond,
callsPerMinute,
callsPerDay,
keysNeeded,
bottleneck,
utilizationSec,
utilizationMin,
utilizationDay,
singleKeyOk,
accountsNeeded,
maxTradesPerSecPerKey,
maxTradesPerMinPerKey,
maxTradesPerDayPerKey,
});
console.log(` ${spec.emoji} ${spec.name.toUpperCase()}`);
console.log(` ${'─'.repeat(70)}`);
console.log(`
πŸ“‹ API LIMITS (per key):
β€’ Requests/second: ${spec.requestsPerSecond}
β€’ Requests/minute: ${spec.requestsPerMinute.toLocaleString()}
β€’ Orders/second: ${spec.ordersPerSecond}
β€’ Orders/day: ${spec.ordersPerDay.toLocaleString()}
β€’ Calls per trade: ${spec.callsPerTrade}
β€’ Max positions: ${spec.maxConcurrentPositions}
πŸ“Š OUR REQUIREMENTS:
β€’ Trades/second: ${tradesPerSecond.toFixed(3)}
β€’ Trades/minute: ${tradesPerMinute.toFixed(2)}
β€’ Trades/day: ${tradesPerDay.toLocaleString()}
β€’ API calls/second: ${callsPerSecond.toFixed(2)}
β€’ API calls/day: ${callsPerDay.toLocaleString()}
πŸ“ˆ CAPACITY PER KEY:
β€’ Max trades/sec: ${maxTradesPerSecPerKey}
β€’ Max trades/min: ${maxTradesPerMinPerKey.toFixed(1)}
β€’ Max trades/day: ${maxTradesPerDayPerKey.toLocaleString()}
⚑ UTILIZATION (with 1 key):
β€’ Second limit: ${utilizationSec}% ${parseFloat(utilizationSec) > 100 ? 'πŸ”΄ EXCEEDED' : '🟒 OK'}
β€’ Minute limit: ${utilizationMin}% ${parseFloat(utilizationMin) > 100 ? 'πŸ”΄ EXCEEDED' : '🟒 OK'}
β€’ Daily limit: ${utilizationDay}% ${parseFloat(utilizationDay) > 100 ? 'πŸ”΄ EXCEEDED' : '🟒 OK'}
πŸ”‘ KEYS REQUIRED:
β€’ Minimum keys: ${keysNeeded} ${singleKeyOk ? 'βœ… (1 key sufficient!)' : '⚠️'}
β€’ Bottleneck: ${bottleneck}
β€’ Max keys/account: [REDACTED]
β€’ Accounts needed: ${accountsNeeded}
`);
}
// Summary table
console.log(`
═══════════════════════════════════════════════════════════════════════════════
πŸ“‹ SUMMARY TABLE
═══════════════════════════════════════════════════════════════════════════════
`);
console.log(' β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”');
console.log(' β”‚ Broker β”‚ Trades/Day β”‚ Keys Need β”‚ Bottleneck β”‚ Accounts β”‚ Status β”‚');
console.log(' β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€');
for (const r of results) {
const status = r.singleKeyOk ? 'βœ… 1 KEY OK' : `⚠️ ${r.keysNeeded} KEYS`;
console.log(` β”‚ ${r.spec.emoji} ${r.spec.name.padEnd(9)} β”‚ ${REQUIREMENTS.tradesPerDay.toLocaleString().padStart(11)} β”‚ ${r.keysNeeded.toString().padStart(11)} β”‚ ${r.bottleneck.padStart(11)} β”‚ ${r.accountsNeeded.toString().padStart(11)} β”‚ ${status.padStart(11)} β”‚`);
}
console.log(' β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜');
// Total requirements
const totalKeys = results.reduce((sum, r) => sum + r.keysNeeded, 0);
const totalAccounts = results.reduce((sum, r) => sum + r.accountsNeeded, 0);
console.log(`
═══════════════════════════════════════════════════════════════════════════════
πŸ” TOTAL REQUIREMENTS
═══════════════════════════════════════════════════════════════════════════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
β”‚ πŸ”‘ Total API Keys Needed: ${totalKeys.toString().padStart(3)} β”‚
β”‚ πŸ‘€ Total Accounts Needed: ${totalAccounts.toString().padStart(3)} β”‚
β”‚ πŸ“ˆ Total Daily Trades: ${REQUIREMENTS.totalDailyTrades.toLocaleString().padStart(7)} β”‚
β”‚ πŸ’° Monthly API Costs: Β£0.00 (all free tier!) β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
`);
// Detailed breakdown by broker
console.log(`
═══════════════════════════════════════════════════════════════════════════════
πŸ“ DETAILED SETUP REQUIREMENTS
═══════════════════════════════════════════════════════════════════════════════
`);
for (const r of results) {
console.log(` ${r.spec.emoji} ${r.spec.name.toUpperCase()}`);
console.log(` ${'─'.repeat(70)}`);
if (r.singleKeyOk) {
console.log(`
βœ… SINGLE KEY SUFFICIENT
Setup:
β€’ 1 trading account
β€’ 1 API key
β€’ Enable trading permissions
Headroom: ${(100 - Math.max(parseFloat(r.utilizationSec), parseFloat(r.utilizationMin), parseFloat(r.utilizationDay))).toFixed(1)}% spare capacity
`);
} else {
console.log(`
⚠️ MULTIPLE KEYS REQUIRED
Setup:
β€’ ${r.accountsNeeded} trading account(s)
β€’ ${r.keysNeeded} API key(s) total
β€’ Load balance across keys
Strategy:
β€’ Round-robin requests across ${r.keysNeeded} keys
β€’ Each key handles ${Math.ceil(REQUIREMENTS.tradesPerDay / r.keysNeeded)} trades/day
Alternative:
β€’ Reduce trade frequency to ${Math.floor(r.spec.ordersPerDay / 24)} trades/hour (single key)
`);
}
}
// Scaling recommendations
console.log(`
═══════════════════════════════════════════════════════════════════════════════
πŸ“ˆ SCALING RECOMMENDATIONS
═══════════════════════════════════════════════════════════════════════════════
🎯 CURRENT TARGET: 500 trades/hour per broker (48,000/day total)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Scale β”‚ Trades/Hr β”‚ Trades/Day β”‚ Keys Need β”‚ Accounts β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€`);
const scales = [
{ name: 'Minimal', multiplier: 0.1 },
{ name: 'Conservative', multiplier: 0.25 },
{ name: 'Moderate', multiplier: 0.5 },
{ name: 'Current', multiplier: 1 },
{ name: 'Aggressive', multiplier: 2 },
{ name: 'Maximum', multiplier: 5 },
];
for (const scale of scales) {
const tradesPerHour = Math.floor(500 * scale.multiplier);
const tradesPerDay = tradesPerHour * 24 * 4;
let totalKeysNeeded = 0;
let totalAccountsNeeded = 0;
for (const [brokerId, spec] of Object.entries(BROKERS)) {
const dailyTrades = tradesPerHour * 24;
const keysNeeded = Math.max(
1,
Math.ceil(dailyTrades / spec.ordersPerDay),
Math.ceil((tradesPerHour / 60) / (spec.requestsPerMinute / spec.callsPerTrade))
);
totalKeysNeeded += keysNeeded;
totalAccountsNeeded += Math.ceil(keysNeeded / spec.apiKeyLimit);
}
const current = scale.name === 'Current' ? ' πŸ‘ˆ' : '';
console.log(` β”‚ ${scale.name.padEnd(11)} β”‚ ${(tradesPerHour * 4).toLocaleString().padStart(11)} β”‚ ${tradesPerDay.toLocaleString().padStart(11)} β”‚ ${totalKeysNeeded.toString().padStart(11)} β”‚ ${totalAccountsNeeded.toString().padStart(11)} β”‚${current}`);
}
console.log(` β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜`);
// WebSocket recommendation
console.log(`
═══════════════════════════════════════════════════════════════════════════════
πŸ”Œ WEBSOCKET OPTIMIZATION
═══════════════════════════════════════════════════════════════════════════════
Using WebSocket streams REDUCES API calls significantly:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Broker β”‚ REST (calls/trade) β”‚ WebSocket (calls) β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ πŸͺ™ Binance β”‚ 3 (price+order+conf) β”‚ 1 (order only) β”‚
β”‚ πŸ“Š Capital β”‚ 4 (session+price+2) β”‚ 2 (order+conf) β”‚
β”‚ πŸ¦™ Alpaca β”‚ 2 (order+conf) β”‚ 1 (order only) β”‚
β”‚ πŸ’± OANDA β”‚ 2 (order+conf) β”‚ 1 (order only) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
πŸ’‘ With WebSocket: ALL brokers work with 1 API key!
═══════════════════════════════════════════════════════════════════════════════
`);
// Final verdict
console.log(`
╔═══════════════════════════════════════════════════════════════════════════════╗
β•‘ β•‘
β•‘ 🎡 FINAL VERDICT: THE TEMPO IS ACHIEVABLE 🎡 β•‘
β•‘ β•‘
╠═══════════════════════════════════════════════════════════════════════════════╣
β•‘ β•‘
β•‘ For 48,000 trades/day (current simulation): β•‘
β•‘ β•‘
β•‘ πŸͺ™ Binance: 1 API key βœ… (has 10 orders/sec capacity) β•‘
β•‘ πŸ“Š Capital: 1 API key βœ… (10 req/sec is enough) β•‘
β•‘ πŸ¦™ Alpaca: 1 API key βœ… (200/min handles 8.3 trades/min) β•‘
β•‘ πŸ’± OANDA: 1 API key βœ… (120 req/sec is massive overkill!) β•‘
β•‘ β•‘
β•‘ ───────────────────────────────────────────────────────────────────────── β•‘
β•‘ β•‘
β•‘ TOTAL: 4 API keys (1 per broker) + 4 accounts β•‘
β•‘ COST: Β£0/month (all free tier APIs) β•‘
β•‘ β•‘
β•‘ 🎯 The symphony can play at full tempo with minimal setup! 🎯 β•‘
β•‘ β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
─────────────────────────────────────────────────────────────────────────────────
🎡 "Four keys, four accounts, 48,000 trades - the dance continues" 🎡
─────────────────────────────────────────────────────────────────────────────────
`);
}
analyzeApiCapacity();