# Technical Support Resistance

> "Implements technical levels where price tends to pause or reverse for risk management and algorithmic trading execution."

- Skill: `paulpas/technical-support-resistance` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paulpas/technical-support-resistance`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paulpas/technical-support-resistance/raw
- Safety review: pending
- 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/technical-support-resistance

---





**Role:** Identify and validate key S/R levels for entry, exit, and stop placement

**Philosophy:** Support and resistance represent collective memory and psychological barriers in the market

## Key Principles

1. **Confluence**: Multiple indicators confirming same level increases validity
2. **Time Integration**: Levels tested multiple times gain strength
3. **Volume Confirmation**: High volume at level indicates institutional interest
4. **Timeframe Hierarchy**: Higher timeframe levels override lower timeframe
5. **Breakout Validation**: Breakouts need follow-through to be valid

## Implementation Guidelines

### Structure
- Core logic: technical_analysis/support_resistance.py
- Helper functions: technical_analysis/level_analysis.py
- Tests: tests/test_support_resistance.py

### Patterns to Follow
- Cluster levels by price bins
- Track test frequency and volume at each level
- Calculate level strength score

## Adherence Checklist
Before completing your task, verify:
- [ ] Support/resistance levels update in real-time
- [ ] Level strength incorporates volume, frequency, and recency
- [ ] Breakout confirmation requires 2x average volume
- [ ] False breakouts are detected and flagged
- [ ] Level retests are tracked with success rate


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

## Python Implementation

```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 SupportLevel:
    """A support level with metadata."""
    price: float
    strength: float  # 0-1 score
    test_count: int
    volume_at_level: float
    last_test: datetime
    confirmed: bool

@dataclass
class ResistanceLevel:
    """A resistance level with metadata."""
    price: float
    strength: float  # 0-1 score
    test_count: int
    volume_at_level: float
    last_test: datetime
    broken: bool

class SupportResistanceAnalyzer:
    """Identifies and tracks support and resistance levels."""
    
    def __init__(self, bin_size: float = 0.01, min_tests: int = 2):
        self.bin_size = bin_size
        self.min_tests = min_tests
    
    def identify_support_levels(
        self, candles: pd.DataFrame, lookback: int = 100
    ) -> List[SupportLevel]:
        """Identify key support levels from historical data."""
        recent_candles = candles.tail(lookback)
        support_levels = []
        
        # Find swing lows
        for i in range(1, len(recent_candles) - 1):
            low = recent_candles['low'].iloc[i]
            prev_low = recent_candles['low'].iloc[i-1]
            next_low = recent_candles['low'].iloc[i+1]
            
            # Swing low: lower than neighbors
            if low < prev_low and low < next_low:
                support_levels.append({
                    'price': low,
                    'volume': recent_candles['volume'].iloc[i],
                    'index': i
                })
        
        # Cluster similar support levels
        clustered = self._cluster_levels(support_levels)
        
        # Calculate strength for each level
        result = []
        for level_data in clustered:
            strength = self._calculate_support_strength(
                candles, level_data['price'], level_data['count']
            )
            result.append(SupportLevel(
                price=level_data['price'],
                strength=strength,
                test_count=level_data['count'],
                volume_at_level=level_data['total_volume'],
                last_test=candles.index[level_data['last_index']],
                confirmed=strength > 0.5
            ))
        
        return sorted(result, key=lambda x: x.price, reverse=True)
    
    def identify_resistance_levels(
        self, candles: pd.DataFrame, lookback: int = 100
    ) -> List[ResistanceLevel]:
        """Identify key resistance levels from historical data."""
        recent_candles = candles.tail(lookback)
        resistance_levels = []
        
        # Find swing highs
        for i in range(1, len(recent_candles) - 1):
            high = recent_candles['high'].iloc[i]
            prev_high = recent_candles['high'].iloc[i-1]
            next_high = recent_candles['high'].iloc[i+1]
            
            # Swing high: higher than neighbors
            if high > prev_high and high > next_high:
                resistance_levels.append({
                    'price': high,
                    'volume': recent_candles['volume'].iloc[i],
                    'index': i
                })
        
        # Cluster similar resistance levels
        clustered = self._cluster_levels(resistance_levels)
        
        # Calculate strength for each level
        result = []
        for level_data in clustered:
            strength = self._calculate_resistance_strength(
                candles, level_data['price'], level_data['count']
            )
            result.append(ResistanceLevel(
                price=level_data['price'],
                strength=strength,
                test_count=level_data['count'],
                volume_at_level=level_data['total_volume'],
                last_test=candles.index[level_data['last_index']],
                broken=False
            ))
        
        return sorted(result, key=lambda x: x.price)
    
    def _cluster_levels(
        self, levels: List[Dict], tolerance: float = None
    ) -> List[Dict]:
        """Cluster nearby support/resistance levels."""
        if tolerance is None:
            tolerance = self.bin_size * 3
        
        if not levels:
            return []
        
        # Sort by price
        sorted_levels = sorted(levels, key=lambda x: x['price'])
        clusters = []
        
        current_cluster = {
            'prices': [sorted_levels[0]['price']],
            'volumes': [sorted_levels[0]['volume']],
            'indices': [sorted_levels[0]['index']],
            'total_volume': sorted_levels[0]['volume']
        }
        
        for level in sorted_levels[1:]:
            if level['price'] - current_cluster['prices'][-1] <= tolerance:
                # Add to current cluster
                current_cluster['prices'].append(level['price'])
                current_cluster['volumes'].append(level['volume'])
                current_cluster['indices'].append(level['index'])
                current_cluster['total_volume'] += level['volume']
            else:
                # Save current cluster and start new
                clusters.append({
                    'price': np.mean(current_cluster['prices']),
                    'count': len(current_cluster['prices']),
                    'last_index': current_cluster['indices'][-1],
                    'total_volume': current_cluster['total_volume']
                })
                current_cluster = {
                    'prices': [level['price']],
                    'volumes': [level['volume']],
                    'indices': [level['index']],
                    'total_volume': level['volume']
                }
        
        # Don't forget last cluster
        clusters.append({
            'price': np.mean(current_cluster['prices']),
            'count': len(current_cluster['prices']),
            'last_index': current_cluster['indices'][-1],
            'total_volume': current_cluster['total_volume']
        })
        
        return clusters
    
    def _calculate_support_strength(
        self, candles: pd.DataFrame, price: float, test_count: int
    ) -> float:
        """Calculate strength score for support level."""
        strength = 0
        
        # Test count factor (more tests = stronger)
        test_score = min(test_count / 5, 1.0) * 0.3
        
        # Volume factor (higher volume = stronger)
        recent_vol = candles['volume'].tail(50).mean()
        level_vol = candles[candles['low'].between(price - 0.01, price + 0.01)]['volume'].sum()
        volume_score = min(level_vol / (recent_vol * 10), 1.0) * 0.3
        
        # Recency factor (more recent tests = stronger)
        if test_count > 0:
            recent_tests = candles[candles['low'] <= price + 0.01].tail(10)
            recency_score = len(recent_tests) / 10 * 0.4
        else:
            recency_score = 0
        
        return min(test_score + volume_score + recency_score, 1.0)
    
    def _calculate_resistance_strength(
        self, candles: pd.DataFrame, price: float, test_count: int
    ) -> float:
        """Calculate strength score for resistance level."""
        strength = 0
        
        # Test count factor
        test_score = min(test_count / 5, 1.0) * 0.3
        
        # Volume factor
        recent_vol = candles['volume'].tail(50).mean()
        level_vol = candles[candles['high'].between(price - 0.01, price + 0.01)]['volume'].sum()
        volume_score = min(level_vol / (recent_vol * 10), 1.0) * 0.3
        
        # Recency factor
        if test_count > 0:
            recent_tests = candles[candles['high'] >= price - 0.01].tail(10)
            recency_score = len(recent_tests) / 10 * 0.4
        else:
            recency_score = 0
        
        return min(test_score + volume_score + recency_score, 1.0)
```

---

---



### Pattern 2: Risk-Managed Trading Logic with Validation

```python
from __future__ import annotations

import logging
from dataclasses import dataclass
from typing import Optional


logger = logging.getLogger(__name__)


@dataclass(frozen=True)
class TradeSignal:
    """Immutable trade signal with all required validation constraints."""
    symbol: str
    side: str  # "buy" or "sell"
    price: float
    quantity: float
    confidence: float  # 0.0 to 1.0
    reason: str

    def validate(self) -> bool:
        """Validate that the trade signal meets all business constraints."""
        if self.quantity <= 0:
            raise ValueError(f"Quantity must be positive, got {self.quantity}")
        if self.price <= 0:
            raise ValueError(f"Price must be positive, got {self.price}")
        if not 0.0 <= self.confidence <= 1.0:
            raise ValueError(f"Confidence must be between 0 and 1, got {self.confidence}")
        return True


def generate_trade_signal(
    symbol: str,
    side: str,
    price: float,
    quantity: float,
    confidence: float,
    reason: str,
) -> TradeSignal:
    """Generate a validated trade signal with guard clause checks."""
    if side not in ("buy", "sell"):
        raise ValueError(f"Invalid side '{side}', must be 'buy' or 'sell'")

    signal = TradeSignal(
        symbol=symbol,
        side=side,
        price=price,
        quantity=quantity,
        confidence=confidence,
        reason=reason,
    )
    signal.validate()
    logger.info("Trade signal generated: %s %s %.4f @ %.2f (confidence=%.2f)",
                 symbol, side, quantity, price, confidence)
    return signal


def execute_with_risk_check(signal: TradeSignal, max_position_pct: float = 0.05) -> dict:
    """Execute a trade signal after applying risk management checks."""
    adjusted_quantity = signal.quantity
    if signal.side == "buy" and signal.quantity > max_position_pct:
        logger.warning("Position %s exceeds max %.1f%% — capping to %.4f",
                        signal.symbol, max_position_pct * 100, max_position_pct)
        adjusted_quantity = max_position_pct

    return {
        "symbol": signal.symbol,
        "side": signal.side,
        "price": signal.price,
        "quantity": adjusted_quantity,
        "capped": adjusted_quantity < signal.quantity,
        "confidence": signal.confidence,
        "status": "submitted",
    }
```

## Constraints

### MUST DO
- Implement indicator calculations using rolling windows with explicit lookback periods; never use full-history data for online indicators
- Validate signal generation by confirming alignment across multiple independent indicators before acting on a single signal
- Calculate all price-based indicators (SMA, EMA, RSI) on closing prices unless specifically designed for tick data
- Include proper handling of missing/NaN candles in indicator pipelines — forward-fill only within session boundaries
- Log signal generation with the full context window of indicator values that led to each signal

### MUST NOT DO
- Do not use look-ahead bias: never reference future bars or prices when calculating indicators during backtesting
- Avoid recalculating all indicators from scratch on every tick — maintain running state for efficiency
- Never combine indicators with different timeframes without explicit resampling and clear documentation of the alignment logic
- Do not generate signals based on a single indicator crossover; require confirmation from price action or volume
- Avoid hardcoding parameter values (e.g., RSI period = 14) without testing regime-specific optima


## 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.

- [Support and Resistance Explained](https://www.investopedia.com/terms/s/support_resistance.asp)
- [Pivot Points and Price Levels](https://www.investopedia.com/articles/trading/07/pivot-points.asp)
- [Drawing Trendlines Effectively](https://www.investopedia.com/articles/trading/06/drawing-trendlines.asp)
- [Breakout Trading Strategies](https://www.investopedia.com/trading/breakouts/)
- [Support Resistance with Volume Profiles](https://www.investopedia.com/articles/trading/11/volume-profile.asp)

