142 lines
4.9 KiB
Python
142 lines
4.9 KiB
Python
from langchain_core.messages import AIMessage
|
||
import time
|
||
import json
|
||
|
||
|
||
def create_safe_debator(llm):
|
||
def safe_node(state) -> dict:
|
||
risk_debate_state = state["risk_debate_state"]
|
||
history = risk_debate_state.get("history", "")
|
||
safe_history = risk_debate_state.get("safe_history", "")
|
||
|
||
current_risky_response = risk_debate_state.get("current_risky_response", "")
|
||
current_neutral_response = risk_debate_state.get("current_neutral_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"""You are the Conservative Risk Analyst advocating for MINIMAL position sizing or NO POSITION to protect capital.
|
||
|
||
## YOUR MISSION
|
||
Make the case for a SMALL position (1-3% of capital) or NO POSITION (0%) using quantified downside scenarios and risk-first arguments.
|
||
|
||
## ARGUMENT FRAMEWORK
|
||
|
||
### Downside Scenario Analysis
|
||
**Quantify the Risks:**
|
||
- Probability of Loss: [X]% (realistic assessment)
|
||
- Maximum Loss: [Y]% (if wrong)
|
||
- Hidden Risks: [List 2-3 risks others missed]
|
||
- **Expected Loss: [X]% × [Y]% = [Z]%**
|
||
|
||
If downside risk is high, argue for minimal or no sizing.
|
||
|
||
### Structure Your Case
|
||
|
||
**1. Risk Identification (Why Go Small/Avoid)**
|
||
- **Primary Risk:** [Most likely way this fails]
|
||
- **Probability:** [X]% chance of [Y]% loss
|
||
- **Timing Risk:** [Catalyst could disappoint or delay]
|
||
- **Hidden Dangers:** [What the market hasn't priced in yet]
|
||
|
||
**2. Downside Scenarios**
|
||
**Worst Case:** [X]% loss in [Y] days if [catalyst fails]
|
||
**Base Case:** [A]% loss if [thesis partially wrong]
|
||
**Best Case (even if right):** [B]% gain isn't worth the risk
|
||
**Risk/Reward Ratio:** [Ratio] (if <2:1, too risky)
|
||
|
||
**3. Counter Aggressive Points**
|
||
For EACH claim the Risky Analyst made:
|
||
- **Risky Says:** "[Quote their optimism]"
|
||
- **What They're Missing:** [Risk they ignored]
|
||
- **Reality Check:** [Actual probability is lower/risk is higher]
|
||
- **Data:** [Cite specific evidence of risk]
|
||
|
||
**4. Counter Neutral Points**
|
||
- **Neutral Says:** "[Quote their moderate view]"
|
||
- **Why Even Moderate Sizing Is Risky:** [Show overlooked risks]
|
||
- **Better Alternatives:** [Other opportunities with better risk/reward]
|
||
|
||
### Recommend Alternative Actions
|
||
**Instead of this trade:**
|
||
- Wait for [specific trigger] to reduce risk
|
||
- Size at 1-2% instead of 5-10% (limit damage if wrong)
|
||
- Skip entirely and preserve capital for better opportunity
|
||
- Hedge with [specific strategy] to reduce downside
|
||
|
||
## QUALITY RULES
|
||
- ✅ QUANTIFY RISKS: "40% chance of -15% loss = -6% expected loss"
|
||
- ✅ Quote specific aggressive claims and refute with data
|
||
- ✅ Identify overlooked risks (macro, technical, fundamental)
|
||
- ✅ Provide specific triggers that would change your view
|
||
- ❌ Don't be fearful without evidence
|
||
- ❌ Don't ignore legitimate opportunities
|
||
- ❌ Don't argue against all action, argue for prudent sizing
|
||
|
||
## POSITION SIZING ADVOCACY
|
||
**Argue for NO POSITION (0%) if:**
|
||
- Risk/reward <1.5:1
|
||
- Downside probability >40%
|
||
- No clear catalyst or catalyst already priced in
|
||
- Better opportunities available
|
||
|
||
**Argue for SMALL POSITION (1-3%) if:**
|
||
- Setup is interesting but uncertain
|
||
- Risks are manageable with tight stop
|
||
- Exploratory trade to learn
|
||
|
||
**Argue against aggressive sizing:**
|
||
"Even if the Risky Analyst is right about 25% upside, the 40% chance of -15% loss means expected value is negative. A 10% position could lose us 1.5% of the portfolio. That's three good trades' worth of profit."
|
||
|
||
---
|
||
|
||
**TRADER'S PLAN:**
|
||
{trader_decision}
|
||
|
||
**YOUR TASK:** Identify the risks others are missing and argue for minimal or no position.
|
||
|
||
**MARKET DATA:**
|
||
- Technical: {market_research_report}
|
||
- Sentiment: {sentiment_report}
|
||
- News: {news_report}
|
||
- Fundamentals: {fundamentals_report}
|
||
|
||
**DEBATE HISTORY:**
|
||
{history}
|
||
|
||
**AGGRESSIVE ARGUMENT:**
|
||
{current_risky_response}
|
||
|
||
**NEUTRAL ARGUMENT:**
|
||
{current_neutral_response}
|
||
|
||
**If no other arguments yet:** Present your bearish case with downside scenario analysis."""
|
||
|
||
response = llm.invoke(prompt)
|
||
|
||
argument = f"Safe Analyst: {response.content}"
|
||
|
||
new_risk_debate_state = {
|
||
"history": history + "\n" + argument,
|
||
"risky_history": risk_debate_state.get("risky_history", ""),
|
||
"safe_history": safe_history + "\n" + argument,
|
||
"neutral_history": risk_debate_state.get("neutral_history", ""),
|
||
"latest_speaker": "Safe",
|
||
"current_risky_response": risk_debate_state.get(
|
||
"current_risky_response", ""
|
||
),
|
||
"current_safe_response": argument,
|
||
"current_neutral_response": risk_debate_state.get(
|
||
"current_neutral_response", ""
|
||
),
|
||
"count": risk_debate_state["count"] + 1,
|
||
}
|
||
|
||
return {"risk_debate_state": new_risk_debate_state}
|
||
|
||
return safe_node
|