""" Volatility-Adjusted Position Sizing für EURUSD Berechnet die optimale Positionsgröße basierend auf: - Kontogröße und Risikotoleranz - Aktueller Volatilität (ATR, Historical Volatility) - Marktregime (Hurst Exponent) - Korrelation mit anderen Positionen Druckenmiller-Prinzip: - Bei hoher Conviction und asymmetrischer Chance: Große Position - Bei niedriger Volatilität: Positionsgröße erhöhen - Bei hoher Korrelation: Risk reduzieren """ from dataclasses import dataclass from typing import Literal, Optional, Tuple import numpy as np import pandas as pd @dataclass class PositionSizeResult: """Ergebnis der Positionsgrößen-Berechnung.""" lots: float leverage: int stop_loss_pips: float take_profit_pips: float risk_usd: float risk_percent: float volatility_adjustment: float regime_adjustment: float correlation_adjustment: float final_adjustment: float def calculate_atr(high: pd.Series, low: pd.Series, close: pd.Series, period: int = 14) -> pd.Series: """ Berechnet Average True Range (ATR) für Volatilitätsmessung. Parameters ---------- high : pd.Series High-Preise low : pd.Series Low-Preise close : pd.Series Close-Preise period : int, default 14 ATR-Periode (14 für 14-Bar-ATR) Returns ------- pd.Series ATR-Werte """ prev_close = close.shift(1) # True Range Komponenten tr1 = high - low tr2 = abs(high - prev_close) tr3 = abs(low - prev_close) # True Range tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1) # ATR als gleitender Durchschnitt von TR atr = tr.rolling(window=period).mean() return atr def calculate_historical_volatility(returns: pd.Series, window: int = 20, annualize: bool = True) -> pd.Series: """ Berechnet historische Volatilität (Standardabweichung der Returns). Parameters ---------- returns : pd.Series Log-Returns oder prozentuale Returns window : int, default 20 Fenster für Volatilitätsberechnung (20 Bars) annualize : bool, default True annualisieren der Volatilität (für 1min-Daten: * sqrt(525600)) Returns ------- pd.Series Historische Volatilität """ vol = returns.rolling(window=window).std() if annualize: # Für 1min-Daten: 525600 Minuten pro Jahr (365 * 24 * 60) vol = vol * np.sqrt(525600) return vol def calculate_volatility_percentile(current_vol: float, vol_history: pd.Series, lookback: int = 100) -> float: """ Berechnet das Volatilitäts-Percentile (0-100). Parameters ---------- current_vol : float Aktuelle Volatilität vol_history : pd.Series Historische Volatilitäten lookback : int, default 100 Lookback-Fenster für Percentil-Berechnung Returns ------- float Volatilitäts-Percentile (0-100) """ if len(vol_history) < lookback: lookback = len(vol_history) if lookback < 10: return 50.0 # Default bei zu wenig Daten # Percentile-Rang der aktuellen Volatilität percentile = (vol_history.iloc[-lookback:] < current_vol).mean() * 100 return percentile def calculate_eurusd_position_size( account_equity: float, atr_14: float, volatility_percentile: float, regime: Literal["MEAN_REVERSION", "NEUTRAL", "TRENDING"] = "NEUTRAL", risk_percent: float = 0.02, base_leverage: int = 20, correlation_adjustment: float = 1.0, pip_value: float = 10.0 # $10 pro Pip für Standard-Lot EURUSD ) -> PositionSizeResult: """ Berechnet die optimale Positionsgröße für EURUSD Trades. Volatility-Adjusted Position Sizing: - Niedrige Volatilität (< 20. Percentile) → größere Position (1.5x) - Mittlere Volatilität (20-80. Percentile) → normale Position (1.0x) - Hohe Volatilität (> 80. Percentile) → kleinere Position (0.4-0.7x) Regime-Adjustierung: - MEAN_REVERSION: Engerer TP, weiterer SL (mehr Raum für Mean-Reversion) - TRENDING: Weiterer TP, normaler SL (Trend ausreiten) - NEUTRAL: Vorsichtig, beide eng Korrelations-Adjustierung: - Hohe Korrelation mit anderen Positionen → Risk reduzieren Parameters ---------- account_equity : float Kontogröße in USD atr_14 : float Aktueller ATR(14) in Pip (z.B. 0.0012 = 12 Pips) volatility_percentile : float Volatilitäts-Percentile (0-100) regime : str, default "NEUTRAL" Marktregime: "MEAN_REVERSION", "NEUTRAL", oder "TRENDING" risk_percent : float, default 0.02 Risiko pro Trade (2% = 0.02) base_leverage : int, default 20 Basis-Hebel (10-50) correlation_adjustment : float, default 1.0 Korrelations-Faktor (0.7-1.1) pip_value : float, default 10.0 Wert pro Pip pro Standard-Lot ($10 für EURUSD) Returns ------- PositionSizeResult Berechnete Positionsgröße mit allen Details Example ------- >>> result = calculate_eurusd_position_size( ... account_equity=100000, ... atr_14=12.5, # 12.5 Pips ... volatility_percentile=35, # Unterdurchschnittliche Vol ... regime="MEAN_REVERSION", ... risk_percent=0.02 ... ) >>> print(f"Lots: {result.lots:.2f}, Leverage: {result.leverage}x") >>> print(f"Risk: ${result.risk_usd:.2f} ({result.risk_percent:.1%})") """ # 1. Volatility-Adjustment if volatility_percentile < 20: vol_adjustment = 1.5 # Niedrige Vol → größere Position elif volatility_percentile < 50: vol_adjustment = 1.2 # Unterdurchschnittliche Vol elif volatility_percentile < 80: vol_adjustment = 1.0 # Normale Vol elif volatility_percentile < 95: vol_adjustment = 0.7 # Erhöhte Vol → kleinere Position else: vol_adjustment = 0.4 # Extreme Vol → minimales Risk # 2. Regime-Adjustment if regime == "MEAN_REVERSION": regime_adjustment = 1.1 # Mean-Reversion ist relativ vorhersehbar sl_pips = atr_14 * 2.0 # Weiterer SL für Mean-Reversion tp_pips = atr_14 * 1.0 # Engerer TP elif regime == "TRENDING": regime_adjustment = 1.2 # Trending kann profitabler sein sl_pips = atr_14 * 1.5 # Normaler SL tp_pips = atr_14 * 2.5 # Weiterer TP für Trend else: # NEUTRAL regime_adjustment = 0.8 # Vorsichtig bei unklarem Regime sl_pips = atr_14 * 1.5 # Normaler SL tp_pips = atr_14 * 1.2 # Engerer TP # 3. Gesamtes Adjustment final_adjustment = vol_adjustment * regime_adjustment * correlation_adjustment # 4. Risiko in USD base_risk_usd = account_equity * risk_percent adjusted_risk_usd = base_risk_usd * final_adjustment # 5. Positionsgröße in Lots # Risk = Lots * Pip_Value * SL_Pips # Lots = Risk / (Pip_Value * SL_Pips) if sl_pips > 0 and pip_value > 0: lots = adjusted_risk_usd / (pip_value * sl_pips) else: lots = 0.0 # 6. Effektiver Hebel basierend auf Positionsgröße # 1 Standard-Lot = 100,000 EUR # Bei 100k Konto und 1 Lot = 100k EUR = 1x Hebel position_value_eur = lots * 100000 position_value_usd = position_value_eur # EURUSD ≈ 1:1 effective_leverage = position_value_usd / account_equity if account_equity > 0 else 0 # Begrenze Hebel auf Maximum max_leverage = base_leverage * final_adjustment if effective_leverage > max_leverage: # Reduziere Lots um im Hebel-Limit zu bleiben lots = (max_leverage * account_equity) / 100000 effective_leverage = max_leverage # Begrenze Lots auf vernünftige Werte lots = max(0.01, min(lots, 100.0)) # Min 0.01 Lots, Max 100 Lots # Finales Risiko mit angepassten Lots final_risk_usd = lots * pip_value * sl_pips final_risk_percent = final_risk_usd / account_equity if account_equity > 0 else 0 return PositionSizeResult( lots=round(lots, 2), leverage=round(effective_leverage), stop_loss_pips=round(sl_pips, 1), take_profit_pips=round(tp_pips, 1), risk_usd=round(final_risk_usd, 2), risk_percent=round(final_risk_percent, 4), volatility_adjustment=round(vol_adjustment, 2), regime_adjustment=round(regime_adjustment, 2), correlation_adjustment=round(correlation_adjustment, 2), final_adjustment=round(final_adjustment, 2) ) def calculate_forex_correlation( eurusd_returns: pd.Series, other_positions: dict ) -> Tuple[float, float]: """ Berechnet die durchschnittliche Korrelation von EURUSD mit anderen Positionen. Für Forex relevante Korrelationen: - GBPUSD: +0.75 (positiv, beide EUR/GBP vs USD) - USDCHF: -0.70 (negativ, beide USD-basiert) - DXY: -0.85 (negativ, DXY ist USD-Index) - EURGBP: +0.40 (moderat positiv) Parameters ---------- eurusd_returns : pd.Series EURUSD Returns für Korrelationsberechnung other_positions : dict Andere offene Positionen mit Keys: - symbol: {"position": "LONG"/"SHORT", "size": lots, "returns": pd.Series} Returns ------- Tuple[float, float] (durchschnittliche Korrelation, Korrelations-Adjustment-Faktor) """ # Typische Forex-Korrelationen CORRELATIONS = { "GBPUSD": 0.75, "USDCHF": -0.70, "DXY": -0.85, "EURGBP": 0.40, "USDJPY": -0.50, "AUDUSD": 0.60, "USDCAD": -0.55, "EURUSD": 1.0 # Referenz } if len(other_positions) == 0: return 0.0, 1.0 # Keine Korrelation, kein Adjustment # Berechne gewichtete durchschnittliche Korrelation total_correlation = 0.0 total_weight = 0.0 for symbol, pos_data in other_positions.items(): if symbol not in CORRELATIONS: continue # Korrelation aus historischen Returns (wenn verfügbar) if "returns" in pos_data and pos_data["returns"] is not None: try: # Berechne tatsächliche Korrelation corr = eurusd_returns.corr(pos_data["returns"]) if not np.isnan(corr): actual_corr = corr else: actual_corr = CORRELATIONS[symbol] except Exception: actual_corr = CORRELATIONS[symbol] else: # Verwende typische Korrelation actual_corr = CORRELATIONS[symbol] # Gewichte mit Positionsgröße weight = pos_data.get("size", 1.0) # Berücksichtige Long/Short-Position if pos_data.get("position") == "SHORT": actual_corr = -actual_corr # Short kehrt Korrelation um total_correlation += actual_corr * weight total_weight += weight if total_weight > 0: avg_correlation = total_correlation / total_weight else: avg_correlation = 0.0 # Korrelations-Adjustment if avg_correlation > 0.6: corr_adjustment = 0.7 # Hohe positive Korrelation → Risk reduzieren elif avg_correlation > 0.4: corr_adjustment = 0.85 elif avg_correlation < -0.6: corr_adjustment = 1.1 # Hohe negative Korrelation → natürlicher Hedge elif avg_correlation < -0.4: corr_adjustment = 1.05 else: corr_adjustment = 1.0 # Neutrale Korrelation return avg_correlation, corr_adjustment # Test-Funktion für lokale Validierung if __name__ == "__main__": print("=== Volatility-Adjusted Position Sizing Test ===\n") # Test 1: Normale Volatilität, NEUTRAL Regime print("Test 1: Normale Bedingungen") result1 = calculate_eurusd_position_size( account_equity=100000, atr_14=12.5, # 12.5 Pips volatility_percentile=50, regime="NEUTRAL", risk_percent=0.02 ) print(f" Lots: {result1.lots:.2f}") print(f" Leverage: {result1.leverage}x") print(f" SL: {result1.stop_loss_pips:.1f} Pips, TP: {result1.take_profit_pips:.1f} Pips") print(f" Risk: ${result1.risk_usd:.2f} ({result1.risk_percent:.2%})") print(f" Adjustments: Vol={result1.volatility_adjustment}, Regime={result1.regime_adjustment}, Corr={result1.correlation_adjustment}") # Test 2: Niedrige Volatilität, MEAN_REVERSION Regime print("\nTest 2: Niedrige Volatilität, Mean-Reversion") result2 = calculate_eurusd_position_size( account_equity=100000, atr_14=8.0, # Niedrige Vol volatility_percentile=15, regime="MEAN_REVERSION", risk_percent=0.02 ) print(f" Lots: {result2.lots:.2f}") print(f" Leverage: {result2.leverage}x") print(f" SL: {result2.stop_loss_pips:.1f} Pips, TP: {result2.take_profit_pips:.1f} Pips") print(f" Risk: ${result2.risk_usd:.2f} ({result2.risk_percent:.2%})") print(f" Adjustments: Vol={result2.volatility_adjustment}, Regime={result2.regime_adjustment}") # Test 3: Hohe Volatilität, TRENDING Regime print("\nTest 3: Hohe Volatilität, Trending") result3 = calculate_eurusd_position_size( account_equity=100000, atr_14=25.0, # Hohe Vol volatility_percentile=85, regime="TRENDING", risk_percent=0.02 ) print(f" Lots: {result3.lots:.2f}") print(f" Leverage: {result3.leverage}x") print(f" SL: {result3.stop_loss_pips:.1f} Pips, TP: {result3.take_profit_pips:.1f} Pips") print(f" Risk: ${result3.risk_usd:.2f} ({result3.risk_percent:.2%})") print(f" Adjustments: Vol={result3.volatility_adjustment}, Regime={result3.regime_adjustment}") # Test 4: Korrelations-Adjustment print("\nTest 4: Korrelations-Adjustment") # Simuliere andere Positionen np.random.seed(42) eurusd_returns = pd.Series(np.random.randn(100) * 0.0001) other_positions = { "GBPUSD": {"position": "LONG", "size": 0.5, "returns": pd.Series(np.random.randn(100) * 0.0001)}, "USDCHF": {"position": "SHORT", "size": 0.3, "returns": pd.Series(np.random.randn(100) * 0.0001)} } avg_corr, corr_adj = calculate_forex_correlation(eurusd_returns, other_positions) print(f" Durchschnittliche Korrelation: {avg_corr:.3f}") print(f" Korrelations-Adjustment: {corr_adj:.2f}") result4 = calculate_eurusd_position_size( account_equity=100000, atr_14=12.5, volatility_percentile=50, regime="NEUTRAL", risk_percent=0.02, correlation_adjustment=corr_adj ) print(f" Lots mit Korrelation: {result4.lots:.2f} (vs. {result1.lots:.2f} ohne)") print(f" Korrelations-Adjustment: {result4.correlation_adjustment}") # Zusammenfassung print("\n=== Test Summary ===") print("✅ Volatility-Adjusted Position Sizing ist funktionsfähig!") print("\nKey Features:") print(" - Volatilitäts-Adjustment (0.4x - 1.5x)") print(" - Regime-Adjustment (MEAN_REVERSION/TRENDING/NEUTRAL)") print(" - Korrelations-Adjustment für Forex-Paare") print(" - ATR-basierte SL/TP-Berechnung") print(" - Hebel-Begrenzung und Risk-Management")