86 lines
5.3 KiB
Python
86 lines
5.3 KiB
Python
def create_risk_manager(llm, memory, config):
|
||
"""Create the risk manager 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 risk_manager_node(state) -> dict:
|
||
company_name = state["company_of_interest"]
|
||
|
||
history = state["risk_debate_state"]["history"]
|
||
risk_debate_state = state["risk_debate_state"]
|
||
market_research_report = state["market_report"]
|
||
news_report = state["news_report"]
|
||
fundamentals_report = state["news_report"]
|
||
sentiment_report = state["sentiment_report"]
|
||
trader_plan = state["investment_plan"]
|
||
|
||
curr_situation = f"{market_research_report}\n\n{sentiment_report}\n\n{news_report}\n\n{fundamentals_report}"
|
||
past_memories = memory.get_memories(curr_situation, n_matches=2)
|
||
|
||
past_memory_str = ""
|
||
for i, rec in enumerate(past_memories, 1):
|
||
past_memory_str += rec["recommendation"] + "\n\n"
|
||
|
||
prompt = f"""
|
||
Acting as the Risk Management Judge and Debate Facilitator, evaluate the debate between the three risk analysts—Risky, Neutral, and Safe/Conservative—and produce a single, decisive recommendation: Buy, Sell, or Hold.
|
||
Do not select Hold as a compromise; choose Hold only when key uncertainties are imminent, material, and make the risk-reward effectively flat with no near-term disambiguation.
|
||
|
||
Guidelines for decision-making:
|
||
1. Summarize key arguments:
|
||
- Extract the strongest points from each analyst that directly affect risk exposure, drawdown potential, probability of loss, liquidity, and path dependency.
|
||
- Distinguish facts, assumptions, and model-based inferences. Note time sensitivity and how each point could be falsified or confirmed.
|
||
2. Provide rationale with anchored evidence:
|
||
- Support the recommendation using specific arguments from the debate; when relevant, reference exact claims to show how they alter downside tails, variance, and left-skew risk.
|
||
- Explicitly state what observations would reverse the stance (clear invalidation conditions).
|
||
3. Refine the trader’s plan:
|
||
- Start from the trader’s original plan: {trader_plan}
|
||
- Adjust entries/exits, sizing logic, and guardrails to ensure downside protection under adverse scenarios presented in the debate.
|
||
4. Learn from past mistakes:
|
||
- Use lessons from: {past_memory_str}
|
||
- Translate reflections into concrete guardrails (e.g., two-source confirmation for critical inputs, maximum single-trade loss cap, pre-specified stop discipline) and state exactly how they apply now to avoid a wrong BUY/SELL/HOLD call.
|
||
|
||
Deliverables:
|
||
- Recommendation (BUY / SELL / HOLD) placed at the very beginning of the response.
|
||
- Risk-based rationale: concise, evidence-weighted explanation tying the decision to the debate’s most decision-relevant points and their impact on loss distribution.
|
||
- Risk plan adjustments:
|
||
- Entry/exit conditions and invalidation levels.
|
||
- Position sizing rules linked to volatility or risk budget (e.g., reduce sizing when uncertainty widens or catalysts cluster).
|
||
- Drawdown guardrails (max loss per position/session), and contingency actions under gap risk or liquidity deterioration.
|
||
- Monitoring checklist: list the top triggers and early-warning indicators that would prompt de-risking or re-assessment.
|
||
|
||
Analysts Debate History:
|
||
{history}
|
||
|
||
Output style:
|
||
Focus on actionable risk insights and continuous improvement. Be specific, testable, and time-aware. Avoid generic phrasing; each claim should have a verifying observation, trigger, or control action.
|
||
|
||
Output language: ***{language_prompt}***
|
||
"""
|
||
|
||
response = llm.invoke(prompt)
|
||
|
||
new_risk_debate_state = {
|
||
"judge_decision": response.content,
|
||
"history": risk_debate_state["history"],
|
||
"risky_history": risk_debate_state["risky_history"],
|
||
"safe_history": risk_debate_state["safe_history"],
|
||
"neutral_history": risk_debate_state["neutral_history"],
|
||
"latest_speaker": "Judge",
|
||
"current_risky_response": risk_debate_state["current_risky_response"],
|
||
"current_safe_response": risk_debate_state["current_safe_response"],
|
||
"current_neutral_response": risk_debate_state["current_neutral_response"],
|
||
"count": risk_debate_state["count"],
|
||
}
|
||
|
||
return {
|
||
"risk_debate_state": new_risk_debate_state,
|
||
"final_trade_decision": response.content,
|
||
}
|
||
|
||
return risk_manager_node
|