mirror of
https://github.com/farcasclaudiu/TradingAgents.git
synced 2026-06-22 07:01:21 +03:00
0fda24515f
Three related changes that take the rating pipeline from heuristic-only to type-safe at the source. 1) Research Manager prompt now uses the same 5-tier scale (Buy / Overweight / Hold / Underweight / Sell) as the Portfolio Manager, signal_processing, and the memory log. The prior 3-tier wording (Buy / Sell / Hold) was the only remaining inconsistency in the pipeline. 2) Centralise the 5-tier vocabulary and the heuristic prose-rating parser into tradingagents/agents/utils/rating.py. Both the memory log and the signal processor now share the same parser instead of duplicating regex and word-walker logic. 3) Make structured output a first-class part of the Portfolio Manager's primary call. The PM uses llm.with_structured_output(PortfolioDecision) so each provider's native structured-output mode (json_schema for OpenAI/xAI, response_schema for Gemini, tool-use for Anthropic, function_calling for OpenAI-compatible providers) yields a typed Pydantic instance directly. A render helper turns that instance back into the same markdown shape downstream consumers (memory log, CLI display, saved reports) already expect, so no other code has to know the PM now produces structured output. Providers without structured support fall back gracefully to free-text + the deterministic heuristic. The previous SignalProcessor had been making a second LLM call to re-extract the rating from the PM's prose; that round-trip is now eliminated. SignalProcessor is a thin adapter over parse_rating(), makes zero LLM calls, and stays for backwards compatibility with process_signal() callers. Schema (PortfolioDecision) captures rating + executive_summary + investment_thesis + optional price_target + time_horizon, with field descriptions doubling as output instructions. Agent prose remains the primary artifact; structured output is layered onto the PM only because it is the one agent whose output has machine-readable downstream consumers. 15 new tests cover the heuristic parser (markdown-bold edge cases that had no coverage before), the structured PM happy path, the free-text fallback path, and that SignalProcessor never invokes the LLM. Full suite: 77 tests pass in ~2s without API keys.
55 lines
2.3 KiB
Python
55 lines
2.3 KiB
Python
|
|
from tradingagents.agents.utils.agent_utils import build_instrument_context
|
|
|
|
|
|
def create_research_manager(llm):
|
|
def research_manager_node(state) -> dict:
|
|
instrument_context = build_instrument_context(state["company_of_interest"])
|
|
history = state["investment_debate_state"].get("history", "")
|
|
|
|
investment_debate_state = state["investment_debate_state"]
|
|
|
|
prompt = f"""As the Research Manager and debate facilitator, your role is to critically evaluate this round of debate and deliver a clear, actionable investment plan for the trader.
|
|
|
|
{instrument_context}
|
|
|
|
---
|
|
|
|
**Rating Scale** (use exactly one):
|
|
- **Buy**: Strong conviction in the bull thesis; recommend taking or growing the position
|
|
- **Overweight**: Constructive view; recommend gradually increasing exposure
|
|
- **Hold**: Balanced view; recommend maintaining the current position
|
|
- **Underweight**: Cautious view; recommend trimming exposure
|
|
- **Sell**: Strong conviction in the bear thesis; recommend exiting or avoiding the position
|
|
|
|
Commit to a clear stance whenever the debate's strongest arguments warrant one; reserve Hold for situations where the evidence on both sides is genuinely balanced.
|
|
|
|
**Required Output Structure:**
|
|
1. **Recommendation**: State one of Buy / Overweight / Hold / Underweight / Sell.
|
|
2. **Rationale**: Summarise the key points from both sides and explain which arguments led to this recommendation.
|
|
3. **Strategic Actions**: Concrete steps for the trader to implement the recommendation, including position sizing guidance consistent with the rating.
|
|
|
|
Present your analysis conversationally, as if speaking naturally to a teammate.
|
|
|
|
---
|
|
|
|
**Debate History:**
|
|
{history}"""
|
|
response = llm.invoke(prompt)
|
|
|
|
new_investment_debate_state = {
|
|
"judge_decision": response.content,
|
|
"history": investment_debate_state.get("history", ""),
|
|
"bear_history": investment_debate_state.get("bear_history", ""),
|
|
"bull_history": investment_debate_state.get("bull_history", ""),
|
|
"current_response": response.content,
|
|
"count": investment_debate_state["count"],
|
|
}
|
|
|
|
return {
|
|
"investment_debate_state": new_investment_debate_state,
|
|
"investment_plan": response.content,
|
|
}
|
|
|
|
return research_manager_node
|