Spaces:
Running
Running
Deploy Aureon OS (deploy/selfhost stack: nginx edge + fusion API x2 + console + HNC organ + redis + postgres; paper mode) (part 19)
da0ce7b verified Download scripts/traders/apiCapacityAnalysis.ts from GaryLeckey/aureon-os: direct link, hf CLI and curl.
- Browser
- Download file 22 kB
-
https://huggingface.co/spaces/GaryLeckey/aureon-os/resolve/main/scripts/traders/apiCapacityAnalysis.ts
- Command line
-
hf download hf://spaces/GaryLeckey/aureon-os/scripts/traders/apiCapacityAnalysis.ts
-
curl -L -o apiCapacityAnalysis.ts https://huggingface.co/spaces/GaryLeckey/aureon-os/resolve/main/scripts/traders/apiCapacityAnalysis.ts
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(); | |