import pandas as pd
import numpy as np

class RiskManager:
    """
    Manages trade sizing, stop-loss/take-profit calculation, and risk throttling based on portfolio equity.
    Implements all risk controls from the document including:
    - Fixed fractional risk (1%) split into three legs (50%/25%/25%)
    - Conviction-based stop loss adjustments
    - Drawdown throttling (5% and 10% rules)
    - Daily loss limits (3%)
    - Weekly compounding
    - Leverage cap enforcement
    """

    def __init__(self, risk_config: dict):
        """Initializes the RiskManager with its configuration."""
        self.config = risk_config
        self.initial_equity = self.config.get('INITIAL_EQUITY', 100000.0)
        self.current_equity = self.initial_equity
        self.peak_equity = self.initial_equity
        self.current_drawdown_pct = 0.0
        self.daily_pnl = 0.0
        self.skip_trading = False
        self.skip_days = 0

        # Track equity at start of day and week
        self.equity_at_start_of_day = self.initial_equity
        self.equity_at_start_of_week = self.initial_equity
        self.last_reset_date = None
        self.last_week_reset = None

        # Risk parameters
        self.base_risk_fraction = self.config.get('FIXED_FRACTIONAL_RISK', 0.01)
        self.daily_loss_limit = self.config.get('DAILY_LOSS_LIMIT', 0.03)
        self.max_leverage_cap = self.config.get('MAX_LEVERAGE_CAP', 2.0)

        # Drawdown throttle rules
        self.drawdown_throttles = sorted(
            self.config.get('DRAWDOWN_THROTTLES', [
                {'level': 0.05, 'factor': 0.5},
                {'level': 0.10, 'factor': 0.0}
            ]),
            key=lambda x: x['level'],
            reverse=True
        )

        self.execution_params = self.config.get('EXECUTION_PARAMS', {})
        self.use_conviction_scaling = self.config.get('USE_CONVICTION_SCALING', True)

        # Pip configuration
        self.pip_config = self.config.get('PIP_CONFIG', {
            'default': {'multiplier': 10000, 'pip_value_per_micro_lot': 0.10},
            'JPY': {'multiplier': 100, 'pip_value_per_micro_lot': 0.10}
        })

    def start_new_day(self, current_date):
        if self.last_reset_date != current_date:
            self.equity_at_start_of_day = self.current_equity
            self.daily_pnl = 0.0
            self.skip_trading = False
            self.last_reset_date = current_date

    def start_new_week(self, current_week):
        if self.last_week_reset != current_week:
            self.equity_at_start_of_week = self.current_equity
            self.last_week_reset = current_week

    def update_equity(self, pnl: float):
        self.current_equity += pnl
        self.daily_pnl += pnl
        self.peak_equity = max(self.peak_equity, self.current_equity)

        # Drawdown calculation
        if self.peak_equity > 0:
            self.current_drawdown_pct = (self.peak_equity - self.current_equity) / self.peak_equity
        else:
            self.current_drawdown_pct = 0.0

        # Daily loss limit enforcement
        daily_limit_amt = self.daily_loss_limit * self.equity_at_start_of_day
        if self.daily_pnl < 0 and abs(self.daily_pnl) >= daily_limit_amt:
            self.skip_trading = True

    def _get_current_risk_fraction(self) -> float:
        if self.skip_trading or self.skip_days > 0:
            return 0.0
        risk_fraction = self.base_risk_fraction
        for throttle in self.drawdown_throttles:
            if self.current_drawdown_pct >= throttle['level']:
                risk_fraction *= throttle['factor']
                break
        return risk_fraction

    def check_weekly_compounding(self, current_weekday: str):
        if current_weekday == 'Friday' and self.equity_at_start_of_week > 0:
            weekly_return = (self.current_equity - self.equity_at_start_of_week) / self.equity_at_start_of_week
            if weekly_return >= 0.05:
                self.initial_equity = self.current_equity
                self.peak_equity = self.current_equity
                self.current_drawdown_pct = 0.0
                self.skip_days = 0

    def _get_pip_config(self, ticker: str) -> dict:
        for currency, cfg in self.pip_config.items():
            if currency != 'default' and currency in ticker.upper():
                return cfg
        return self.pip_config['default']

    def calculate_trade_parameters(self, signal: int, entry_price: float, current_atr: float,
                                   action_label: str) -> dict:
        # Early exits
        if self.skip_trading or self.skip_days > 0: return {}
        if signal == 0 or not action_label: return {}
        if pd.isna(entry_price) or pd.isna(current_atr) or entry_price <= 0 or current_atr <= 0: return {}

        # Stop-loss calculation
        atr_mult = self.config.get('ATR_MULTIPLIER', 1.5)
        sl_price = entry_price - (current_atr * atr_mult) if signal == 1 else entry_price + (current_atr * atr_mult)
        if (signal == 1 and sl_price >= entry_price) or (signal == -1 and sl_price <= entry_price): return {}
        stop_distance = abs(entry_price - sl_price)
        if stop_distance == 0: return {}

        # Risk fraction and amount
        current_risk_fraction = self._get_current_risk_fraction()
        if current_risk_fraction <= 0: return {}
        total_risk_amt = self.current_equity * current_risk_fraction

        # Pip-based sizing
        ticker = self.config.get('ticker', 'EURUSD')
        pip_cfg = self._get_pip_config(ticker)
        stop_distance_pips = stop_distance * pip_cfg['multiplier']
        pip_value = pip_cfg['pip_value_per_micro_lot']
        if stop_distance_pips <= 0: return {}

        # Allocate risk across three legs
        legs = [0.5, 0.25, 0.25]
        micro_lot_sizes = [ (total_risk_amt * frac) / (stop_distance_pips * pip_value) for frac in legs ]
        leg_units = [ ml * 1000 for ml in micro_lot_sizes ]
        position_units = sum(leg_units)

        # Enforce leverage cap
        leverage_cap_units = (self.current_equity * self.max_leverage_cap) / entry_price
        if position_units > leverage_cap_units:
            scale = leverage_cap_units / position_units
            leg_units = [ u * scale for u in leg_units ]
            position_units = leverage_cap_units

        # Compute take-profits
        tp1 = entry_price + stop_distance if signal == 1 else entry_price - stop_distance
        tp2 = entry_price + stop_distance * 2.0 if signal == 1 else entry_price - stop_distance * 2.0

        rr_ratio = abs(tp1 - entry_price) / stop_distance

        return {
            'direction': 'long' if signal == 1 else 'short',
            'entry_price': entry_price,
            'stop_loss_price': sl_price,
            'take_profit_1': tp1,
            'take_profit_2': tp2,
            'position_size': position_units,
            'leg1_size': leg_units[0],
            'leg2_size': leg_units[1],
            'leg3_size': leg_units[2],
            'risk_fraction': current_risk_fraction,
            'risk_amount': total_risk_amt,
            'stop_distance': stop_distance,
            'stop_distance_pips': stop_distance_pips,
            'signal': signal,
            'risk_reward_ratio': rr_ratio
        }
import pandas as pd
import numpy as np
class RiskManager:
    """
    Manages trade sizing, stop-loss/take-profit calculation, and risk throttling based on portfolio equity.
    Implements all risk controls from the document including:
    - Fixed fractional risk (1%) split into three legs (50%/25%/25%)
    - Conviction-based stop loss adjustments
    - Drawdown throttling (5% and 10% rules)
    - Daily loss limits (3%)
    - Weekly compounding
    """

    def __init__(self, risk_config: dict):
        """Initializes the RiskManager with its configuration."""
        self.config = risk_config
        self.initial_equity = self.config.get('INITIAL_EQUITY', 100000.0)
        self.current_equity = self.initial_equity
        self.peak_equity = self.initial_equity
        self.current_drawdown_pct = 0.0
        self.daily_pnl = 0.0
        self.skip_trading = False
        self.skip_days = 0

        # NEW: Track equity at start of day and week for proper calculations
        self.equity_at_start_of_day = self.initial_equity
        self.equity_at_start_of_week = self.initial_equity
        self.last_reset_date = None
        self.last_week_reset = None

        # Risk parameters
        self.base_risk_fraction = self.config.get('FIXED_FRACTIONAL_RISK', 0.01)
        self.daily_loss_limit = self.config.get('DAILY_LOSS_LIMIT', 0.03)

        # Drawdown throttle rules
        self.drawdown_throttles = sorted(
            self.config.get('DRAWDOWN_THROTTLES', [
                {'level': 0.05, 'factor': 0.5},
                {'level': 0.10, 'factor': 0.0}
            ]),
            key=lambda x: x['level'],
            reverse=True
        )

        self.execution_params = self.config.get('EXECUTION_PARAMS', {})
        self.use_conviction_scaling = self.config.get('USE_CONVICTION_SCALING', True)

        # NEW: Configurable pip values
        self.pip_config = self.config.get('PIP_CONFIG', {
            'default': {'multiplier': 10000, 'pip_value_per_micro_lot': 0.10},
            'JPY': {'multiplier': 100, 'pip_value_per_micro_lot': 0.10}
        })

    def start_new_day(self, current_date):
        """
        FIXED: Call this at the start of each trading day to reset daily tracking.
        Args:
            current_date: Current date (datetime or string)
        """
        if self.last_reset_date != current_date:
            self.equity_at_start_of_day = self.current_equity
            self.daily_pnl = 0.0
            self.skip_trading = False
            self.last_reset_date = current_date

    def start_new_week(self, current_week):
        """
        FIXED: Call this at the start of each trading week.
        Args:
            current_week: Current week identifier (e.g., week number or date)
        """
        if self.last_week_reset != current_week:
            self.equity_at_start_of_week = self.current_equity
            self.last_week_reset = current_week

    def update_equity(self, pnl: float):
        """
        FIXED: Updates the manager's state with the PnL from the last trade.
        Args:
            pnl: Profit/loss from the last trade
        """
        self.current_equity += pnl
        self.daily_pnl += pnl
        self.peak_equity = max(self.peak_equity, self.current_equity)

        # Calculate current drawdown percentage
        if self.peak_equity > 0:
            self.current_drawdown_pct = (self.peak_equity - self.current_equity) / self.peak_equity
        else:
            self.current_drawdown_pct = 0.0

        # FIXED: Daily loss control using equity at start of day
        daily_loss_limit_amount = self.daily_loss_limit * self.equity_at_start_of_day
        if self.daily_pnl < 0 and abs(self.daily_pnl) >= daily_loss_limit_amount:
            self.skip_trading = True
            # print(f"Daily loss limit reached. Daily PnL: {self.daily_pnl:.2f}, Limit: {daily_loss_limit_amount:.2f}")

    def _get_current_risk_fraction(self) -> float:
        """Determines the current risk fraction after applying all throttles."""
        if self.skip_trading or self.skip_days > 0:
            return 0.0
            
        risk_fraction = self.base_risk_fraction
        
        # Apply drawdown throttles (Page 34)
        for throttle in self.drawdown_throttles:
            if self.current_drawdown_pct >= throttle['level']:
                risk_fraction *= throttle['factor']
                break
                
        return risk_fraction

    def check_weekly_compounding(self, current_weekday: str):
        """
        FIXED: Implements weekly compounding logic using actual weekly returns.
        Call this at end of each day with weekday name (e.g., 'Friday').
        """
        if current_weekday == 'Friday' and self.equity_at_start_of_week > 0:
            # FIXED: Calculate actual weekly return
            weekly_return = (self.current_equity - self.equity_at_start_of_week) / self.equity_at_start_of_week
            
            if weekly_return >= 0.05:  # 5% weekly return threshold
                # print(f"Weekly compounding triggered. Weekly return: {weekly_return:.2%}")
                self.initial_equity = self.current_equity
                # Reset any active drawdown throttles
                self.peak_equity = self.current_equity
                self.current_drawdown_pct = 0.0
                self.skip_days = 0

    def _get_pip_config(self, ticker: str) -> dict:
        """
        FIXED: Gets pip configuration based on ticker.
        Args:
            ticker: Currency pair ticker (e.g., 'USDJPY', 'EURUSD')
        Returns:
            dict: Pip configuration with multiplier and pip_value_per_micro_lot
        """
        for currency, config in self.pip_config.items():
            if currency != 'default' and currency in ticker.upper():
                return config
        return self.pip_config['default']

    def calculate_trade_parameters(self, signal: int, entry_price: float, current_atr: float, 
                                action_label: str) -> dict:
        """
        FIXED: Calculates trade parameters with improved pip value handling.
        """
        # Skip if risk controls are active
        if self.skip_trading or self.skip_days > 0: return {}
        # Enhanced input validation
        if signal == 0 or not action_label: return {}
        if pd.isna(entry_price) or pd.isna(current_atr) or entry_price <= 0 or current_atr <= 0: return {}

        # --- MODIFIED: Use ATR_MULTIPLIER from config instead of hardcoded value ---
        atr_multiplier = self.config.get('ATR_MULTIPLIER', 2.0)
        
        # Calculate Stop Loss
        stop_loss_price = entry_price - (current_atr * atr_multiplier) if signal == 1 else entry_price + (current_atr * atr_multiplier)
        if (signal == 1 and stop_loss_price >= entry_price) or (signal == -1 and stop_loss_price <= entry_price): return {}
        
        stop_distance = abs(entry_price - stop_loss_price)
        if stop_distance == 0: return {}

        # Calculate Risk and Pip values
        current_risk_fraction = self._get_current_risk_fraction()
        if current_risk_fraction <= 0: return {}
        
        total_risk_amount = self.current_equity * current_risk_fraction

        # FIXED: Use configurable pip values
        ticker = self.config.get('ticker', 'EURUSD')
        pip_config = self._get_pip_config(ticker)
        stop_distance_pips = stop_distance * pip_config['multiplier']
        pip_value_per_micro_lot = pip_config['pip_value_per_micro_lot']

        if stop_distance_pips <= 0: return {}
        
        # --- NEW: Leg-based sizing ---
        risk_budget_leg1 = total_risk_amount * 0.50  # 50% for TP1
        risk_budget_leg2 = total_risk_amount * 0.25  # 25% for TP2
        risk_budget_leg3 = total_risk_amount * 0.25  # 25% for Trailing EMA

        size_leg1_micro_lots = risk_budget_leg1 / (stop_distance_pips * pip_value_per_micro_lot)
        size_leg2_micro_lots = risk_budget_leg2 / (stop_distance_pips * pip_value_per_micro_lot)
        size_leg3_micro_lots = risk_budget_leg3 / (stop_distance_pips * pip_value_per_micro_lot)
        
        # Convert micro lots (1k) to base units
        leg1_size_units = size_leg1_micro_lots * 1000
        leg2_size_units = size_leg2_micro_lots * 1000
        leg3_size_units = size_leg3_micro_lots * 1000

        position_size_micro_lots = size_leg1_micro_lots + size_leg2_micro_lots + size_leg3_micro_lots
        position_size = leg1_size_units + leg2_size_units + leg3_size_units

        if position_size <= 0: return {}

        # Calculate Take Profit levels based on 1R, 2R
        take_profit_1 = entry_price + stop_distance if signal == 1 else entry_price - stop_distance
        take_profit_2 = entry_price + stop_distance * 2.0 if signal == 1 else entry_price - stop_distance * 2.0

        # Add risk-reward ratio
        risk_reward_ratio = (take_profit_1 - entry_price) / (entry_price - stop_loss_price) if signal == 1 \
            else (entry_price - take_profit_1) / (stop_loss_price - entry_price)

        return {
            "direction": 'long' if signal == 1 else 'short',
            "entry_price": entry_price,
            "stop_loss_price": stop_loss_price,
            "take_profit_1": take_profit_1,
            "take_profit_2": take_profit_2,
            "position_size": position_size,
            "leg1_size": leg1_size_units,
            "leg2_size": leg2_size_units,
            "leg3_size": leg3_size_units,
            "risk_fraction": current_risk_fraction,
            "risk_amount": total_risk_amount,
            "stop_distance": stop_distance,
            "stop_distance_pips": stop_distance_pips,
            "signal": signal,
            "risk_reward_ratio": risk_reward_ratio
        }

what are difference in this two codes