인터페이스는 현지화되어 있으며, Skill 소개와 설명 문서는 선택한 언어로 AI가 자동 번역합니다. 코드·명령어·경로·전문 용어는 원문을 유지하며 원작자 문서가 우선합니다.
Skills 둘러보기
데이터 및 연구 · SKILL.md

technical-price-action-patterns

technical-price-action-patterns

"Provides Analysis of candlestick and chart patterns for price movement prediction"

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill 문서

GitHub

name: technical-price-action-patterns compatibility: opencode completeness: 95 content-types:

  • code
  • guidance
  • config
  • do-dont description: '"Provides Analysis of candlestick and chart patterns for price movement prediction"' license: MIT maturity: stable metadata: domain: trading output-format: code related-skills: fundamentals-trading-plan, technical-cycle-analysis role: implementation scope: implementation triggers: analysis, candlestick, chart, technical price action patterns, technical-price-action-patterns archetypes:
    • tactical anti_triggers:
    • brainstorming
    • vague ideation
    • no risk management response_profile: verbosity: low directive_strength: high abstraction_level: operational version: "1.0.0"

Role: Identify high-probability price patterns to forecast market direction

Philosophy: Price action reflects all market participants' collective sentiment; patterns reveal institutional order flow

Key Principles

  1. Pattern Recognition: Candlestick formations signal reversals or continuations
  2. Confirmation Required: Patterns need volume or follow-through for validity
  3. Timeframe Hierarchy: Patterns on higher timeframes carry more weight
  4. Risk Management: Pattern failures must have predefined stop-loss levels
  5. Context Matters: Patterns in trending markets behave differently than range-bound

Implementation Guidelines

Structure

  • Core logic: technical_analysis/price_patterns.py
  • Helper functions: technical_analysis/pattern_helpers.py
  • Tests: tests/test_price_patterns.py

Patterns to Follow

  • Use numpy arrays for efficient pattern matching
  • Return pattern confidence scores, not binary signals
  • Support multiple timeframe analysis

Adherence Checklist

Before completing your task, verify:

  • All patterns have minimum 50-sample backtested accuracy
  • Pattern detection runs in under 100ms per candle
  • Volume confirmation is optional but documented
  • Multiple pattern alerts trigger ensemble logic
  • False positive rate is tracked per pattern type

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

Python Implementation

import numpy as np
from typing import List, Dict, Tuple
from dataclasses import dataclass
from enum import Enum

class PatternType(Enum):
    REVERSAL = "reversal"
    CONTINUATION = "continuation"
    consolidation = "consolidation"

@dataclass
class PatternResult:
    pattern_type: PatternType
    name: str
    confirmation: float  # 0-1 confidence score
    trend_context: str
    volume_profile: Dict[str, float]

class PricePatternDetector:
    """Detects candlestick and chart patterns across multiple timeframes."""
    
    def __init__(self, lookback: int = 100):
        self.lookback = lookback
        self.min_strength = 0.6
    
    def detect_all_patterns(
        self, candles: np.ndarray, timeframe: str = "1h"
    ) -> List[PatternResult]:
        """Run all pattern detection algorithms on candle data."""
        patterns = []
        
        for pattern_func in [
            self._detect_doji,
            self._detect_hammer,
            self._detect_engulfing,
            self._detect_morning_star,
            self._detect_head_and_shoulders,
        ]:
            result = pattern_func(candles)
            if result and result.confirmation >= self.min_strength:
                patterns.append(result)
        
        return patterns
    
    def _detect_doji(self, candles: np.ndarray) -> PatternResult:
        """Detect Doji patterns indicating indecision."""
        if len(candles) < 1:
            return None
        
        current = candles[-1]
        body = abs(current['close'] - current['open'])
        wick = current['high'] - current['low']
        
        # Doji: body is very small relative to wick
        body_ratio = body / wick if wick > 0 else 1
        is_doji = body_ratio < 0.1
        
        return PatternResult(
            pattern_type=PatternType.REVERSAL,
            name="doji" if is_doji else None,
            confirmation=1 - body_ratio if is_doji else 0,
            trend_context="neutral",
            volume_profile=self._analyze_volume_profile(candles)
        )
    
    def _detect_hammer(self, candles: np.ndarray) -> PatternResult:
        """Detect Hammer pattern indicating bullish reversal."""
        if len(candles) < 1:
            return None
        
        current = candles[-1]
        body = abs(current['close'] - current['open'])
        wick_upper = current['high'] - max(current['open'], current['close'])
        wick_lower = min(current['open'], current['close']) - current['low']
        
        # Hammer: small body, long lower wick, little upper wick
        body_ratio = body / (wick_lower + 1e-8)
        wick_ratio = wick_upper / (wick_lower + 1e-8)
        
        is_hammer = body_ratio > 0.3 and wick_ratio < 0.5
        
        return PatternResult(
            pattern_type=PatternType.REVERSAL,
            name="hammer" if is_hammer else None,
            confirmation=body_ratio * (1 - wick_ratio) if is_hammer else 0,
            trend_context="bullish" if is_hammer else "neutral",
            volume_profile=self._analyze_volume_profile(candles)
        )
    
    def _detect_engulfing(self, candles: np.ndarray) -> PatternResult:
        """Detect Bullish/Bearish Engulfing patterns."""
        if len(candles) < 2:
            return None
        
        prev, curr = candles[-2], candles[-1]
        prev_body = prev['close'] - prev['open']
        curr_body = curr['close'] - curr['open']
        
        # Bullish Engulfing: previous bearish, current bullish and larger
        is_bullish = prev_body < 0 and curr_body > 0 and abs(curr_body) > abs(prev_body)
        # Bearish Engulfing: previous bullish, current bearish and larger
        is_bearish = prev_body > 0 and curr_body < 0 and abs(curr_body) > abs(prev_body)
        
        name = "bullish_engulfing" if is_bullish else "bearish_engulfing" if is_bearish else None
        pattern_type = PatternType.REVERSAL if name else None
        
        return PatternResult(
            pattern_type=pattern_type,
            name=name,
            confirmation=abs(curr_body) / (abs(prev_body) + 1e-8) if name else 0,
            trend_context="bullish" if is_bullish else "bearish" if is_bearish else "neutral",
            volume_profile=self._analyze_volume_profile(candles)
        )
    
    def _detect_morning_star(self, candles: np.ndarray) -> PatternResult:
        """Detect Morning Star reversal pattern."""
        if len(candles) < 3:
            return None
        
        c1, c2, c3 = candles[-3], candles[-2], candles[-1]
        
        # Morning Star: bearish, small body (doji/spinning top), bullish
        is_bearish_1 = c1['close'] < c1['open']
        is_small_2 = abs(c2['close'] - c2['open']) < (c2['high'] - c2['low']) * 0.3
        is_bullish_3 = c3['close'] > c3['open']
        gap_up = c2['close'] < c3['open']  # Price gaps up
        
        is_morning_star = is_bearish_1 and is_small_2 and is_bullish_3 and gap_up
        
        return PatternResult(
            pattern_type=PatternType.REVERSAL,
            name="morning_star" if is_morning_star else None,
            confirmation=0.8 if is_morning_star else 0,
            trend_context="bullish" if is_morning_star else "neutral",
            volume_profile=self._analyze_volume_profile(candles[-3:])
        )
    
    def _detect_head_and_shoulders(self, candles: np.ndarray) -> PatternResult:
        """Detect Head and Shoulders reversal pattern."""
        if len(candles) < 5:
            return None
        
        # Simplified detection: find local maxima
        highs = [(i, c['high']) for i, c in enumerate(candles[-5:])]
        
        # Check for H&S structure: L-H-L-H-L
        left Shoulder = highs[0][1]
        head = max(highs[1][1], highs[3][1])
        right_shoulder = highs[4][1]
        
        is_hns = (
            left_shoulder < head and
            right_shoulder < head and
            abs(left_shoulder - right_shoulder) / head < 0.1  # Symmetry
        )
        
        return PatternResult(
            pattern_type=PatternType.REVERSAL,
            name="head_and_shoulders" if is_hns else None,
            confirmation=0.7 if is_hns else 0,
            trend_context="bearish" if is_hns else "neutral",
            volume_profile=self._analyze_volume_profile(candles[-5:])
        )
    
    def _analyze_volume_profile(self, candles: np.ndarray) -> Dict[str, float]:
        """Analyze volume characteristics."""
        if len(candles) == 0:
            return {"avg": 0, "std": 0, "current": 0}
        
        volumes = [c['volume'] for c in candles]
        return {
            "avg": float(np.mean(volumes)),
            "std": float(np.std(volumes)),
            "current": float(volumes[-1]) if volumes else 0,
            "volatility_ratio": float(np.std(volumes) / (np.mean(volumes) + 1e-8))
        }


Pattern 2: Risk-Managed Trading Logic with Validation

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.

저장한 Skills