# Risk Stop Loss

> "Implements stop loss strategies for risk management for risk management and algorithmic trading execution."

- Skill: `paulpas/risk-stop-loss` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paulpas/risk-stop-loss`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paulpas/risk-stop-loss/raw
- Safety review: PASS (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: paulpas (https://skillmd.com/u/paulpas)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/paulpas/risk-stop-loss

---





**Role:** Implement stop loss mechanisms to limit losses and protect capital

**Philosophy:** Stop losses are not just price levels; they're risk management tools that should adapt to market conditions

## Key Principles

1. **Stop Types**: Fixed percentage, ATR-based, support/resistance, volatility-adjusted
2. **Trailing Stops**: Lock in profits while limiting losses
3. **Stop Placement**: Avoid stop hunting by placing stops beyond obvious levels
4. **Stop Adjustment**: Modify stops as trade progresses
5. **Emergency Stops**: Automatic closure on extreme events

## Implementation Guidelines

### Structure
- Core logic: risk_engine/stop_loss.py
- Helper functions: risk_engine/stop_strategies.py
- Tests: tests/test_stop_loss.py

### Patterns to Follow
- Support multiple stop types with consistent interface
- Track stop trigger rates for analysis
- Implement dynamic stop adjustment

## Adherence Checklist
Before completing your task, verify:
- [ ] Multiple stop loss types implemented
- [ ] Trailing stop logic handles partial closures
- [ ] Stop levels adapt to volatility
- [ ] Stop placement avoids round numbers
- [ ] Emergency stop triggers on extreme moves


Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.

## Python Implementation

### Fixed Percentage Stop
```python
import numpy as np
import pandas as pd
from typing import List, Dict

class StopLossManager:
    def fixed_percentage_stop(self, current_price: float, stop_pct: float = 0.02) -> float:
        """Calculate fixed percentage stop loss based on current price."""
        return current_price * (1 - stop_pct)
```

### Moving Average Stop
```python
    def moving_average_stop(self, current_price: float, moving_average: float, buffer_pct: float = 0.01) -> float:
        """Calculate stop loss using moving average."""
        return moving_average * (1 - buffer_pct)
```

### Price Action Stop
```python
    def price_action_stop(self, current_prices: List[float], period: int) -> float:
        """Calculate stop loss based on the average of the last `period` prices."""
        if len(current_prices) < period:
            raise ValueError("Not enough data points.")
        average_price = sum(current_prices[-period:]) / period
        return average_price * 0.95  # Set stop at 5% below the moving average
```

```python
import numpy as np
import pandas as pd
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
from datetime import datetime

@dataclass
class StopLoss:
    """Stop loss configuration."""
    symbol: str
    entry_price: float
    stop_price: float
    stop_type: str
    active: bool
    trail_distance: float = 0
    trail_threshold: float = 0

class StopLossManager:

    def moving_average_stop(self, current_price: float, moving_average: float, buffer_pct: float = 0.01) -> float:
        """Calculate stop loss using moving average."""
        return moving_average * (1 - buffer_pct)

    def fixed_percentage_stop(self, current_price: float, stop_pct: float = 0.02) -> float:
        """Calculate fixed percentage stop loss based on current price."""
        return current_price * (1 - stop_pct)

    def price_action_stop(self, current_prices: List[float], period: int) -> float:
        """Calculate stop loss based on the average of the last `period` prices."""
        if len(current_prices) < period:
            raise ValueError("Not enough data points.")
        average_price = sum(current_prices[-period:]) / period
        return average_price * 0.95  # Set stop at 5% below the moving average

        """Calculate stop loss using moving average."""
        return moving_average * (1 - buffer_pct)

    def fixed_percentage_stop(self, current_price: float, stop_pct: float = 0.02) -> float:
        """Calculate fixed percentage stop loss based on current price."""
        return current_price * (1 - stop_pct)

        """Calculate stop loss using moving average."""
                """Calculate stop loss using moving average."""
        return moving_average * (1 - buffer_pct)

    def price_action_stop(self, current_prices: List[float], period: int) -> float:
        """Calculate stop loss based on the average of the last `period` prices."""
        if len(current_prices) < period:
            raise ValueError("Not enough data points.")
        average_price = sum(current_prices[-period:]) / period
        return average_price * 0.95  # Set stop at 5% below the moving average

    """Manages stop loss strategies across positions."""
    
    def __init__(
        self,
        atr_period: int = 14,
        max_drawdown_pct: float = 0.10
    ):
        self.atr_period = atr_period
        self.max_drawdown_pct = max_drawdown_pct
    
    def fixed_percentage_stop(
        self, entry_price: float, stop_pct: float = 0.02
    ) -> float:
        """Calculate fixed percentage stop loss level."""
        return entry_price * (1 - stop_pct)
    
    def atr_stop(
        self, current_price: float, atr: float, atr_multiplier: float = 2.0
    ) -> float:
        """Calculate ATR-based stop loss."""
        return current_price - (atr * atr_multiplier)
    
    def support_based_stop(
        self, current_price: float, support_level: float, buffer_pct: float = 0.01
    ) -> float:
        """Place stop below support level."""
        return support_level * (1 - buffer_pct)
    
    def trailing_stop(
        self, current_price: float, highest_price: float,
        trail_pct: float = 0.05
    ) -> float:
        """Calculate trailing stop level."""
        trail_distance = highest_price * trail_pct
        return highest_price - trail_distance
    
    def volatility_adjusted_stop(
        self, current_price: float, atr: float,
        recent_volatility: float, base_atr_mult: float = 2.0
    ) -> float:
        """Adjust stop based on current volatility."""
        volatility_ratio = recent_volatility / atr if atr > 0 else 1.0
        adjusted_mult = base_atr_mult * volatility_ratio
        
        return current_price - (atr * adjusted_mult)
    
    def time_decay_stop(
        self, entry_price: float, days_held: int,
        half_life_days: int = 5
    ) -> float:
        """Move stop closer to entry as position ages."""
        decay_factor = np.exp(-days_held / half_life_days)
        return entry_price * (1 - 0.02 * decay_factor)
    
    def dynamic_stop(
        self, price_history: pd.DataFrame, entry_price: float
    ) -> float:
        """Calculate dynamic stop based on price action."""
        if len(price_history) < 20:
            return self.fixed_percentage_stop(entry_price)
        
        # Use ATR for current volatility
        high = price_history['high']
        low = price_history['low']
        close = price_history['close']
        
        tr1 = high - low
        tr2 = abs(high - close.shift())
        tr3 = abs(low - close.shift())
        tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
        atr = tr.tail(14).mean()
        
        # Stop below recent low
        recent_low = price_history['low'].tail(10).min()
        
        return min(
            self.atr_stop(entry_price, atr),
            self.support_based_stop(entry_price, recent_low)
        )
    
    def emergency_stop(
        self, current_price: float, entry_price: float,
        max_loss_pct: float = 0.15
    ) -> float:
        """Immediate stop if price moves against position."""
        max_loss_amount = entry_price * max_loss_pct
        
        if current_price < entry_price - max_loss_amount:
            return current_price * 0.95  # Immediate closure
        elif current_price > entry_price + max_loss_amount:
            return entry_price + max_loss_amount * 0.5  # Lock in profit
        
        return None  # No emergency stop needed
    
    def update_trailing_stops(
        self, positions: List[Dict], current_prices: Dict[str, float]
    ) -> Dict[str, float]:
        """Update trailing stops for all active positions."""
        updated_stops = {}
        
        for position in positions:
            symbol = position['symbol']
            entry_price = position['entry_price']
            highest_price = position.get('highest_price', entry_price)
            trail_pct = position.get('trail_pct', 0.05)
            
            current_price = current_prices.get(symbol, entry_price)
            
            # Update highest price
            new_highest = max(highest_price, current_price)
            
            # Calculate new stop
            new_stop = self.trailing_stop(current_price, new_highest, trail_pct)
            
            updated_stops[symbol] = new_stop
            
            # Update position with new highest price
            position['highest_price'] = new_highest
        
        return updated_stops
```

---

---

## Constraints

### MUST DO
- Calculate position sizing using a risk-per-trade percentage of portfolio equity, not a fixed dollar amount
- Implement layered risk controls: stop loss → drawdown limit → portfolio-level circuit breaker → kill switch
- Compute VaR using historical simulation with at least 1 year of data and multiple confidence levels (95%, 99%)
- Track correlation matrices across all open positions and flag portfolios where top-3 correlations exceed 0.8
- Log all risk events (stop hits, drawdown warnings, kill switches) with full context including P&L, position state, and market conditions

### MUST NOT DO
- Do not use a stop loss as the sole risk control — always layer with portfolio-level limits
- Avoid recalculating position sizes during active drawdown without regime analysis — volatility is likely elevated
- Never allow a single position to exceed 5% of portfolio equity regardless of signal strength or confidence score
- Do not backtest risk metrics without including slippage, commissions, and partial fills in the simulation
- Avoid using standard deviation alone for VaR when returns show fat tails — use historical simulation or EVT


## Live References

> Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.

- [Stop Loss Explained](https://www.investopedia.com/terms/s/stoploss.asp)
- [Trailing Stop Loss Strategies](https://www.investopedia.com/terms/t/trailing-stop.asp)
- [ATR-Based Stop Loss Placement](https://en.wikipedia.org/wiki/Average_true_range)
- [Stop Loss Order Types](https://www.investopedia.com/trading/stop-loss-orders/)
- [Risk Management with Stops](https://docs.quantconnect.com/tutorials/risk-management)

