From 0a203fa475e5e1ab17c4dd325a55d6646ac245d0 Mon Sep 17 00:00:00 2001 From: MarkLo Date: Thu, 20 Nov 2025 22:36:58 +0800 Subject: [PATCH] --- backend/app/api/routes.py | 46 +++---- backend/app/models/schemas.py | 11 ++ backend/app/services/trading_service.py | 121 +++++++++++------- frontend/components/analysis/AnalysisForm.tsx | 70 +++++++++- frontend/lib/types.ts | 5 + 5 files changed, 175 insertions(+), 78 deletions(-) diff --git a/backend/app/api/routes.py b/backend/app/api/routes.py index bd0cf121..a25b9042 100644 --- a/backend/app/api/routes.py +++ b/backend/app/api/routes.py @@ -47,40 +47,26 @@ async def run_analysis( service: TradingService = Depends(get_trading_service), ): """ - Run trading analysis for a given ticker and date + Run a comprehensive trading analysis for a given ticker and date. - This endpoint initiates a comprehensive trading analysis using the TradingAgents - multi-agent system. The analysis includes: - - Market technical analysis - - Sentiment analysis - - News analysis - - Fundamental analysis - - Research team debate - - Trading decision - - Risk assessment - - Portfolio management decision - - The process may take several minutes depending on the research depth. + Requires OpenAI API key to be provided in the request. """ logger.info(f"Received analysis request for {request.ticker} on {request.analysis_date}") - try: - # Run analysis - result = await service.run_analysis( - ticker=request.ticker, - analysis_date=request.analysis_date, - analysts=request.analysts, - research_depth=request.research_depth, - deep_think_llm=request.deep_think_llm, - quick_think_llm=request.quick_think_llm, - ) - - # Return response - return AnalysisResponse(**result) - - except Exception as e: - logger.error(f"Analysis failed: {str(e)}", exc_info=True) - raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}") + # Run analysis with all provided parameters including API keys + result = await trading_service.run_analysis( + ticker=request.ticker, + analysis_date=request.analysis_date, + openai_api_key=request.openai_api_key, + openai_base_url=request.openai_base_url, + alpha_vantage_api_key=request.alpha_vantage_api_key, + analysts=request.analysts, + research_depth=request.research_depth, + deep_think_llm=request.deep_think_llm, + quick_think_llm=request.quick_think_llm, + ) + + return result @router.get("/tickers") diff --git a/backend/app/models/schemas.py b/backend/app/models/schemas.py index b3830af6..9b2998cf 100644 --- a/backend/app/models/schemas.py +++ b/backend/app/models/schemas.py @@ -17,6 +17,17 @@ class AnalysisRequest(BaseModel): research_depth: Optional[int] = Field(default=1, ge=1, le=5, description="Research depth (1-5)") deep_think_llm: Optional[str] = Field(default="gpt-4o-mini", description="Deep thinking LLM model") quick_think_llm: Optional[str] = Field(default="gpt-4o-mini", description="Quick thinking LLM model") + + # API Configuration + openai_api_key: str = Field(..., description="OpenAI API Key (required)", min_length=10) + openai_base_url: Optional[str] = Field( + default="https://api.openai.com/v1", + description="OpenAI API Base URL (optional)" + ) + alpha_vantage_api_key: Optional[str] = Field( + None, + description="Alpha Vantage API Key (optional, for enhanced data)" + ) class AnalysisResponse(BaseModel): diff --git a/backend/app/services/trading_service.py b/backend/app/services/trading_service.py index dc6269d6..bfc63e9d 100644 --- a/backend/app/services/trading_service.py +++ b/backend/app/services/trading_service.py @@ -42,17 +42,23 @@ class TradingService: self, ticker: str, analysis_date: str, + openai_api_key: str, + openai_base_url: str = "https://api.openai.com/v1", + alpha_vantage_api_key: Optional[str] = None, analysts: Optional[List[str]] = None, research_depth: int = 1, deep_think_llm: str = "gpt-4o-mini", quick_think_llm: str = "gpt-4o-mini", ) -> Dict[str, Any]: """ - Run trading analysis for a given ticker and date + Run trading analysis for a given ticker and date with user-provided API keys Args: ticker: Stock ticker symbol analysis_date: Date in YYYY-MM-DD format + openai_api_key: OpenAI API Key (required) + openai_base_url: OpenAI API Base URL (optional) + alpha_vantage_api_key: Alpha Vantage API Key (optional) analysts: List of analyst types to include research_depth: Research depth (1-5) deep_think_llm: Deep thinking LLM model @@ -66,37 +72,64 @@ class TradingService: if analysts is None: analysts = ["market", "sentiment", "news", "fundamentals"] - # Create configuration - config = self.create_config(research_depth, deep_think_llm, quick_think_llm) + # Dynamically set environment variables for this request + import os + original_openai_key = os.environ.get("OPENAI_API_KEY") + original_alpha_key = os.environ.get("ALPHA_VANTAGE_API_KEY") - # Initialize TradingAgents graph - logger.info(f"Initializing TradingAgents for {ticker} on {analysis_date}") - graph = TradingAgentsGraph(analysts, config=config, debug=True) + try: + # Set API keys for this request + os.environ["OPENAI_API_KEY"] = openai_api_key + if alpha_vantage_api_key: + os.environ["ALPHA_VANTAGE_API_KEY"] = alpha_vantage_api_key + + # Create configuration + logger.info(f"Initializing TradingAgents for {ticker} on {analysis_date}") + config = self.create_config(research_depth, deep_think_llm, quick_think_llm) + + # Override with user-provided settings + config["llm_provider"] = "openai" + config["backend_url"] = openai_base_url + + # Initialize TradingAgents graph + graph = TradingAgentsGraph(analysts, config=config, debug=True) + + # Run analysis + logger.info(f"Running analysis for {ticker}") + final_state, decision = graph.propagate(ticker, analysis_date) - # Run analysis - logger.info(f"Running analysis for {ticker}") - final_state, decision = graph.propagate(ticker, analysis_date) - - # Extract reports from final state - reports = { - "market_report": final_state.get("market_report"), - "sentiment_report": final_state.get("sentiment_report"), - "news_report": final_state.get("news_report"), - "fundamentals_report": final_state.get("fundamentals_report"), - "investment_plan": final_state.get("investment_plan"), - "trader_investment_plan": final_state.get("trader_investment_plan"), - "final_trade_decision": final_state.get("final_trade_decision"), - "investment_debate_state": final_state.get("investment_debate_state"), - "risk_debate_state": final_state.get("risk_debate_state"), - } - - return { - "status": "success", - "ticker": ticker, - "analysis_date": analysis_date, - "decision": decision, - "reports": reports, - } + # Extract reports from final state + reports = { + "market_report": final_state.get("market_report"), + "sentiment_report": final_state.get("sentiment_report"), + "news_report": final_state.get("news_report"), + "fundamentals_report": final_state.get("fundamentals_report"), + "investment_plan": final_state.get("investment_plan"), + "trader_investment_plan": final_state.get("trader_investment_plan"), + "final_trade_decision": final_state.get("final_trade_decision"), + "investment_debate_state": final_state.get("investment_debate_state"), + "risk_debate_state": final_state.get("risk_debate_state"), + } + + return { + "status": "success", + "ticker": ticker, + "analysis_date": analysis_date, + "decision": decision, + "reports": reports, + } + + finally: + # Clean up environment variables after request + if original_openai_key is not None: + os.environ["OPENAI_API_KEY"] = original_openai_key + elif "OPENAI_API_KEY" in os.environ: + del os.environ["OPENAI_API_KEY"] + + if original_alpha_key is not None: + os.environ["ALPHA_VANTAGE_API_KEY"] = original_alpha_key + elif "ALPHA_VANTAGE_API_KEY" in os.environ: + del os.environ["ALPHA_VANTAGE_API_KEY"] except Exception as e: logger.error(f"Analysis failed for {ticker}: {str(e)}", exc_info=True) @@ -111,23 +144,17 @@ class TradingService: """Get list of available analyst types""" return ["market", "sentiment", "news", "fundamentals"] - def get_available_llms(self) -> Dict[str, List[str]]: - """Get list of available LLM models by provider""" - return { - "openai": [ - "gpt-4o", - "gpt-4o-mini", - "gpt-4-turbo", - "gpt-4", - "gpt-3.5-turbo", - ], - "anthropic": [ - "claude-3-5-sonnet-20241022", - "claude-3-opus-20240229", - "claude-3-sonnet-20240229", - "claude-3-haiku-20240307", - ], - } + def get_available_llms(self) -> List[str]: + """Get list of available OpenAI LLM models""" + return [ + "gpt-5.1-2025-11-13", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4o", + "gpt-4o-mini", + ] def get_default_config(self) -> Dict[str, Any]: """Get default configuration""" diff --git a/frontend/components/analysis/AnalysisForm.tsx b/frontend/components/analysis/AnalysisForm.tsx index 36b52c00..de5b4698 100644 --- a/frontend/components/analysis/AnalysisForm.tsx +++ b/frontend/components/analysis/AnalysisForm.tsx @@ -36,6 +36,11 @@ const formSchema = z.object({ research_depth: z.number().min(1).max(5), deep_think_llm: z.string(), quick_think_llm: z.string(), + + // API Configuration + openai_api_key: z.string().min(20, "請輸入有效的 OpenAI API Key"), + openai_base_url: z.string().url("請輸入有效的 URL").optional().or(z.literal("")), + alpha_vantage_api_key: z.string().optional().or(z.literal("")), }); interface AnalysisFormProps { @@ -52,6 +57,9 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { research_depth: 1, deep_think_llm: "gpt-4o-mini", quick_think_llm: "gpt-4o-mini", + openai_api_key: "", + openai_base_url: "https://api.openai.com/v1", + alpha_vantage_api_key: "", }, }); @@ -153,9 +161,13 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { + GPT-5.1 + GPT-5 Mini + GPT-5 Nano + GPT-4.1 Mini + GPT-4.1 Nano GPT-4o GPT-4o Mini - GPT-4 Turbo @@ -167,6 +179,62 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { /> + {/* API Configuration Section */} +
+

API 配置

+ + ( + + OpenAI API Key(必填) + + + + + 您的 OpenAI API Key(用於 LLM 推理) + + + + )} + /> + + ( + + OpenAI Base URL(選填) + + + + + API 基礎網址(預設為 OpenAI 官方) + + + + )} + /> + + ( + + Alpha Vantage API Key(選填) + + + + + 用於獲取更詳細的財務數據(可選) + + + + )} + /> +
+ diff --git a/frontend/lib/types.ts b/frontend/lib/types.ts index 62e34df8..6d6072a0 100644 --- a/frontend/lib/types.ts +++ b/frontend/lib/types.ts @@ -9,6 +9,11 @@ export interface AnalysisRequest { research_depth?: number; deep_think_llm?: string; quick_think_llm?: string; + + // API Configuration + openai_api_key: string; + openai_base_url?: string; + alpha_vantage_api_key?: string; } export interface AnalysisResponse {