Files
NexQuant/rdagent/components/coder/factor_coder/eurusd_debate.py
T

750 lines
24 KiB
Python
Raw Normal View History

"""
EURUSD Trading-Debatte: Bull vs Bear vs Neutral
Multi-Perspektiven-Debatte für bessere Trading-Entscheidungen:
- Bull Agent: Argumentiert für LONG EURUSD
- Bear Agent: Argumentiert für SHORT EURUSD
- Neutral Agent: Argumentiert für WAIT/Range-Trading
Jeder Agent analysiert die gleichen Daten aus seiner Perspektive.
Ein Research Manager bewertet die Debatte und trifft die finale Entscheidung.
"""
import json
import sys
from dataclasses import dataclass
2026-03-30 21:11:07 +02:00
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Literal, Optional
# Füge Parent-Directory zum Path hinzu für lokale Imports
sys.path.insert(0, str(Path(__file__).parent))
from eurusd_llm import MultiProviderLLM
2026-03-30 21:11:07 +02:00
from fx_config import get_fx_config
def get_current_session_info() -> dict:
"""
Gibt Informationen zur aktuellen FX-Session.
Returns
-------
dict
Session-Info mit Name, Stunden, Charakteristika, empfohlene Strategie
"""
config = get_fx_config()
current_session = config.get_current_session()
session_desc = config.get_session_description(current_session)
# Aktuelle UTC Zeit hinzufügen
hour_utc = datetime.now(timezone.utc).hour
return {
"session": current_session,
"name": session_desc["name"],
"hours": session_desc["hours"],
"current_utc_hour": hour_utc,
"characteristics": session_desc["characteristics"],
"recommended_strategy": session_desc["recommended_strategy"],
"avoid": session_desc["avoid"]
}
@dataclass
class TradingSignal:
"""Trading-Signal mit Details."""
action: Literal["LONG", "SHORT", "NEUTRAL"]
confidence: int # 0-100
reasoning: List[str]
entry_price: Optional[float] = None
stop_loss: Optional[float] = None
take_profit: Optional[float] = None
leverage: Optional[int] = None
class EURUSDBullAgent:
"""
Bull Agent: Argumentiert für LONG EURUSD.
Sucht nach positiven Faktoren für EUR:
- EZB hawkish (Zinserhöhungen)
- Positive Wirtschaftsdaten aus Eurozone
- USD-Schwäche (Fed dovish, schlechte US-Daten)
- Technisches Setup (Support, bullish Patterns)
- Positives Sentiment (Risk-On)
"""
def __init__(self, llm: Optional[MultiProviderLLM] = None):
self.llm = llm or MultiProviderLLM()
def analyze(self, market_data: dict) -> TradingSignal:
"""
Analysiert Marktdaten aus Bull-Perspektive.
Parameters
----------
market_data : dict
Marktdaten mit Keys:
- price: aktueller EURUSD-Preis
- hurst_regime: "MEAN_REVERSION", "NEUTRAL", "TRENDING"
- rsi: RSI-Wert
- macd: MACD-Signal
- economic_data: Wirtschaftsdaten
- sentiment: Marktstimmung
Returns
-------
TradingSignal
Bull-Signal mit LONG-Empfehlung und Confidence
"""
2026-03-30 21:11:07 +02:00
# Session-Info hinzufügen
session_info = get_current_session_info()
market_data["session"] = session_info
prompt = self._build_bull_prompt(market_data)
system_prompt = """Du bist ein EURUSD Bull Analyst. Deine Aufgabe ist es,
Argumente FÜR einen LONG EURUSD Trade zu finden.
Analysiere die Daten und finde positive Faktoren für EUR:
- EZB hawkish vs Fed dovish
- Positive Eurozone-Wirtschaftsdaten
- USD-Schwäche
- Bullische technische Signale
- Risk-On Sentiment
Antworte IMMER im JSON-Format."""
try:
response = self.llm.chat(
prompt=prompt,
system_prompt=system_prompt,
temperature=0.1,
max_tokens=500,
json_mode=True
)
result = json.loads(response["content"])
return TradingSignal(
action="LONG",
confidence=min(100, max(0, result.get("confidence", 50))),
reasoning=result.get("reasons", []),
entry_price=market_data.get("price"),
stop_loss=result.get("stop_loss"),
take_profit=result.get("take_profit"),
leverage=result.get("leverage", 20)
)
except Exception as e:
# Fallback bei Fehlern
return TradingSignal(
action="LONG",
confidence=50,
reasoning=[f"Bull-Analyse fehlgeschlagen: {str(e)}"],
entry_price=market_data.get("price")
)
def _build_bull_prompt(self, data: dict) -> str:
"""Erstellt Bull-spezifischen Prompt."""
2026-03-30 21:11:07 +02:00
session = data.get("session", {})
session_str = f"""
=== Aktuelle Session ===
- Session: {session.get('name', 'N/A')} ({session.get('hours', '')})
- Charakteristika: {session.get('characteristics', '')}
- Empfohlene Strategie: {session.get('recommended_strategy', '')}
""" if session else ""
return f"""
Analysiere EURUSD für LONG-Setup:
Aktuelle Daten:
- Preis: {data.get('price', 'N/A')}
- Hurst Regime: {data.get('hurst_regime', 'N/A')}
- RSI: {data.get('rsi', 'N/A')}
- MACD: {data.get('macd', 'N/A')}
- Wirtschaftsdaten: {data.get('economic_data', 'N/A')}
- Sentiment: {data.get('sentiment', 'N/A')}
2026-03-30 21:11:07 +02:00
{session_str}
Finde Argumente FÜR LONG EURUSD:
1. Welche positiven Faktoren für EUR siehst du?
2. Gibt es USD-Schwäche?
3. Ist das technische Setup bullisch?
2026-03-30 21:11:07 +02:00
4. Passt der Trade zur aktuellen Session?
5. Was ist das Risk/Reward?
Antworte als JSON:
{{
"confidence": 0-100,
"reasons": ["Grund 1", "Grund 2", ...],
"stop_loss": 1.0800,
"take_profit": 1.0950,
"leverage": 20
}}
"""
class EURUSDBearAgent:
"""
Bear Agent: Argumentiert für SHORT EURUSD.
Sucht nach negativen Faktoren für EUR:
- EZB dovish (Zinssenkungen)
- Negative Wirtschaftsdaten aus Eurozone
- USD-Stärke (Fed hawkish, gute US-Daten)
- Technisches Setup (Resistance, bearish Patterns)
- Negatives Sentiment (Risk-Off)
"""
def __init__(self, llm: Optional[MultiProviderLLM] = None):
self.llm = llm or MultiProviderLLM()
def analyze(self, market_data: dict) -> TradingSignal:
"""
Analysiert Marktdaten aus Bear-Perspektive.
Parameters
----------
market_data : dict
Gleiche Daten wie Bull Agent
Returns
-------
TradingSignal
Bear-Signal mit SHORT-Empfehlung und Confidence
"""
prompt = self._build_bear_prompt(market_data)
system_prompt = """Du bist ein EURUSD Bear Analyst. Deine Aufgabe ist es,
Argumente FÜR einen SHORT EURUSD Trade zu finden.
Analysiere die Daten und finde negative Faktoren für EUR:
- EZB dovish vs Fed hawkish
- Negative Eurozone-Wirtschaftsdaten
- USD-Stärke
- Bearische technische Signale
- Risk-Off Sentiment
Antworte IMMER im JSON-Format."""
try:
response = self.llm.chat(
prompt=prompt,
system_prompt=system_prompt,
temperature=0.1,
max_tokens=500,
json_mode=True
)
result = json.loads(response["content"])
return TradingSignal(
action="SHORT",
confidence=min(100, max(0, result.get("confidence", 50))),
reasoning=result.get("reasons", []),
entry_price=market_data.get("price"),
stop_loss=result.get("stop_loss"),
take_profit=result.get("take_profit"),
leverage=result.get("leverage", 20)
)
except Exception as e:
return TradingSignal(
action="SHORT",
confidence=50,
reasoning=[f"Bear-Analyse fehlgeschlagen: {str(e)}"],
entry_price=market_data.get("price")
)
def _build_bear_prompt(self, data: dict) -> str:
"""Erstellt Bear-spezifischen Prompt."""
return f"""
Analysiere EURUSD für SHORT-Setup:
Aktuelle Daten:
- Preis: {data.get('price', 'N/A')}
- Hurst Regime: {data.get('hurst_regime', 'N/A')}
- RSI: {data.get('rsi', 'N/A')}
- MACD: {data.get('macd', 'N/A')}
- Wirtschaftsdaten: {data.get('economic_data', 'N/A')}
- Sentiment: {data.get('sentiment', 'N/A')}
Finde Argumente FÜR SHORT EURUSD:
1. Welche negativen Faktoren für EUR siehst du?
2. Gibt es USD-Stärke?
3. Ist das technische Setup bearisch?
4. Was ist das Risk/Reward?
Antworte als JSON:
{{
"confidence": 0-100,
"reasons": ["Grund 1", "Grund 2", ...],
"stop_loss": 1.0950,
"take_profit": 1.0800,
"leverage": 20
}}
"""
class EURUSDNeutralAgent:
"""
Neutral Agent: Argumentiert für WAIT/Range-Trading.
Sucht nach Gründen für Abwarten:
- Unklares Marktregime (Hurst 0.4-0.6)
- Widersprüchliche Signale
- Wichtige News bevorstehend (NFP, EZB, Fed)
- Enge Range ohne klaren Ausbruch
- Zu geringes Risk/Reward
"""
def __init__(self, llm: Optional[MultiProviderLLM] = None):
self.llm = llm or MultiProviderLLM()
def analyze(self, market_data: dict) -> TradingSignal:
"""
Analysiert Marktdaten aus Neutral-Perspektive.
Parameters
----------
market_data : dict
Gleiche Daten wie andere Agenten
Returns
-------
TradingSignal
Neutral-Signal mit WAIT-Empfehlung
"""
prompt = self._build_neutral_prompt(market_data)
system_prompt = """Du bist ein EURUSD Neutral Analyst. Deine Aufgabe ist es,
Argumente für ABWARTEN oder RANGE-TRADING zu finden.
Analysiere die Daten und finde Gründe für Vorsicht:
- Unklares Marktregime
- Widersprüchliche Signale
- Wichtige News bevorstehend
- Zu geringes Risk/Reward
- Choppy Market
Antworte IMMER im JSON-Format."""
try:
response = self.llm.chat(
prompt=prompt,
system_prompt=system_prompt,
temperature=0.1,
max_tokens=500,
json_mode=True
)
result = json.loads(response["content"])
return TradingSignal(
action="NEUTRAL",
confidence=min(100, max(0, result.get("confidence", 50))),
reasoning=result.get("reasons", []),
entry_price=market_data.get("price"),
stop_loss=None,
take_profit=None,
leverage=0
)
except Exception as e:
return TradingSignal(
action="NEUTRAL",
confidence=50,
reasoning=[f"Neutral-Analyse fehlgeschlagen: {str(e)}"],
entry_price=market_data.get("price")
)
def _build_neutral_prompt(self, data: dict) -> str:
"""Erstellt Neutral-spezifischen Prompt."""
return f"""
Analysiere EURUSD für WAIT/Range-Trading:
Aktuelle Daten:
- Preis: {data.get('price', 'N/A')}
- Hurst Regime: {data.get('hurst_regime', 'N/A')}
- RSI: {data.get('rsi', 'N/A')}
- MACD: {data.get('macd', 'N/A')}
- Wirtschaftsdaten: {data.get('economic_data', 'N/A')}
- Sentiment: {data.get('sentiment', 'N/A')}
Finde Argumente für ABWARTEN:
1. Ist das Marktregime unklar?
2. Gibt es widersprüchliche Signale?
3. Stehen wichtige News an (NFP, EZB, Fed)?
4. Ist das Risk/Reward zu gering?
Antworte als JSON:
{{
"confidence": 0-100,
"reasons": ["Grund 1", "Grund 2", ...],
"range_low": 1.0820,
"range_high": 1.0900
}}
"""
class EURUSDResearchManager:
"""
Research Manager: Bewertet Bull/Bear/Neutral Debatte.
Analysiert alle drei Signale und trifft finale Entscheidung:
- Wenn Bull Confidence >> Bear Confidence → LONG
- Wenn Bear Confidence >> Bull Confidence → SHORT
- Wenn Neutral Confidence hoch oder uneindeutig → NEUTRAL
"""
def __init__(self, llm: Optional[MultiProviderLLM] = None):
self.llm = llm or MultiProviderLLM()
def evaluate(
self,
bull_signal: TradingSignal,
bear_signal: TradingSignal,
neutral_signal: TradingSignal,
market_data: dict
) -> TradingSignal:
"""
Bewertet Debatte und trifft finale Entscheidung.
Parameters
----------
bull_signal : TradingSignal
Bull-Analyse
bear_signal : TradingSignal
Bear-Analyse
neutral_signal : TradingSignal
Neutral-Analyse
market_data : dict
Marktdaten
Returns
-------
TradingSignal
Finale Trading-Entscheidung
"""
prompt = self._build_evaluation_prompt(
bull_signal, bear_signal, neutral_signal, market_data
)
system_prompt = """Du bist ein EURUSD Research Manager. Deine Aufgabe ist es,
die Bull/Bear/Neutral-Analysen zu bewerten und eine finale Entscheidung zu treffen.
Entscheidungslogik:
- Wenn Bull Confidence > 70 und > Bear Confidence + 20 → LONG
- Wenn Bear Confidence > 70 und > Bull Confidence + 20 → SHORT
- Wenn Neutral Confidence > 60 oder Differenz < 20 → NEUTRAL/WAIT
- Berücksichtige auch Hurst-Regime und Risk/Reward
Antworte IMMER im JSON-Format."""
try:
response = self.llm.chat(
prompt=prompt,
system_prompt=system_prompt,
temperature=0.1,
max_tokens=600,
json_mode=True
)
result = json.loads(response["content"])
action = result.get("action", "NEUTRAL")
if action not in ["LONG", "SHORT", "NEUTRAL"]:
action = "NEUTRAL"
return TradingSignal(
action=action,
confidence=min(100, max(0, result.get("confidence", 50))),
reasoning=result.get("reasons", []),
entry_price=market_data.get("price"),
stop_loss=result.get("stop_loss"),
take_profit=result.get("take_profit"),
leverage=result.get("leverage", 0 if action == "NEUTRAL" else 20)
)
except Exception as e:
# Default zu NEUTRAL bei Fehlern
return TradingSignal(
action="NEUTRAL",
confidence=50,
reasoning=[f"Research Manager fehlgeschlagen: {str(e)}"],
entry_price=market_data.get("price")
)
def _build_evaluation_prompt(
self,
bull: TradingSignal,
bear: TradingSignal,
neutral: TradingSignal,
data: dict
) -> str:
"""Erstellt Evaluations-Prompt."""
return f"""
Bewerte Bull/Bear/Neutral Debatte für EURUSD:
=== Bull Argumente (Confidence: {bull.confidence}) ===
{chr(10).join(f"- {r}" for r in bull.reasoning)}
Stop Loss: {bull.stop_loss}, Take Profit: {bull.take_profit}, Leverage: {bull.leverage}
=== Bear Argumente (Confidence: {bear.confidence}) ===
{chr(10).join(f"- {r}" for r in bear.reasoning)}
Stop Loss: {bear.stop_loss}, Take Profit: {bear.take_profit}, Leverage: {bear.leverage}
=== Neutral Argumente (Confidence: {neutral.confidence}) ===
{chr(10).join(f"- {r}" for r in neutral.reasoning)}
=== Marktdaten ===
- Preis: {data.get('price', 'N/A')}
- Hurst Regime: {data.get('hurst_regime', 'N/A')}
- RSI: {data.get('rsi', 'N/A')}
Treffe eine finale Entscheidung (LONG/SHORT/NEUTRAL):
Antworte als JSON:
{{
"action": "LONG" oder "SHORT" oder "NEUTRAL",
"confidence": 0-100,
"reasons": ["Warum diese Entscheidung", ...],
"stop_loss": 1.0800,
"take_profit": 1.0950,
"leverage": 20
}}
"""
class EURUSDDebateTeam:
"""
Komplettes Debate-Team für EURUSD Trading-Entscheidungen.
Verwendung:
>>> debate = EURUSDDebateTeam()
>>> market_data = {
... "price": 1.0850,
... "hurst_regime": "MEAN_REVERSION",
... "rsi": 28,
... "macd": "bullish",
... "economic_data": "EZB hawkish, Fed pause",
... "sentiment": "risk-on"
... }
>>> signal = debate.run_debate(market_data)
>>> print(f"Signal: {signal.action} ({signal.confidence}%)")
"""
def __init__(self, llm: Optional[MultiProviderLLM] = None):
self.llm = llm or MultiProviderLLM()
self.bull = EURUSDBullAgent(self.llm)
self.bear = EURUSDBearAgent(self.llm)
self.neutral = EURUSDNeutralAgent(self.llm)
self.manager = EURUSDResearchManager(self.llm)
def run_debate(self, market_data: dict) -> TradingSignal:
"""
Führt komplette Bull/Bear/Neutral Debatte durch.
Parameters
----------
market_data : dict
Marktdaten für die Analyse
Returns
-------
TradingSignal
Finale Trading-Entscheidung nach Debatte
"""
# Alle Agenten analysieren parallel (unabhängig)
bull_signal = self.bull.analyze(market_data)
bear_signal = self.bear.analyze(market_data)
neutral_signal = self.neutral.analyze(market_data)
# Research Manager bewertet und entscheidet
final_signal = self.manager.evaluate(
bull_signal, bear_signal, neutral_signal, market_data
)
return final_signal
def get_debate_summary(
self,
bull: TradingSignal,
bear: TradingSignal,
neutral: TradingSignal,
final: TradingSignal
) -> str:
"""
Erstellt Zusammenfassung der Debatte.
Parameters
----------
bull, bear, neutral : TradingSignal
Einzelne Agenten-Signale
final : TradingSignal
Finale Entscheidung
Returns
-------
str
Formatierter Debatten-Bericht
"""
summary = []
summary.append("=" * 60)
summary.append("EURUSD DEBATE SUMMARY")
summary.append("=" * 60)
summary.append(f"\n🐂 BULL (Confidence: {bull.confidence}%)")
for reason in bull.reasoning[:3]:
summary.append(f" • {reason}")
summary.append(f"\n🐻 BEAR (Confidence: {bear.confidence}%)")
for reason in bear.reasoning[:3]:
summary.append(f" • {reason}")
summary.append(f"\n😐 NEUTRAL (Confidence: {neutral.confidence}%)")
for reason in neutral.reasoning[:3]:
summary.append(f" • {reason}")
summary.append(f"\n{'=' * 60}")
emoji = {"LONG": "📈", "SHORT": "📉", "NEUTRAL": "⏸️"}
summary.append(f"FINALE ENTSCHEIDUNG: {emoji.get(final.action, '')} {final.action}")
summary.append(f"Confidence: {final.confidence}%")
summary.append(f"Leverage: {final.leverage}x")
if final.stop_loss and final.take_profit:
summary.append(f"Stop Loss: {final.stop_loss}")
summary.append(f"Take Profit: {final.take_profit}")
summary.append(f"\nBegründung:")
for reason in final.reasoning[:3]:
summary.append(f" • {reason}")
return "\n".join(summary)
# Test-Funktion für lokale Validierung
if __name__ == "__main__":
print("=== EURUSD Debate Team Test (Mock Mode) ===\n")
# Test-Marktdaten
test_market_data = {
"price": 1.0850,
"hurst_regime": "MEAN_REVERSION",
"rsi": 28,
"macd": "bullish",
"economic_data": "EZB hawkish, Fed pause, Eurozone PMI beat",
"sentiment": "risk-on"
}
print("Marktdaten:")
for key, value in test_market_data.items():
print(f" {key}: {value}")
# Teste TradingSignal Dataclass
print("\n=== Test 1: TradingSignal Dataclass ===")
bull_signal = TradingSignal(
action="LONG",
confidence=75,
reasoning=[
"RSI < 30 in Mean-Reversion Regime = gute Long-Opportunity",
"EZB hawkish unterstützt EUR",
"Risk-On Sentiment begünstigt EUR"
],
entry_price=1.0850,
stop_loss=1.0820,
take_profit=1.0920,
leverage=20
)
print(f"✓ Bull Signal erstellt: {bull_signal.action} @ {bull_signal.confidence}%")
bear_signal = TradingSignal(
action="SHORT",
confidence=45,
reasoning=[
"Widerstand bei 1.0900 stark",
"US-Daten könnten besser werden"
],
entry_price=1.0850,
stop_loss=1.0900,
take_profit=1.0780,
leverage=15
)
print(f"✓ Bear Signal erstellt: {bear_signal.action} @ {bear_signal.confidence}%")
neutral_signal = TradingSignal(
action="NEUTRAL",
confidence=60,
reasoning=[
"Warte auf NFP am Freitag",
"Range-Trading zwischen 1.0800-1.0900 sinnvoller"
],
entry_price=1.0850
)
print(f"✓ Neutral Signal erstellt: {neutral_signal.action} @ {neutral_signal.confidence}%")
# Teste Research Manager Decision Logic (ohne LLM)
print("\n=== Test 2: Research Manager Decision Logic ===")
# Simuliere Decision-Logik
if bull_signal.confidence > 70 and bull_signal.confidence > bear_signal.confidence + 20:
final_action = "LONG"
final_confidence = bull_signal.confidence
elif bear_signal.confidence > 70 and bear_signal.confidence > bull_signal.confidence + 20:
final_action = "SHORT"
final_confidence = bear_signal.confidence
elif neutral_signal.confidence > 60 or abs(bull_signal.confidence - bear_signal.confidence) < 20:
final_action = "NEUTRAL"
final_confidence = neutral_signal.confidence
else:
# Höhere Confidence gewinnt
if bull_signal.confidence > bear_signal.confidence:
final_action = "LONG"
final_confidence = bull_signal.confidence
else:
final_action = "SHORT"
final_confidence = bear_signal.confidence
print(f"Decision Logic:")
print(f" Bull: {bull_signal.confidence}%, Bear: {bear_signal.confidence}%, Neutral: {neutral_signal.confidence}%")
print(f" → Finale Entscheidung: {final_action} ({final_confidence}%)")
# Teste Debate Summary
print("\n=== Test 3: Debate Summary ===")
debate = EURUSDDebateTeam.__new__(EURUSDDebateTeam) # Mock ohne LLM
summary = debate.get_debate_summary(bull_signal, bear_signal, neutral_signal,
TradingSignal(final_action, final_confidence, ["Decision based on rules"]))
print(summary)
# Teste verschiedene Szenarien
print("\n=== Test 4: Verschiedene Szenarien ===")
scenarios = [
{"bull": 80, "bear": 40, "neutral": 30, "expected": "LONG"},
{"bull": 35, "bear": 85, "neutral": 40, "expected": "SHORT"},
{"bull": 55, "bear": 50, "neutral": 70, "expected": "NEUTRAL"},
{"bull": 60, "bear": 55, "neutral": 40, "expected": "LONG"},
]
for i, scenario in enumerate(scenarios, 1):
if scenario["bull"] > 70 and scenario["bull"] > scenario["bear"] + 20:
result = "LONG"
elif scenario["bear"] > 70 and scenario["bear"] > scenario["bull"] + 20:
result = "SHORT"
elif scenario["neutral"] > 60 or abs(scenario["bull"] - scenario["bear"]) < 20:
result = "NEUTRAL"
elif scenario["bull"] > scenario["bear"]:
result = "LONG"
else:
result = "SHORT"
status = "✓" if result == scenario["expected"] else "✗"
print(f" {status} Scenario {i}: Bull={scenario['bull']}%, Bear={scenario['bear']}%, Neutral={scenario['neutral']}%")
print(f" → {result} (expected: {scenario['expected']})")
print("\n✅ EURUSD Debate Team implementation is functional!")
print("\nNote: Full LLM tests require a running server.")