TradingAgents/tradingagents/agents/risk_mgmt/neutral_debator.py

85 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

def create_neutral_debator(llm, config):
"""Create the neutral debator node with language support."""
language = config["output_language"]
language_prompts = {
"en": "",
"zh-tw": "Use Traditional Chinese as the output.",
"zh-cn": "Use Simplified Chinese as the output.",
}
language_prompt = language_prompts.get(language, "")
def neutral_node(state) -> dict:
risk_debate_state = state["risk_debate_state"]
history = risk_debate_state.get("history", "")
neutral_history = risk_debate_state.get("neutral_history", "")
current_risky_response = risk_debate_state.get("current_risky_response", "")
current_safe_response = risk_debate_state.get("current_safe_response", "")
market_research_report = state["market_report"]
sentiment_report = state["sentiment_report"]
news_report = state["news_report"]
fundamentals_report = state["fundamentals_report"]
trader_decision = state["trader_investment_plan"]
prompt = f"""
As the Neutral Risk Analyst, provide a balanced, evidence-led perspective that weighs potential benefits against risks, and proposes a moderate, sustainable adjustment to the traders plan when warranted.
Your goal is to identify the risk-reward midpoint that preserves upside while containing left-tail outcomes.
Evaluate macro context, market regime, and diversification effects without drifting into generic statements.
Anchor on the traders decision: {trader_decision}
- Assess upside pathways and downside distributions, highlighting how a balanced approach could retain key optionality while reducing variance and drawdown sensitivity.
Balanced critique framework:
- Upside validation: Identify the 23 strongest upside drivers and specify the observable confirmations (KPI, catalysts) needed to maintain or scale exposure. Explain causal links to earnings, cash flow, and valuation.
- Downside containment: Identify the 23 most material risks and translate each into a concrete control (exposure cap, pacing, invalidation condition, liquidity buffer).
- Diversification and correlation: Discuss how exposures interact with broader market factors and whether modest hedges or diversification can improve the overall risk-adjusted outcome.
Direct engagement with other viewpoints:
- Last response from the risky analyst: {current_risky_response}
- Last response from the safe analyst: {current_safe_response}
- Challenge over-optimism and over-caution by separating facts, assumptions, and model sensitivities. Where feasible, propose compromise conditions (what confirmation would validate the risky stance; what protections address the safe stances key concerns).
Traceable evidence base:
- Market Research Report: {market_research_report}
- Social Media Sentiment Report: {sentiment_report}
- Latest World Affairs Report: {news_report}
- Company Fundamentals Report: {fundamentals_report}
- Conversation history for context and citations: {history}
Neutral adjustment proposal (analytical):
- Position calibration: suggest a moderate exposure profile with clear conditions to scale up on confirmation or derisk on deterioration.
- Invalidation and reassessment: define specific triggers that warrant stance changes (e.g., KPI misses/beats, guidance shifts, spread/volatility regime changes).
- Monitoring checklist: list a concise set of indicators covering fundamentals, market/liquidity, and event risk, with a reasonable review cadence.
Communication style:
- Be conversational yet precise. Anchor each claim to a datapoint, mechanism, or catalyst from the provided materials. Avoid generic language; make the tradeoffs explicit and testable.
If opposing responses are missing, do not fabricate them; present the neutral analysis grounded in the available inputs only.
Output language: ***{language_prompt}***
"""
response = llm.invoke(prompt)
argument = f"Neutral Analyst: {response.content}"
new_risk_debate_state = {
"history": history + "\n" + argument,
"risky_history": risk_debate_state.get("risky_history", ""),
"safe_history": risk_debate_state.get("safe_history", ""),
"neutral_history": neutral_history + "\n" + argument,
"latest_speaker": "Neutral",
"current_risky_response": risk_debate_state.get(
"current_risky_response", ""
),
"current_safe_response": risk_debate_state.get("current_safe_response", ""),
"current_neutral_response": argument,
"count": risk_debate_state["count"] + 1,
}
return {"risk_debate_state": new_risk_debate_state}
return neutral_node