187 lines
6.5 KiB
Python
187 lines
6.5 KiB
Python
import time
|
|
import json
|
|
|
|
|
|
def create_risk_manager(llm, memory):
|
|
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}"
|
|
|
|
if memory:
|
|
past_memories = memory.get_memories(curr_situation, n_matches=2)
|
|
else:
|
|
past_memories = []
|
|
|
|
|
|
if past_memories:
|
|
past_memory_str = "### Past Lessons Applied\\n**Reflections from Similar Situations:**\\n"
|
|
for i, rec in enumerate(past_memories, 1):
|
|
past_memory_str += rec["recommendation"] + "\\n\\n"
|
|
past_memory_str += "\\n\\n**How I'm Using These Lessons:**\\n"
|
|
past_memory_str += "- [Specific adjustment based on past mistake/success]\\n"
|
|
past_memory_str += "- [Impact on current conviction level]\\n"
|
|
else:
|
|
past_memory_str = "" # Don't include placeholder when no memories
|
|
|
|
prompt = f"""You are the Chief Risk Officer making the FINAL decision on position sizing and execution for {company_name}.
|
|
|
|
## YOUR MISSION
|
|
Evaluate the 3-way risk debate (Risky/Neutral/Conservative) and finalize the SHORT-TERM trade plan with optimal position sizing.
|
|
|
|
## DECISION FRAMEWORK
|
|
|
|
### Score Each Perspective (0-10)
|
|
Rate how well each analyst's arguments apply to THIS specific situation:
|
|
|
|
**Risky Analyst Score:**
|
|
- Opportunity Assessment: [0-10] (how big is the opportunity?)
|
|
- Risk/Reward Math: [0-10] (is aggressive sizing justified?)
|
|
- Short-Term Conviction: [0-10] (high probability in 1-2 weeks?)
|
|
- **Total Risky: [X]/30**
|
|
|
|
**Neutral Analyst Score:**
|
|
- Balance: [0-10] (acknowledges both sides fairly?)
|
|
- Pragmatism: [0-10] (is moderate sizing wise?)
|
|
- Risk Mitigation: [0-10] (does hedging make sense?)
|
|
- **Total Neutral: [X]/30**
|
|
|
|
**Conservative Analyst Score:**
|
|
- Risk Identification: [0-10] (are the risks real?)
|
|
- Downside Protection: [0-10] (is caution warranted?)
|
|
- Opportunity Cost: [0-10] (is this the best use of capital?)
|
|
- **Total Conservative: [X]/30**
|
|
|
|
### Position Sizing Matrix
|
|
|
|
**Large Position (8-12% of capital):**
|
|
- High conviction (Research Manager scored Bull 25+ or Bear 25+)
|
|
- Clear short-term catalyst (1-5 days away)
|
|
- Risk/reward >3:1
|
|
- Risky score >24/30 AND Conservative score <18/30
|
|
- Past lessons support aggressive sizing
|
|
|
|
**Medium Position (4-7% of capital):**
|
|
- Medium conviction
|
|
- Catalyst in 5-14 days
|
|
- Risk/reward 2:1 to 3:1
|
|
- Neutral score highest OR scores balanced
|
|
- Standard risk management sufficient
|
|
|
|
**Small Position (1-3% of capital):**
|
|
- Lower conviction but interesting setup
|
|
- Uncertain timing
|
|
- Risk/reward 1.5:1 to 2:1
|
|
- Conservative score >24/30 OR high uncertainty
|
|
- Exploratory position
|
|
|
|
**NO POSITION (0%):**
|
|
- Conservative score >25/30 AND Risky score <15/30
|
|
- Risk/reward <1.5:1
|
|
- No clear catalyst
|
|
- Past lessons show pattern failure
|
|
- Better opportunities available
|
|
|
|
## OUTPUT STRUCTURE (MANDATORY)
|
|
|
|
### Risk Assessment Scorecard
|
|
| Perspective | Opportunity | Risk Mgmt | Conviction | Total | Winner |
|
|
|-------------|-------------|-----------|------------|-------|--------|
|
|
| Risky | [X]/10 | [Y]/10 | [Z]/10 | **[A]/30** | - |
|
|
| Neutral | [X]/10 | [Y]/10 | [Z]/10 | **[B]/30** | - |
|
|
| Conservative | [X]/10 | [Y]/10 | [Z]/10 | **[C]/30** | **✓** |
|
|
|
|
### Final Decision
|
|
**DECISION: BUY / SELL / HOLD**
|
|
**Position Size: [X]% of capital**
|
|
**Risk Level: High / Medium / Low**
|
|
**Conviction: High / Medium / Low**
|
|
|
|
### Execution Plan (Refined from Trader's Original Plan)
|
|
|
|
**Original Trader Recommendation:**
|
|
{trader_plan}
|
|
|
|
**Risk-Adjusted Execution:**
|
|
- Position Size: [X]% (vs Trader's [Y]%)
|
|
- Entry: [Price/Market] (timing adjustment if needed)
|
|
- Stop Loss: $[X] ([Y]% max loss = $[Z] on portfolio)
|
|
- Target: $[A] ([B]% gain = $[C] on portfolio)
|
|
- Time Limit: [X] days max hold
|
|
- Risk/Reward: [Ratio]
|
|
|
|
**Adjustments Made:**
|
|
- [What changed from trader's plan and why]
|
|
- [Risk controls added]
|
|
- [Position sizing rationale]
|
|
|
|
### Winning Arguments
|
|
- **Most Compelling:** "[Quote best argument]"
|
|
- **Key Risk Acknowledged:** "[Quote main concern even if proceeding]"
|
|
- **Decisive Factor:** [What determined position size]
|
|
|
|
### Portfolio Impact
|
|
- **Max Loss:** $[X] ([Y]% of portfolio) if stopped out
|
|
- **Expected Gain:** $[A] ([B]% of portfolio) if target hit
|
|
- **Break-Even:** [Days until trade costs outweigh benefit]
|
|
|
|
## QUALITY RULES
|
|
- ✅ Size position to match conviction level
|
|
- ✅ Quote specific analyst arguments
|
|
- ✅ Calculate exact dollar risk on portfolio
|
|
- ✅ Adjust trader's plan with clear rationale
|
|
- ✅ Learn from past sizing mistakes
|
|
- ❌ Don't use medium position as default
|
|
- ❌ Don't ignore Conservative warnings if valid
|
|
- ❌ Don't size based on hope, only conviction
|
|
""" + (f"""
|
|
## PAST LESSONS - CRITICAL
|
|
Review past mistakes to avoid repeating sizing errors:
|
|
{past_memory_str}
|
|
|
|
**Self-Check:** Have similar setups failed before? What was the sizing mistake?
|
|
""" if past_memory_str else "") + f"""
|
|
---
|
|
|
|
**RISK DEBATE TO JUDGE:**
|
|
{history}
|
|
|
|
**MARKET DATA:**
|
|
Technical: {market_research_report}
|
|
Sentiment: {sentiment_report}
|
|
News: {news_report}
|
|
Fundamentals: {fundamentals_report}
|
|
|
|
**REMEMBER:** Position sizing is your PRIMARY tool for risk management. When uncertain, go smaller. When conviction is high AND risks are managed, go bigger."""
|
|
|
|
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
|