164 lines
6.4 KiB
Python
164 lines
6.4 KiB
Python
"""
|
|
Enhanced Conditional Logic with Rejection Loops
|
|
|
|
Adds backward edges to send proposals back to agents if they fail validation.
|
|
"""
|
|
|
|
from tradingagents.agents.utils.agent_states import AgentState
|
|
|
|
|
|
class EnhancedConditionalLogic:
|
|
"""Handles conditional logic with rejection loops and quality checks."""
|
|
|
|
def __init__(self, max_debate_rounds=1, max_risk_discuss_rounds=1):
|
|
"""Initialize with configuration parameters."""
|
|
self.max_debate_rounds = max_debate_rounds
|
|
self.max_risk_discuss_rounds = max_risk_discuss_rounds
|
|
|
|
# ... (keep existing analyst conditional methods) ...
|
|
|
|
def should_continue_debate_with_validation(self, state: AgentState) -> str:
|
|
"""
|
|
Determine if debate should continue WITH QUALITY CHECKS.
|
|
|
|
This replaces the naive round-robin with actual validation.
|
|
"""
|
|
debate_state = state["investment_debate_state"]
|
|
|
|
# Check 1: Was last argument fact-checked and rejected?
|
|
if debate_state.get("last_argument_invalid", False):
|
|
# Send back to same agent to revise
|
|
print(f"❌ REJECTED: {debate_state.get('rejection_reason', 'Invalid argument')}")
|
|
print(f" Sending back to {debate_state['latest_speaker']} for revision")
|
|
|
|
# Route back to the agent that made the bad argument
|
|
if debate_state["latest_speaker"] == "Bull":
|
|
return "Bull Researcher"
|
|
else:
|
|
return "Bear Researcher"
|
|
|
|
# Check 2: Has consensus been reached?
|
|
if debate_state.get("consensus_reached", False):
|
|
print("✅ CONSENSUS REACHED: Proceeding to Research Manager")
|
|
return "Research Manager"
|
|
|
|
# Check 3: Max rounds exceeded
|
|
if debate_state["count"] >= 2 * self.max_debate_rounds:
|
|
print(f"⏱️ MAX ROUNDS REACHED: {debate_state['count']} rounds")
|
|
return "Research Manager"
|
|
|
|
# Check 4: Confidence too low (force another round)
|
|
if debate_state.get("confidence", 1.0) < 0.5:
|
|
print(f"⚠️ LOW CONFIDENCE ({debate_state['confidence']:.1%}): Continuing debate")
|
|
# Continue round-robin
|
|
if debate_state["current_response"].startswith("Bull"):
|
|
return "Bear Researcher"
|
|
return "Bull Researcher"
|
|
|
|
# Default: Round-robin
|
|
if debate_state["current_response"].startswith("Bull"):
|
|
return "Bear Researcher"
|
|
return "Bull Researcher"
|
|
|
|
def should_proceed_after_risk_gate(self, state: AgentState) -> str:
|
|
"""
|
|
Determine next step after deterministic risk gate validation.
|
|
|
|
This is a NEW node that checks mathematical risk validation.
|
|
"""
|
|
risk_validation = state.get("risk_gate_result", {})
|
|
|
|
# Check 1: Was trade rejected by risk gate?
|
|
if not risk_validation.get("approved", False):
|
|
rejection_reason = risk_validation.get("rejection_reason", "Unknown")
|
|
|
|
# Determine severity
|
|
if "CIRCUIT BREAKER" in rejection_reason:
|
|
# Critical failure - halt trading
|
|
print(f"🚨 CIRCUIT BREAKER TRIGGERED: {rejection_reason}")
|
|
return "END"
|
|
|
|
elif "DATA QUALITY" in rejection_reason:
|
|
# Data issue - send back to analysts
|
|
print(f"📊 DATA QUALITY FAILURE: {rejection_reason}")
|
|
print(" Routing back to Market Analyst for data refresh")
|
|
return "Market Analyst"
|
|
|
|
elif "PORTFOLIO HEAT" in rejection_reason or "POSITION RISK" in rejection_reason:
|
|
# Risk limit exceeded - send to Risk Manager for review
|
|
print(f"⚠️ RISK LIMIT EXCEEDED: {rejection_reason}")
|
|
print(" Routing to Risk Manager for position adjustment")
|
|
return "Risk Manager Revision"
|
|
|
|
else:
|
|
# Generic rejection - log and hold
|
|
print(f"❌ TRADE REJECTED: {rejection_reason}")
|
|
return "END"
|
|
|
|
# Check 2: Was position size overridden?
|
|
if risk_validation.get("override_message"):
|
|
print(f"🔧 {risk_validation['override_message']}")
|
|
|
|
# Approved - proceed to execution
|
|
print("✅ RISK GATE PASSED: Trade approved")
|
|
return "Execute Trade"
|
|
|
|
def should_continue_risk_analysis_with_validation(self, state: AgentState) -> str:
|
|
"""
|
|
Enhanced risk analysis routing with validation.
|
|
"""
|
|
risk_state = state["risk_debate_state"]
|
|
|
|
# Check 1: Did any analyst provide mathematically invalid reasoning?
|
|
if risk_state.get("invalid_reasoning_detected", False):
|
|
# Send back to the analyst who made the error
|
|
print(f"❌ INVALID REASONING: {risk_state.get('error_message', '')}")
|
|
return risk_state["latest_speaker"]
|
|
|
|
# Check 2: Max rounds
|
|
if risk_state["count"] >= 3 * self.max_risk_discuss_rounds:
|
|
return "Deterministic Risk Gate" # NEW: Route to math validation
|
|
|
|
# Round-robin
|
|
if risk_state["latest_speaker"].startswith("Risky"):
|
|
return "Safe Analyst"
|
|
if risk_state["latest_speaker"].startswith("Safe"):
|
|
return "Neutral Analyst"
|
|
return "Risky Analyst"
|
|
|
|
|
|
# Integration example for trading_graph.py
|
|
"""
|
|
To integrate this into your graph:
|
|
|
|
1. Add the Deterministic Risk Gate node:
|
|
workflow.add_node("Deterministic Risk Gate", deterministic_risk_gate_node)
|
|
|
|
2. Replace the edge from "Risk Judge" to END:
|
|
# OLD:
|
|
workflow.add_edge("Risk Judge", END)
|
|
|
|
# NEW:
|
|
workflow.add_conditional_edges(
|
|
"Risk Judge",
|
|
enhanced_logic.should_proceed_after_risk_gate,
|
|
{
|
|
"END": END,
|
|
"Market Analyst": "Market Analyst", # Data quality failure
|
|
"Risk Manager Revision": "Risk Manager Revision", # Risk limit exceeded
|
|
"Execute Trade": "Execute Trade" # Approved
|
|
}
|
|
)
|
|
|
|
3. Add backward edge for debate rejection:
|
|
workflow.add_conditional_edges(
|
|
"Bull Researcher",
|
|
enhanced_logic.should_continue_debate_with_validation,
|
|
{
|
|
"Bear Researcher": "Bear Researcher",
|
|
"Bull Researcher": "Bull Researcher", # NEW: Rejection loop
|
|
"Research Manager": "Research Manager",
|
|
}
|
|
)
|
|
"""
|