feat: Implement Equity Curve Trading for automatic drawdown protection
NEW MODULE: equity_curve_trading.py - EquityCurveManager class for meta-strategy control - Tracks equity history after each trade - Calculates Moving Average over configurable period (default: 10 trades) - Soft Mode: Reduces lot size to 50% when equity < MA - Hard Mode: Completely stops trading when equity < MA - Recovery detection with buffer percentage - Persistent storage in equity_curve_history.json CONFIGURATION: - ma_period: 10 trades (Moving Average window) - min_trades_required: 5 (warmup period) - soft_mode: True (reduce lots instead of stopping) - soft_mode_multiplier: 0.5 (50% lots when under MA) - recovery_buffer_pct: 0.5% (buffer for recovery status) INTEGRATION: - Added to Cell 78 (Advanced Optimizations setup) - Integrated in enhanced_trading_check_wrapper (Cells 85, 90) - Added lot_multiplier parameter to execute_trade_v2_adaptive - Equity update after each successful trade EXAMPLE FLOW: 1. Before trade: Check should_trade() → returns (allowed, reason, lot_multiplier) 2. If equity < MA: lot_multiplier = 0.5 (or 0.0 in hard mode) 3. Position size adjusted: volume = volume * lot_multiplier 4. After trade: update_equity() called to track new equity BENEFITS: - Automatic protection during losing streaks - Reduces exposure when strategy underperforms - Capitalizes fully when strategy is working - No emotional decisions needed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1230,7 +1230,9 @@
|
|||||||
" debug=True,\n",
|
" debug=True,\n",
|
||||||
" # Enhanced Scoring Overrides\n",
|
" # Enhanced Scoring Overrides\n",
|
||||||
" signal_info_override=None,\n",
|
" signal_info_override=None,\n",
|
||||||
" confidence_override=None\n",
|
" confidence_override=None,\n",
|
||||||
|
" # Equity Curve Trading\n",
|
||||||
|
" lot_multiplier=1.0\n",
|
||||||
"):\n",
|
"):\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
" V1.6 Adaptive Complete Trade-Ausführung:\n",
|
" V1.6 Adaptive Complete Trade-Ausführung:\n",
|
||||||
@@ -1351,6 +1353,13 @@
|
|||||||
" else:\n",
|
" else:\n",
|
||||||
" volume = TRADING_CONFIG[\"lot_sizing\"][\"default_lot\"]\n",
|
" volume = TRADING_CONFIG[\"lot_sizing\"][\"default_lot\"]\n",
|
||||||
" \n",
|
" \n",
|
||||||
|
" # Apply Equity Curve lot multiplier\n",
|
||||||
|
" if lot_multiplier != 1.0:\n",
|
||||||
|
" original_volume = volume\n",
|
||||||
|
" volume = round(volume * lot_multiplier, 2)\n",
|
||||||
|
" volume = max(TRADING_CONFIG[\"lot_sizing\"][\"min_lot\"], volume) # Ensure minimum\n",
|
||||||
|
" print(f\"📈 Equity Curve: Lot adjusted {original_volume:.2f} → {volume:.2f} ({lot_multiplier:.0%})\")\n",
|
||||||
|
" \n",
|
||||||
" # Log Trade Info\n",
|
" # Log Trade Info\n",
|
||||||
" print(f\"\\n🚀 V1.6 ADAPTIVE COMPLETE TRADE EXECUTION\")\n",
|
" print(f\"\\n🚀 V1.6 ADAPTIVE COMPLETE TRADE EXECUTION\")\n",
|
||||||
" print(f\"Direction: {'LONG' if entry_signal == 1 else 'SHORT'}\")\n",
|
" print(f\"Direction: {'LONG' if entry_signal == 1 else 'SHORT'}\")\n",
|
||||||
@@ -3011,6 +3020,7 @@
|
|||||||
"from dynamic_threshold_optimizer import DynamicThresholdOptimizer, auto_optimize_thresholds\n",
|
"from dynamic_threshold_optimizer import DynamicThresholdOptimizer, auto_optimize_thresholds\n",
|
||||||
"from enhanced_signal_scoring import EnhancedSignalScorer\n",
|
"from enhanced_signal_scoring import EnhancedSignalScorer\n",
|
||||||
"from enhanced_trailing_stop import EnhancedTrailingStopManager, create_enhanced_position_monitor\n",
|
"from enhanced_trailing_stop import EnhancedTrailingStopManager, create_enhanced_position_monitor\n",
|
||||||
|
"from equity_curve_trading import EquityCurveManager\n",
|
||||||
"\n",
|
"\n",
|
||||||
"print(\"🚀 INITIALIZING ADVANCED OPTIMIZATIONS...\")\n",
|
"print(\"🚀 INITIALIZING ADVANCED OPTIMIZATIONS...\")\n",
|
||||||
"print(\"=\" * 70)\n",
|
"print(\"=\" * 70)\n",
|
||||||
@@ -3075,6 +3085,18 @@
|
|||||||
"print(\"✅ Enhanced Trailing Stop Manager initialized\")\n",
|
"print(\"✅ Enhanced Trailing Stop Manager initialized\")\n",
|
||||||
"print()\n",
|
"print()\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"# 4. Equity Curve Trading\n",
|
||||||
|
"equity_curve_manager = EquityCurveManager(\n",
|
||||||
|
" ma_period=10, # MA über 10 Trades\n",
|
||||||
|
" min_trades_required=5, # Warmup: 5 Trades\n",
|
||||||
|
" soft_mode=True, # Reduzierte Lots statt Stop\n",
|
||||||
|
" soft_mode_multiplier=0.5, # 50% Lots wenn unter MA\n",
|
||||||
|
" recovery_buffer_pct=0.5, # 0.5% über MA = Recovery\n",
|
||||||
|
" data_file=\"equity_curve_history.json\"\n",
|
||||||
|
")\n",
|
||||||
|
"print(\"✅ Equity Curve Manager initialized\")\n",
|
||||||
|
"print()\n",
|
||||||
|
"\n",
|
||||||
"# 4. Run initial threshold optimization\n",
|
"# 4. Run initial threshold optimization\n",
|
||||||
"print(\"🔄 Running initial threshold optimization...\")\n",
|
"print(\"🔄 Running initial threshold optimization...\")\n",
|
||||||
"try:\n",
|
"try:\n",
|
||||||
@@ -3092,6 +3114,7 @@
|
|||||||
"print(\" • Dynamic Thresholds: ✅ (auto-adjusts daily)\")\n",
|
"print(\" • Dynamic Thresholds: ✅ (auto-adjusts daily)\")\n",
|
||||||
"print(\" • Enhanced Scoring: ✅ (5-factor analysis)\")\n",
|
"print(\" • Enhanced Scoring: ✅ (5-factor analysis)\")\n",
|
||||||
"print(\" • Enhanced Trailing: ✅ (multi-tier protection)\")\n",
|
"print(\" • Enhanced Trailing: ✅ (multi-tier protection)\")\n",
|
||||||
|
"print(\" • Equity Curve Trading: ✅ (auto-pause on drawdown)\")\n",
|
||||||
"print()\n",
|
"print()\n",
|
||||||
"print(\"💡 Tip: Use 'threshold_optimizer.generate_report()' for details\")"
|
"print(\"💡 Tip: Use 'threshold_optimizer.generate_report()' for details\")"
|
||||||
]
|
]
|
||||||
@@ -3362,6 +3385,14 @@
|
|||||||
"\n",
|
"\n",
|
||||||
" print(f\"✅ Position-Check OK: {position_info['count']}/{max_positions}\")\n",
|
" print(f\"✅ Position-Check OK: {position_info['count']}/{max_positions}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
" # SCHRITT 1.5: EQUITY CURVE CHECK\n",
|
||||||
|
" ec_allowed, ec_reason, lot_multiplier = equity_curve_manager.should_trade()\n",
|
||||||
|
" print(f\"📈 Equity Curve: {ec_reason}\")\n",
|
||||||
|
" \n",
|
||||||
|
" if not ec_allowed:\n",
|
||||||
|
" print(f\"⛔ TRADE BLOCKIERT durch Equity Curve Filter\")\n",
|
||||||
|
" return None\n",
|
||||||
|
"\n",
|
||||||
" # SCHRITT 2: Signal Analysis (wie vorher)\n",
|
" # SCHRITT 2: Signal Analysis (wie vorher)\n",
|
||||||
" signal_info = extended_top_down_v2_adaptive(symbol)\n",
|
" signal_info = extended_top_down_v2_adaptive(symbol)\n",
|
||||||
" if signal_info is None:\n",
|
" if signal_info is None:\n",
|
||||||
@@ -3424,8 +3455,14 @@
|
|||||||
" result = execute_trade_v2_adaptive(\n",
|
" result = execute_trade_v2_adaptive(\n",
|
||||||
" symbol=symbol,\n",
|
" symbol=symbol,\n",
|
||||||
" signal_info_override=signal_info,\n",
|
" signal_info_override=signal_info,\n",
|
||||||
" confidence_override=final_confidence # ← Use hybrid score!\n",
|
" confidence_override=final_confidence, # ← Use hybrid score!\n",
|
||||||
|
" lot_multiplier=lot_multiplier # ← Equity Curve adjustment\n",
|
||||||
" )\n",
|
" )\n",
|
||||||
|
" \n",
|
||||||
|
" # Update Equity Curve nach Trade\n",
|
||||||
|
" if result is not None:\n",
|
||||||
|
" equity_curve_manager.update_equity()\n",
|
||||||
|
" print(f\"📈 Equity Curve updated\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" return result\n",
|
" return result\n",
|
||||||
" else:\n",
|
" else:\n",
|
||||||
@@ -3629,6 +3666,14 @@
|
|||||||
"\n",
|
"\n",
|
||||||
" print(f\"✅ Position-Check OK: {position_info['count']}/{max_positions}\")\n",
|
" print(f\"✅ Position-Check OK: {position_info['count']}/{max_positions}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
" # SCHRITT 1.5: EQUITY CURVE CHECK\n",
|
||||||
|
" ec_allowed, ec_reason, lot_multiplier = equity_curve_manager.should_trade()\n",
|
||||||
|
" print(f\"📈 Equity Curve: {ec_reason}\")\n",
|
||||||
|
" \n",
|
||||||
|
" if not ec_allowed:\n",
|
||||||
|
" print(f\"⛔ TRADE BLOCKIERT durch Equity Curve Filter\")\n",
|
||||||
|
" return None\n",
|
||||||
|
"\n",
|
||||||
" # SCHRITT 2: Signal Analysis (wie vorher)\n",
|
" # SCHRITT 2: Signal Analysis (wie vorher)\n",
|
||||||
" signal_info = extended_top_down_v2_adaptive(symbol)\n",
|
" signal_info = extended_top_down_v2_adaptive(symbol)\n",
|
||||||
" if signal_info is None:\n",
|
" if signal_info is None:\n",
|
||||||
@@ -3691,8 +3736,14 @@
|
|||||||
" result = execute_trade_v2_adaptive(\n",
|
" result = execute_trade_v2_adaptive(\n",
|
||||||
" symbol=symbol,\n",
|
" symbol=symbol,\n",
|
||||||
" signal_info_override=signal_info,\n",
|
" signal_info_override=signal_info,\n",
|
||||||
" confidence_override=final_confidence # ← Use hybrid score!\n",
|
" confidence_override=final_confidence, # ← Use hybrid score!\n",
|
||||||
|
" lot_multiplier=lot_multiplier # ← Equity Curve adjustment\n",
|
||||||
" )\n",
|
" )\n",
|
||||||
|
" \n",
|
||||||
|
" # Update Equity Curve nach Trade\n",
|
||||||
|
" if result is not None:\n",
|
||||||
|
" equity_curve_manager.update_equity()\n",
|
||||||
|
" print(f\"📈 Equity Curve updated\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" return result\n",
|
" return result\n",
|
||||||
" else:\n",
|
" else:\n",
|
||||||
|
|||||||
@@ -66,6 +66,14 @@ def enhanced_trading_check_wrapper(symbol="XAUUSD", debug=False):
|
|||||||
|
|
||||||
print(f"✅ Position-Check OK: {position_info['count']}/{max_positions}")
|
print(f"✅ Position-Check OK: {position_info['count']}/{max_positions}")
|
||||||
|
|
||||||
|
# SCHRITT 1.5: EQUITY CURVE CHECK
|
||||||
|
ec_allowed, ec_reason, lot_multiplier = equity_curve_manager.should_trade()
|
||||||
|
print(f"📈 Equity Curve: {ec_reason}")
|
||||||
|
|
||||||
|
if not ec_allowed:
|
||||||
|
print(f"⛔ TRADE BLOCKIERT durch Equity Curve Filter")
|
||||||
|
return None
|
||||||
|
|
||||||
# SCHRITT 2: Signal Analysis (wie vorher)
|
# SCHRITT 2: Signal Analysis (wie vorher)
|
||||||
signal_info = extended_top_down_v2_adaptive(symbol)
|
signal_info = extended_top_down_v2_adaptive(symbol)
|
||||||
if signal_info is None:
|
if signal_info is None:
|
||||||
@@ -127,9 +135,15 @@ def enhanced_trading_check_wrapper(symbol="XAUUSD", debug=False):
|
|||||||
result = execute_trade_v2_adaptive(
|
result = execute_trade_v2_adaptive(
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
signal_info_override=signal_info,
|
signal_info_override=signal_info,
|
||||||
confidence_override=final_confidence # ← Use hybrid score!
|
confidence_override=final_confidence, # ← Use hybrid score!
|
||||||
|
lot_multiplier=lot_multiplier # ← Equity Curve adjustment
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Update Equity Curve nach Trade
|
||||||
|
if result is not None:
|
||||||
|
equity_curve_manager.update_equity()
|
||||||
|
print(f"📈 Equity Curve updated")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
print(f"\\n❌ Signal below threshold: {final_confidence:.1f}% < {adaptive_threshold:.1f}%")
|
print(f"\\n❌ Signal below threshold: {final_confidence:.1f}% < {adaptive_threshold:.1f}%")
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"timestamp": "2026-01-23T08:15:25.214791",
|
"timestamp": "2026-01-26T00:27:01.306793",
|
||||||
"session_thresholds": {
|
"session_thresholds": {
|
||||||
"asian": 60,
|
"asian": 60,
|
||||||
"ny": 60,
|
"ny": 60,
|
||||||
"london": 80,
|
"london": 75,
|
||||||
"overlap": 70
|
"overlap": 65
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"lookback_trades": 20,
|
"lookback_trades": 20,
|
||||||
|
|||||||
@@ -0,0 +1,352 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
📈 Equity Curve Trading Module
|
||||||
|
Meta-Strategy: Trade nur wenn Equity über Moving Average
|
||||||
|
|
||||||
|
KONZEPT:
|
||||||
|
- Trackt Equity-Historie nach jedem Trade
|
||||||
|
- Berechnet Moving Average der Equity
|
||||||
|
- Erlaubt Trading nur wenn Equity >= MA
|
||||||
|
- Reduziert Drawdowns durch automatische Pausen
|
||||||
|
|
||||||
|
VERWENDUNG:
|
||||||
|
from equity_curve_trading import EquityCurveManager
|
||||||
|
|
||||||
|
ecm = EquityCurveManager(ma_period=10)
|
||||||
|
|
||||||
|
# Vor jedem Trade prüfen:
|
||||||
|
if ecm.should_trade():
|
||||||
|
execute_trade(...)
|
||||||
|
|
||||||
|
# Nach jedem Trade updaten:
|
||||||
|
ecm.update_equity()
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List, Dict, Optional, Tuple
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EquityCurveManager:
|
||||||
|
"""
|
||||||
|
Equity Curve Trading Manager
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Automatisches Equity-Tracking
|
||||||
|
- Konfigurierbarer MA-Zeitraum
|
||||||
|
- Optionaler "Soft Mode" (reduzierte Lots statt Stop)
|
||||||
|
- Persistente Speicherung der Historie
|
||||||
|
- Recovery-Erkennung
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
ma_period: int = 10,
|
||||||
|
min_trades_required: int = 5,
|
||||||
|
soft_mode: bool = True,
|
||||||
|
soft_mode_multiplier: float = 0.5,
|
||||||
|
recovery_buffer_pct: float = 0.5,
|
||||||
|
data_file: str = "equity_curve_history.json"):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
ma_period: Anzahl der Trades für Moving Average (default: 10)
|
||||||
|
min_trades_required: Minimum Trades bevor Filter aktiv wird (default: 5)
|
||||||
|
soft_mode: True = reduzierte Lots, False = komplett stoppen
|
||||||
|
soft_mode_multiplier: Lot-Multiplikator wenn unter MA (default: 0.5 = 50%)
|
||||||
|
recovery_buffer_pct: Prozent über MA für "Recovery" Status (default: 0.5%)
|
||||||
|
data_file: Datei für persistente Speicherung
|
||||||
|
"""
|
||||||
|
self.ma_period = ma_period
|
||||||
|
self.min_trades = min_trades_required
|
||||||
|
self.soft_mode = soft_mode
|
||||||
|
self.soft_multiplier = soft_mode_multiplier
|
||||||
|
self.recovery_buffer = recovery_buffer_pct / 100
|
||||||
|
self.data_file = data_file
|
||||||
|
|
||||||
|
# Equity Historie laden oder initialisieren
|
||||||
|
self.equity_history: List[Dict] = []
|
||||||
|
self._load_history()
|
||||||
|
|
||||||
|
# Status
|
||||||
|
self.current_status = "ACTIVE" # ACTIVE, PAUSED, RECOVERY
|
||||||
|
self.trades_while_paused = 0
|
||||||
|
|
||||||
|
logger.info("=" * 60)
|
||||||
|
logger.info("📈 EQUITY CURVE TRADING INITIALIZED")
|
||||||
|
logger.info("=" * 60)
|
||||||
|
logger.info(f" MA Period: {ma_period} trades")
|
||||||
|
logger.info(f" Min Trades: {min_trades_required}")
|
||||||
|
logger.info(f" Mode: {'Soft (reduced lots)' if soft_mode else 'Hard (full stop)'}")
|
||||||
|
if soft_mode:
|
||||||
|
logger.info(f" Soft Multiplier: {soft_mode_multiplier:.0%}")
|
||||||
|
logger.info(f" Recovery Buffer: {recovery_buffer_pct}%")
|
||||||
|
logger.info(f" History File: {data_file}")
|
||||||
|
logger.info(f" Loaded Trades: {len(self.equity_history)}")
|
||||||
|
logger.info("=" * 60)
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# CORE METHODS
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
def should_trade(self, mt5_account_info=None) -> Tuple[bool, str, float]:
|
||||||
|
"""
|
||||||
|
Prüft ob Trading erlaubt ist basierend auf Equity Curve
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mt5_account_info: Optional MT5 account info object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(should_trade, reason, lot_multiplier)
|
||||||
|
- should_trade: True wenn traden erlaubt
|
||||||
|
- reason: Erklärung
|
||||||
|
- lot_multiplier: 1.0 = normal, 0.5 = reduziert, etc.
|
||||||
|
"""
|
||||||
|
# Nicht genug Historie
|
||||||
|
if len(self.equity_history) < self.min_trades:
|
||||||
|
return True, f"Warmup: {len(self.equity_history)}/{self.min_trades} trades", 1.0
|
||||||
|
|
||||||
|
# Aktuelle Equity holen
|
||||||
|
current_equity = self._get_current_equity(mt5_account_info)
|
||||||
|
if current_equity is None:
|
||||||
|
return True, "Could not get equity, allowing trade", 1.0
|
||||||
|
|
||||||
|
# MA berechnen
|
||||||
|
ma_equity = self._calculate_ma()
|
||||||
|
|
||||||
|
# Status bestimmen
|
||||||
|
equity_vs_ma_pct = ((current_equity - ma_equity) / ma_equity) * 100
|
||||||
|
|
||||||
|
if current_equity >= ma_equity * (1 + self.recovery_buffer):
|
||||||
|
# Deutlich über MA = ACTIVE
|
||||||
|
self.current_status = "ACTIVE"
|
||||||
|
self.trades_while_paused = 0
|
||||||
|
return True, f"✅ Equity ${current_equity:,.2f} > MA ${ma_equity:,.2f} (+{equity_vs_ma_pct:.1f}%)", 1.0
|
||||||
|
|
||||||
|
elif current_equity >= ma_equity:
|
||||||
|
# Knapp über MA = RECOVERY (vorsichtig)
|
||||||
|
self.current_status = "RECOVERY"
|
||||||
|
if self.soft_mode:
|
||||||
|
return True, f"🔄 Recovery: ${current_equity:,.2f} ≈ MA ${ma_equity:,.2f} ({equity_vs_ma_pct:+.1f}%)", 0.75
|
||||||
|
else:
|
||||||
|
return True, f"🔄 Recovery: ${current_equity:,.2f} ≈ MA ${ma_equity:,.2f}", 1.0
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Unter MA = PAUSED oder SOFT
|
||||||
|
self.current_status = "PAUSED"
|
||||||
|
self.trades_while_paused += 1
|
||||||
|
|
||||||
|
if self.soft_mode:
|
||||||
|
return True, f"⚠️ Soft Mode: ${current_equity:,.2f} < MA ${ma_equity:,.2f} ({equity_vs_ma_pct:.1f}%)", self.soft_multiplier
|
||||||
|
else:
|
||||||
|
return False, f"⛔ PAUSED: ${current_equity:,.2f} < MA ${ma_equity:,.2f} ({equity_vs_ma_pct:.1f}%)", 0.0
|
||||||
|
|
||||||
|
def update_equity(self, mt5_account_info=None, trade_result: Optional[Dict] = None):
|
||||||
|
"""
|
||||||
|
Updated Equity-Historie nach einem Trade
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mt5_account_info: Optional MT5 account info
|
||||||
|
trade_result: Optional dict mit Trade-Details
|
||||||
|
"""
|
||||||
|
current_equity = self._get_current_equity(mt5_account_info)
|
||||||
|
if current_equity is None:
|
||||||
|
logger.warning("Could not get equity for update")
|
||||||
|
return
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"equity": current_equity,
|
||||||
|
"trade_count": len(self.equity_history) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if trade_result:
|
||||||
|
entry["trade_profit"] = trade_result.get("profit", 0)
|
||||||
|
entry["trade_symbol"] = trade_result.get("symbol", "UNKNOWN")
|
||||||
|
|
||||||
|
self.equity_history.append(entry)
|
||||||
|
self._save_history()
|
||||||
|
|
||||||
|
# Log status
|
||||||
|
ma = self._calculate_ma() if len(self.equity_history) >= self.min_trades else None
|
||||||
|
if ma:
|
||||||
|
diff_pct = ((current_equity - ma) / ma) * 100
|
||||||
|
status_emoji = "✅" if current_equity >= ma else "⚠️"
|
||||||
|
logger.info(f"📈 Equity Update: ${current_equity:,.2f} | MA: ${ma:,.2f} | {status_emoji} {diff_pct:+.1f}%")
|
||||||
|
else:
|
||||||
|
logger.info(f"📈 Equity Update: ${current_equity:,.2f} | Warmup: {len(self.equity_history)}/{self.min_trades}")
|
||||||
|
|
||||||
|
def get_status(self, mt5_account_info=None) -> Dict:
|
||||||
|
"""
|
||||||
|
Gibt detaillierten Status zurück
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict mit allen relevanten Informationen
|
||||||
|
"""
|
||||||
|
current_equity = self._get_current_equity(mt5_account_info)
|
||||||
|
ma = self._calculate_ma() if len(self.equity_history) >= self.min_trades else None
|
||||||
|
|
||||||
|
status = {
|
||||||
|
"current_equity": current_equity,
|
||||||
|
"ma_equity": ma,
|
||||||
|
"ma_period": self.ma_period,
|
||||||
|
"total_trades": len(self.equity_history),
|
||||||
|
"min_trades_required": self.min_trades,
|
||||||
|
"warmup_complete": len(self.equity_history) >= self.min_trades,
|
||||||
|
"status": self.current_status,
|
||||||
|
"soft_mode": self.soft_mode,
|
||||||
|
"soft_multiplier": self.soft_multiplier if self.soft_mode else None
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_equity and ma:
|
||||||
|
status["equity_vs_ma_pct"] = ((current_equity - ma) / ma) * 100
|
||||||
|
status["equity_above_ma"] = current_equity >= ma
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
def get_report(self, mt5_account_info=None) -> str:
|
||||||
|
"""
|
||||||
|
Generiert einen formatierten Status-Report
|
||||||
|
"""
|
||||||
|
status = self.get_status(mt5_account_info)
|
||||||
|
|
||||||
|
report = []
|
||||||
|
report.append("")
|
||||||
|
report.append("=" * 60)
|
||||||
|
report.append("📈 EQUITY CURVE TRADING STATUS")
|
||||||
|
report.append("=" * 60)
|
||||||
|
|
||||||
|
if status["current_equity"]:
|
||||||
|
report.append(f" Current Equity: ${status['current_equity']:,.2f}")
|
||||||
|
|
||||||
|
if status["ma_equity"]:
|
||||||
|
report.append(f" MA ({self.ma_period} trades): ${status['ma_equity']:,.2f}")
|
||||||
|
|
||||||
|
diff = status.get("equity_vs_ma_pct", 0)
|
||||||
|
if status.get("equity_above_ma"):
|
||||||
|
report.append(f" Status: ✅ ABOVE MA (+{diff:.1f}%)")
|
||||||
|
else:
|
||||||
|
report.append(f" Status: ⚠️ BELOW MA ({diff:.1f}%)")
|
||||||
|
else:
|
||||||
|
report.append(f" Status: 🔄 Warmup ({status['total_trades']}/{status['min_trades_required']} trades)")
|
||||||
|
|
||||||
|
report.append("")
|
||||||
|
report.append(f" Trading Status: {status['status']}")
|
||||||
|
report.append(f" Mode: {'Soft' if status['soft_mode'] else 'Hard'}")
|
||||||
|
|
||||||
|
if status['soft_mode'] and status['status'] == 'PAUSED':
|
||||||
|
report.append(f" Lot Multiplier: {status['soft_multiplier']:.0%}")
|
||||||
|
|
||||||
|
report.append("")
|
||||||
|
report.append(f" Total Trades: {status['total_trades']}")
|
||||||
|
report.append("=" * 60)
|
||||||
|
|
||||||
|
return "\n".join(report)
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# HELPER METHODS
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
def _get_current_equity(self, mt5_account_info=None) -> Optional[float]:
|
||||||
|
"""Holt aktuelle Equity von MT5 oder übergebenem Object"""
|
||||||
|
if mt5_account_info:
|
||||||
|
return mt5_account_info.equity
|
||||||
|
|
||||||
|
try:
|
||||||
|
import MetaTrader5 as mt
|
||||||
|
account = mt.account_info()
|
||||||
|
if account:
|
||||||
|
return account.equity
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Could not get MT5 equity: {e}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _calculate_ma(self) -> float:
|
||||||
|
"""Berechnet Moving Average der letzten N Equity-Werte"""
|
||||||
|
if len(self.equity_history) < self.ma_period:
|
||||||
|
# Nutze alle verfügbaren wenn nicht genug
|
||||||
|
recent = self.equity_history
|
||||||
|
else:
|
||||||
|
recent = self.equity_history[-self.ma_period:]
|
||||||
|
|
||||||
|
equities = [entry["equity"] for entry in recent]
|
||||||
|
return sum(equities) / len(equities) if equities else 0
|
||||||
|
|
||||||
|
def _load_history(self):
|
||||||
|
"""Lädt Equity-Historie aus Datei"""
|
||||||
|
try:
|
||||||
|
if os.path.exists(self.data_file):
|
||||||
|
with open(self.data_file, 'r') as f:
|
||||||
|
self.equity_history = json.load(f)
|
||||||
|
logger.info(f"📂 Loaded {len(self.equity_history)} equity records")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not load equity history: {e}")
|
||||||
|
self.equity_history = []
|
||||||
|
|
||||||
|
def _save_history(self):
|
||||||
|
"""Speichert Equity-Historie in Datei"""
|
||||||
|
try:
|
||||||
|
with open(self.data_file, 'w') as f:
|
||||||
|
json.dump(self.equity_history, f, indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Could not save equity history: {e}")
|
||||||
|
|
||||||
|
def reset_history(self):
|
||||||
|
"""Setzt Historie zurück (Vorsicht!)"""
|
||||||
|
self.equity_history = []
|
||||||
|
self._save_history()
|
||||||
|
logger.warning("⚠️ Equity history has been reset!")
|
||||||
|
|
||||||
|
def add_initial_equity(self, equity: float):
|
||||||
|
"""
|
||||||
|
Fügt initiale Equity hinzu (für Warmup)
|
||||||
|
|
||||||
|
Nützlich wenn du mit bestehendem Konto startest
|
||||||
|
"""
|
||||||
|
for i in range(self.min_trades):
|
||||||
|
self.equity_history.append({
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"equity": equity,
|
||||||
|
"trade_count": i + 1,
|
||||||
|
"note": "Initial warmup entry"
|
||||||
|
})
|
||||||
|
self._save_history()
|
||||||
|
logger.info(f"📈 Added {self.min_trades} initial equity entries at ${equity:,.2f}")
|
||||||
|
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# STANDALONE USAGE
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Demo
|
||||||
|
print("📈 Equity Curve Trading Demo")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
ecm = EquityCurveManager(
|
||||||
|
ma_period=5,
|
||||||
|
min_trades_required=3,
|
||||||
|
soft_mode=True,
|
||||||
|
soft_mode_multiplier=0.5
|
||||||
|
)
|
||||||
|
|
||||||
|
# Simulate some trades
|
||||||
|
test_equities = [10000, 10200, 10150, 9900, 9700, 9500, 9600, 9800, 10000, 10300]
|
||||||
|
|
||||||
|
print("\nSimulating trades:")
|
||||||
|
for i, eq in enumerate(test_equities):
|
||||||
|
# Fake the history
|
||||||
|
ecm.equity_history.append({
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"equity": eq,
|
||||||
|
"trade_count": i + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
# Check if should trade
|
||||||
|
should, reason, mult = ecm.should_trade()
|
||||||
|
print(f"Trade {i+1}: Equity ${eq:,} | {reason} | Lot mult: {mult}")
|
||||||
|
|
||||||
|
print("\n" + ecm.get_report())
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user