From dad47fc13d4349c183e0ae997e6d8890b40f04b6 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 6 May 2026 06:40:49 +0000 Subject: [PATCH] fix(simple): prefer available user alpaca creds --- backend/src/index.ts | 46 ++++++++++++++++++++++++------- backend/src/services/apiServer.ts | 20 ++++++++------ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 1a1458f..93a93c1 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -101,6 +101,28 @@ const computeSimpleProfitTargetPrice = (entryPrice: number, entry: ManualEntryRe return Number((entryPrice * (1 + (threshold / 100))).toFixed(4)); }; +const resolvePreferredUserAlpacaCredentials = (user: { + ALPACA_API_KEY?: string; + ALPACA_SECRET_KEY?: string; + REAL_ALPACA_API_KEY?: string; + REAL_ALPACA_SECRET_KEY?: string; +}): { key: string; secret: string; source: 'paper' | 'live' | 'none' } => { + const paperKey = String(user.ALPACA_API_KEY || '').trim(); + const paperSecret = String(user.ALPACA_SECRET_KEY || '').trim(); + const liveKey = String(user.REAL_ALPACA_API_KEY || '').trim(); + const liveSecret = String(user.REAL_ALPACA_SECRET_KEY || '').trim(); + + if (config.PAPER_TRADING) { + if (paperKey && paperSecret) return { key: paperKey, secret: paperSecret, source: 'paper' }; + if (liveKey && liveSecret) return { key: liveKey, secret: liveSecret, source: 'live' }; + } else { + if (liveKey && liveSecret) return { key: liveKey, secret: liveSecret, source: 'live' }; + if (paperKey && paperSecret) return { key: paperKey, secret: paperSecret, source: 'paper' }; + } + + return { key: '', secret: '', source: 'none' }; +}; + async function main() { logger.info(`Starting ${config.PRODUCT_ID} trading backend...`); validateConfig(); @@ -137,8 +159,9 @@ async function main() { // --- 1. Identify Primary Key (for Data Fetching) --- const isPlaceholder = (val: string) => !val || val === 'your_key' || val === 'your_secret'; - const primaryAlpacaKey = (!isPlaceholder(config.ALPACA_API_KEY) ? config.ALPACA_API_KEY : (users.length > 0 ? (config.PAPER_TRADING ? users[0].ALPACA_API_KEY : users[0].REAL_ALPACA_API_KEY) : '')); - const primaryAlpacaSecret = (!isPlaceholder(config.ALPACA_API_SECRET) ? config.ALPACA_API_SECRET : (users.length > 0 ? (config.PAPER_TRADING ? users[0].ALPACA_SECRET_KEY : users[0].REAL_ALPACA_SECRET_KEY) : '')); + const preferredPrimaryUserCredentials = users.length > 0 ? resolvePreferredUserAlpacaCredentials(users[0]) : { key: '', secret: '', source: 'none' as const }; + const primaryAlpacaKey = !isPlaceholder(config.ALPACA_API_KEY) ? config.ALPACA_API_KEY : preferredPrimaryUserCredentials.key; + const primaryAlpacaSecret = !isPlaceholder(config.ALPACA_API_SECRET) ? config.ALPACA_API_SECRET : preferredPrimaryUserCredentials.secret; logger.info(`🚨 Bot Initialized for ${config.SYMBOLS.length} Symbols: ${config.SYMBOLS.join(', ')}`); @@ -237,14 +260,12 @@ async function main() { const ctx = getSimpleWorkerContext(entry); if (!ctx) continue; - const currentPrice = resolveSimpleMarketPrice(entry); - if (!(currentPrice && currentPrice > 0)) continue; - if (shouldArmSimpleBuy(entry)) { + const currentPrice = resolveSimpleMarketPrice(entry) ?? toPositiveNumber(entry.reference_price); const triggerPrice = computeSimpleBuyTriggerPrice(entry); const desiredQty = toPositiveNumber(entry.quantity); const threshold = toNonNegativeNumber(entry.drop_threshold_for_buy); - if (!triggerPrice || !desiredQty) continue; + if (!triggerPrice || !desiredQty || !(currentPrice && currentPrice > 0)) continue; if (currentPrice > triggerPrice) continue; const result = await ctx.manualTrader.executeRequest( @@ -272,6 +293,9 @@ async function main() { continue; } + const currentPrice = resolveSimpleMarketPrice(entry); + if (!(currentPrice && currentPrice > 0)) continue; + if (isSimpleSubmittedStatus(entry.status)) { await bindSimpleBoughtPosition(entry, ctx); continue; @@ -458,8 +482,9 @@ async function main() { users.push(user); } - const userKey = config.PAPER_TRADING ? user.ALPACA_API_KEY : user.REAL_ALPACA_API_KEY; - const userSecret = config.PAPER_TRADING ? user.ALPACA_SECRET_KEY : user.REAL_ALPACA_SECRET_KEY; + const preferredCredentials = resolvePreferredUserAlpacaCredentials(user); + const userKey = preferredCredentials.key; + const userSecret = preferredCredentials.secret; if (!userKey || !userSecret) { logger.warn(`⚠️ User ${user.email} missing keys for profile ${profile.name}. Skipping.`); @@ -540,8 +565,9 @@ async function main() { // Fallback to one-per-user if no profiles table entries exist logger.info(`👥 No specific profiles found. Falling back to multi-user default...`); for (const user of users) { - const userKey = config.PAPER_TRADING ? user.ALPACA_API_KEY : user.REAL_ALPACA_API_KEY; - const userSecret = config.PAPER_TRADING ? user.ALPACA_SECRET_KEY : user.REAL_ALPACA_SECRET_KEY; + const preferredCredentials = resolvePreferredUserAlpacaCredentials(user); + const userKey = preferredCredentials.key; + const userSecret = preferredCredentials.secret; if (!userKey || !userSecret) continue; diff --git a/backend/src/services/apiServer.ts b/backend/src/services/apiServer.ts index cb81ae2..0f05d59 100644 --- a/backend/src/services/apiServer.ts +++ b/backend/src/services/apiServer.ts @@ -156,18 +156,20 @@ interface AlpacaCredentials { async function getUserExecutionAlpacaCredentials(userId: string): Promise { const profile = await getCurrentUserProfile(userId); - const key = String(config.PAPER_TRADING ? profile.ALPACA_API_KEY || '' : profile.REAL_ALPACA_API_KEY || '').trim(); - const secret = String(config.PAPER_TRADING ? profile.ALPACA_SECRET_KEY || '' : profile.REAL_ALPACA_SECRET_KEY || '').trim(); + const paperKey = String(profile.ALPACA_API_KEY || '').trim(); + const paperSecret = String(profile.ALPACA_SECRET_KEY || '').trim(); + const liveKey = String(profile.REAL_ALPACA_API_KEY || '').trim(); + const liveSecret = String(profile.REAL_ALPACA_SECRET_KEY || '').trim(); - if (!key || !secret) { - throw new MissingServiceConfigError( - config.PAPER_TRADING - ? 'User Alpaca paper credentials are not configured' - : 'User Alpaca live credentials are not configured' - ); + if (config.PAPER_TRADING) { + if (paperKey && paperSecret) return { key: paperKey, secret: paperSecret }; + if (liveKey && liveSecret) return { key: liveKey, secret: liveSecret }; + } else { + if (liveKey && liveSecret) return { key: liveKey, secret: liveSecret }; + if (paperKey && paperSecret) return { key: paperKey, secret: paperSecret }; } - return { key, secret }; + throw new MissingServiceConfigError('User Alpaca credentials are not configured'); } async function getUserMarketDataAlpacaCredentials(userId: string): Promise {