This commit is contained in:
MarkLo 2025-11-20 22:36:58 +08:00
parent 75a94c421b
commit 0a203fa475
5 changed files with 175 additions and 78 deletions

View File

@ -47,40 +47,26 @@ async def run_analysis(
service: TradingService = Depends(get_trading_service), 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 Requires OpenAI API key to be provided in the request.
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.
""" """
logger.info(f"Received analysis request for {request.ticker} on {request.analysis_date}") logger.info(f"Received analysis request for {request.ticker} on {request.analysis_date}")
try: # Run analysis with all provided parameters including API keys
# Run analysis result = await trading_service.run_analysis(
result = await service.run_analysis( ticker=request.ticker,
ticker=request.ticker, analysis_date=request.analysis_date,
analysis_date=request.analysis_date, openai_api_key=request.openai_api_key,
analysts=request.analysts, openai_base_url=request.openai_base_url,
research_depth=request.research_depth, alpha_vantage_api_key=request.alpha_vantage_api_key,
deep_think_llm=request.deep_think_llm, analysts=request.analysts,
quick_think_llm=request.quick_think_llm, research_depth=request.research_depth,
) deep_think_llm=request.deep_think_llm,
quick_think_llm=request.quick_think_llm,
# Return response )
return AnalysisResponse(**result)
return 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)}")
@router.get("/tickers") @router.get("/tickers")

View File

@ -17,6 +17,17 @@ class AnalysisRequest(BaseModel):
research_depth: Optional[int] = Field(default=1, ge=1, le=5, description="Research depth (1-5)") 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") 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") 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): class AnalysisResponse(BaseModel):

View File

@ -42,17 +42,23 @@ class TradingService:
self, self,
ticker: str, ticker: str,
analysis_date: 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, analysts: Optional[List[str]] = None,
research_depth: int = 1, research_depth: int = 1,
deep_think_llm: str = "gpt-4o-mini", deep_think_llm: str = "gpt-4o-mini",
quick_think_llm: str = "gpt-4o-mini", quick_think_llm: str = "gpt-4o-mini",
) -> Dict[str, Any]: ) -> 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: Args:
ticker: Stock ticker symbol ticker: Stock ticker symbol
analysis_date: Date in YYYY-MM-DD format 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 analysts: List of analyst types to include
research_depth: Research depth (1-5) research_depth: Research depth (1-5)
deep_think_llm: Deep thinking LLM model deep_think_llm: Deep thinking LLM model
@ -66,37 +72,64 @@ class TradingService:
if analysts is None: if analysts is None:
analysts = ["market", "sentiment", "news", "fundamentals"] analysts = ["market", "sentiment", "news", "fundamentals"]
# Create configuration # Dynamically set environment variables for this request
config = self.create_config(research_depth, deep_think_llm, quick_think_llm) import os
original_openai_key = os.environ.get("OPENAI_API_KEY")
original_alpha_key = os.environ.get("ALPHA_VANTAGE_API_KEY")
# Initialize TradingAgents graph try:
logger.info(f"Initializing TradingAgents for {ticker} on {analysis_date}") # Set API keys for this request
graph = TradingAgentsGraph(analysts, config=config, debug=True) 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 # Extract reports from final state
logger.info(f"Running analysis for {ticker}") reports = {
final_state, decision = graph.propagate(ticker, analysis_date) "market_report": final_state.get("market_report"),
"sentiment_report": final_state.get("sentiment_report"),
# Extract reports from final state "news_report": final_state.get("news_report"),
reports = { "fundamentals_report": final_state.get("fundamentals_report"),
"market_report": final_state.get("market_report"), "investment_plan": final_state.get("investment_plan"),
"sentiment_report": final_state.get("sentiment_report"), "trader_investment_plan": final_state.get("trader_investment_plan"),
"news_report": final_state.get("news_report"), "final_trade_decision": final_state.get("final_trade_decision"),
"fundamentals_report": final_state.get("fundamentals_report"), "investment_debate_state": final_state.get("investment_debate_state"),
"investment_plan": final_state.get("investment_plan"), "risk_debate_state": final_state.get("risk_debate_state"),
"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"), return {
"risk_debate_state": final_state.get("risk_debate_state"), "status": "success",
} "ticker": ticker,
"analysis_date": analysis_date,
return { "decision": decision,
"status": "success", "reports": reports,
"ticker": ticker, }
"analysis_date": analysis_date,
"decision": decision, finally:
"reports": reports, # 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: except Exception as e:
logger.error(f"Analysis failed for {ticker}: {str(e)}", exc_info=True) logger.error(f"Analysis failed for {ticker}: {str(e)}", exc_info=True)
@ -111,23 +144,17 @@ class TradingService:
"""Get list of available analyst types""" """Get list of available analyst types"""
return ["market", "sentiment", "news", "fundamentals"] return ["market", "sentiment", "news", "fundamentals"]
def get_available_llms(self) -> Dict[str, List[str]]: def get_available_llms(self) -> List[str]:
"""Get list of available LLM models by provider""" """Get list of available OpenAI LLM models"""
return { return [
"openai": [ "gpt-5.1-2025-11-13",
"gpt-4o", "gpt-5-mini-2025-08-07",
"gpt-4o-mini", "gpt-5-nano-2025-08-07",
"gpt-4-turbo", "gpt-4.1-mini",
"gpt-4", "gpt-4.1-nano",
"gpt-3.5-turbo", "gpt-4o",
], "gpt-4o-mini",
"anthropic": [ ]
"claude-3-5-sonnet-20241022",
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
"claude-3-haiku-20240307",
],
}
def get_default_config(self) -> Dict[str, Any]: def get_default_config(self) -> Dict[str, Any]:
"""Get default configuration""" """Get default configuration"""

View File

@ -36,6 +36,11 @@ const formSchema = z.object({
research_depth: z.number().min(1).max(5), research_depth: z.number().min(1).max(5),
deep_think_llm: z.string(), deep_think_llm: z.string(),
quick_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 { interface AnalysisFormProps {
@ -52,6 +57,9 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
research_depth: 1, research_depth: 1,
deep_think_llm: "gpt-4o-mini", deep_think_llm: "gpt-4o-mini",
quick_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) {
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent>
<SelectItem value="gpt-5.1-2025-11-13">GPT-5.1</SelectItem>
<SelectItem value="gpt-5-mini-2025-08-07">GPT-5 Mini</SelectItem>
<SelectItem value="gpt-5-nano-2025-08-07">GPT-5 Nano</SelectItem>
<SelectItem value="gpt-4.1-mini">GPT-4.1 Mini</SelectItem>
<SelectItem value="gpt-4.1-nano">GPT-4.1 Nano</SelectItem>
<SelectItem value="gpt-4o">GPT-4o</SelectItem> <SelectItem value="gpt-4o">GPT-4o</SelectItem>
<SelectItem value="gpt-4o-mini">GPT-4o Mini</SelectItem> <SelectItem value="gpt-4o-mini">GPT-4o Mini</SelectItem>
<SelectItem value="gpt-4-turbo">GPT-4 Turbo</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
@ -167,6 +179,62 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
/> />
</div> </div>
{/* API Configuration Section */}
<div className="space-y-4 border-t pt-6 mt-6">
<h3 className="text-lg font-semibold">API </h3>
<FormField
control={form.control}
name="openai_api_key"
render={({ field }) => (
<FormItem>
<FormLabel>OpenAI API Key</FormLabel>
<FormControl>
<Input type="password" placeholder="sk-..." {...field} />
</FormControl>
<FormDescription>
OpenAI API Key LLM
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="openai_base_url"
render={({ field }) => (
<FormItem>
<FormLabel>OpenAI Base URL</FormLabel>
<FormControl>
<Input placeholder="https://api.openai.com/v1" {...field} />
</FormControl>
<FormDescription>
API OpenAI
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="alpha_vantage_api_key"
render={({ field }) => (
<FormItem>
<FormLabel>Alpha Vantage API Key</FormLabel>
<FormControl>
<Input type="password" placeholder="選填,用於更詳細的數據" {...field} />
</FormControl>
<FormDescription>
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
<Button type="submit" className="w-full" disabled={loading} size="lg"> <Button type="submit" className="w-full" disabled={loading} size="lg">
{loading ? "執行分析中..." : "執行分析"} {loading ? "執行分析中..." : "執行分析"}
</Button> </Button>

View File

@ -9,6 +9,11 @@ export interface AnalysisRequest {
research_depth?: number; research_depth?: number;
deep_think_llm?: string; deep_think_llm?: string;
quick_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 { export interface AnalysisResponse {