/* TRADINGGRAPH related file */ // Server-only imports are loaded dynamically inside the action to avoid client bundling issues export async function action({ request }: { request: Request }) { console.log("[analyze] Request received:", request.method, request.url); const body = await request.json(); console.log("[analyze] Request body:", JSON.stringify(body)); const ticker = body.ticker?.toUpperCase(); const date = body.date || new Date().toISOString().split("T")[0]; if (!ticker) { console.log("[analyze] Error: ticker missing"); return Response.json({ error: "ticker is required" }, { status: 400 }); } // Load server-only modules dynamically to prevent them from being included in client bundles const { buildTradingGraph } = await import("../../lib/tradingConfig.server"); const { db } = await import("../../lib/db.server"); const { fetchAccount, fetchRecentCloses, fetchBars } = await import("../../lib/alpacaClient"); const apiKey = process.env.OPENROUTER_API_KEY; console.log("[analyze] API key configured:", !!apiKey, apiKey?.substring(0, 10) + "..."); if (!apiKey || apiKey === "your_openrouter_api_key_here") { console.log("[analyze] Using mock mode"); const mockDecision = { action: "hold" as const, confidence: 0.75, reasoning: `${ticker} analysis - Mock mode: positive momentum detected with neutral technical signals`, agentSignals: [ { agent: "fundamentals" as const, signal: "bullish" as const, confidence: 0.7, reasoning: "Strong fundamentals with positive earnings outlook", timestamp: new Date().toISOString(), }, { agent: "technical" as const, signal: "neutral" as const, confidence: 0.6, reasoning: "Mixed technical indicators", timestamp: new Date().toISOString(), }, ], debateRounds: [ { bullishView: "Bullish case supported by fundamentals and momentum", bearishView: "Bearish case from mixed technical signals", researcher: "bullish" as const, }, ], }; console.log("[analyze] Returning mock decision"); return Response.json(mockDecision); } const { graph, config } = await buildTradingGraph(apiKey); console.log("[analyze] Trading config:", config); // Fetch latest Alpaca account and recent prices; abort if unavailable let account: any = undefined; let prices: number[] = []; let recentBars: any[] = []; try { account = await fetchAccount(); prices = await fetchRecentCloses(ticker); // Also fetch recent intraday bars to enable deterministic execution plan calculation try { recentBars = await fetchBars(ticker, '1Min', { limit: 200 }); // derive prices from bars if available (prefer freshest closes) if (recentBars && recentBars.length) { prices = recentBars.map((b: any) => (typeof b.ClosePrice === 'number' ? b.ClosePrice : (typeof b.c === 'number' ? b.c : 0))).filter((p: number) => p > 0); } } catch (barErr) { console.warn('[analyze] Failed to fetch recent bars for deterministic execution plan:', barErr); } } catch (e) { console.error("[analyze] Failed to fetch Alpaca data before analysis:", e); return Response.json({ error: "Failed to fetch Alpaca data: " + String(e) }, { status: 502 }); } const input = { financialData: `Financial data for ${ticker} as of ${date}`, technicalData: { prices, bars: recentBars, sma: 0, ema: 0, rsi: 0, macd: 0, }, sentimentData: { headlines: [`${ticker} showing positive momentum`], source: "news" as const, }, account, }; try { console.log("[analyze] Running trading graph..."); if (body.background) { // Enqueue background analyze job and return 202 immediately try { const { enqueueAnalyze } = await import("../../lib/queue"); const jobId = await enqueueAnalyze(ticker, input); return Response.json({ status: "queued", jobId }, { status: 202 }); } catch (enqueueErr) { console.error("[analyze] enqueue error:", enqueueErr); return Response.json({ error: "failed to enqueue" }, { status: 500 }); } } let decision = await graph.propagate(ticker, input); // Enrich executionPlan deterministically on server-side try { const { enrichExecutionPlan, verifyExecutionPlanWithLLM } = await import("../../lib/execution"); decision = enrichExecutionPlan(decision, input); // Optionally ask LLM to verify/adjust the computed plan if API key is present if (process.env.OPENROUTER_API_KEY) { try { decision = await verifyExecutionPlanWithLLM(decision, input, config.model); } catch (e) { console.warn("LLM verification failed:", e); } } } catch (e) { console.warn("Failed to enrich execution plan:", e); } // Avoid logging potentially verbose debate rounds to server CLI try { const { debateRounds, ...decisionSafe } = decision as any; console.log("[analyze] Decision received (debate redacted):", JSON.stringify(decisionSafe)); } catch (e) { console.log("[analyze] Decision received"); } return Response.json(decision); } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; console.error("[analyze] Error:", error); return Response.json({ error: message }, { status: 500 }); } }