Pattern Recognition Implementation
This page provides technical details about the pattern recognition implementation in OnlineTechnicalIndicators.jl.
Overview
Status: ✅ Production Ready Test Coverage: 58/58 tests passing (100%) Performance: ~1-5 μs per candle (comprehensive detector) Memory: O(1) - constant regardless of data length
Architecture
Incremental Processing
All pattern detectors use an incremental (online) approach:
CircBuff for State: Each detector maintains a circular buffer for the required lookback window
- Single-candle patterns: 1 candle
- Two-candle patterns: 2 candles
- Three-candle patterns: 3 candles
O(1) Complexity:
- Memory: Fixed size regardless of data stream length
- Time: Constant processing time per candle
No Reprocessing: Each
fit!()
call processes only the new candle
Type Safety
The implementation uses Julia's type system for safety:
# Enums prevent invalid states
@enum SingleCandlePattern begin
NONE
DOJI
HAMMER
# ...
end
# Parametric types for precision
struct SingleCandlePatternVal{T}
pattern::SingleCandlePattern
confidence::T
direction::Direction
end
Confidence Scoring
Each pattern includes a confidence score (0.0-1.0) calculated from:
- Pattern Fit Quality: How well the candle matches ideal pattern characteristics
- Relative Measurements: Body ratios, shadow lengths, etc.
- Pattern-Specific Factors: Engulfing magnitude, penetration depth, etc.
Example (Hammer):
# Multi-factor confidence
shadow_confidence = min(shadow_to_body_ratio / (threshold * 2), 1.0)
body_confidence = 1.0 - (body_to_range / max_body_ratio)
upper_confidence = 1.0 - (upper_to_range / tolerance)
confidence = (shadow_confidence + body_confidence + upper_confidence) / 3
Implementation Details
Pattern Detector Structure
All detectors follow this structure:
mutable struct PatternDetector{Tohlcv,IN,S} <: TechnicalIndicatorSingleOutput{Tohlcv}
# Standard fields
value::Union{Missing,PatternVal}
n::Int
output_listeners::Series
input_indicator::Union{Missing,TechnicalIndicator}
# Pattern-specific parameters
parameter1::S
parameter2::S
# State management
input_values::CircBuff # Lookback window
# Filters
input_modifier::Function
input_filter::Function
end
Processing Pipeline
function _calculate_new_value(ind::PatternDetector)
# 1. Get candles from lookback window
candles = ind.input_values
# 2. Extract OHLC data
o, h, l, c = candle.open, candle.high, candle.low, candle.close
# 3. Calculate metrics
body = abs(c - o)
total_range = h - l
# ...
# 4. Check pattern conditions
if meets_pattern_criteria
# 5. Calculate confidence
confidence = calculate_confidence(...)
# 6. Return pattern
return PatternVal(PATTERN_TYPE, confidence, direction)
else
return PatternVal(NONE, 0.0, NEUTRAL)
end
end
Performance Characteristics
Benchmarks
Operation | Time | Memory |
---|---|---|
Single pattern detection | ~100-500 ns | ~1-3 KB |
Comprehensive detection | ~1-5 μs | ~15-20 KB |
1 million candles | ~1-5 seconds | O(1) |
Optimization Techniques
- Minimal State: Only store what's needed (1-3 candles)
- Early Returns: Exit quickly when pattern can't match
- Type Stability: All types known at compile time
- No Allocations: Reuse buffers, no temporary arrays
Patterns Implemented
Single-Candle (5 detectors → 10 patterns)
Detector | Patterns | Lookback |
---|---|---|
Doji | DOJI, DRAGONFLYDOJI, GRAVESTONEDOJI | 1 |
Hammer | HAMMER | 1 |
ShootingStar | SHOOTING_STAR | 1 |
Marubozu | MARUBOZUBULLISH, MARUBOZUBEARISH | 1 |
SpinningTop | SPINNING_TOP | 1 |
Two-Candle (4 detectors → 8 patterns)
Detector | Patterns | Lookback |
---|---|---|
Engulfing | BULLISHENGULFING, BEARISHENGULFING | 2 |
Harami | BULLISHHARAMI, BEARISHHARAMI | 2 |
PiercingDarkCloud | PIERCINGLINE, DARKCLOUD_COVER | 2 |
Tweezer | TWEEZERTOP, TWEEZERBOTTOM | 2 |
Three-Candle (3 detectors → 6 patterns)
Detector | Patterns | Lookback |
---|---|---|
Star | MORNINGSTAR, EVENINGSTAR | 3 |
ThreeSoldiersCrows | THREEWHITESOLDIERS, THREEBLACKCROWS | 3 |
ThreeInside | THREEINSIDEUP, THREEINSIDEDOWN | 3 |
Comprehensive (1 detector → all patterns)
Detector | Description |
---|---|
CandlestickPatternDetector | Detects all patterns simultaneously |
Design Decisions
Why Enums?
Enums provide:
- Type Safety: Cannot have invalid pattern types
- Performance: Efficient comparisons
- Clarity: Explicit pattern names
- Pattern Matching: Switch-like behavior
Why Separate Categories?
Separating single/two/three-candle patterns:
- Performance: Selective detection (only what you need)
- Organization: Clear code structure
- Extensibility: Easy to add new categories
Why Confidence Scores?
Confidence enables:
- Quality Filtering: Ignore weak patterns
- Ranking: Sort by strength
- Sensitivity: Adjustable thresholds
- Better Decisions: Quantify pattern quality
Extending the System
Adding a New Pattern
- Add Enum in
PatternTypes.jl
:
@enum NewPatternType begin
# ... existing ...
MY_NEW_PATTERN
end
- Create Detector in
patterns/MyPattern.jl
:
mutable struct MyPattern{Tohlcv,IN,S} <: TechnicalIndicatorSingleOutput{Tohlcv}
# Standard fields
value::Union{Missing,PatternVal}
n::Int
# ...
# Pattern-specific
my_parameter::S
input_values::CircBuff
function MyPattern{Tohlcv}(;my_parameter = DEFAULT_VALUE, ...)
# Initialize
end
end
function _calculate_new_value(ind::MyPattern)
# Pattern detection logic
end
- Add to Module in
OnlineTechnicalIndicators.jl
:
PATTERN_INDICATORS = [
# ... existing ...
"MyPattern",
]
- Add Tests in
test/test_patterns.jl
:
@testset "MyPattern" begin
# Test cases
end
Testing Strategy
Test Categories
- Pattern Detection: Verify correct pattern identification
- Confidence Scoring: Check confidence calculations
- Edge Cases: Test boundary conditions
- Incremental Processing: Verify streaming behavior
- Integration: Test with comprehensive detector
Example Test
@testset "Doji Detection" begin
# Perfect doji
doji_candle = OHLCV(100.0, 102.0, 98.0, 100.0)
ind = Doji{OHLCV{Missing,Float64,Missing}}()
fit!(ind, doji_candle)
@test !ismissing(value(ind))
@test value(ind).pattern != SingleCandlePatternType.NONE
@test value(ind).confidence > 0.5
end
Integration with OnlineTechnicalIndicators.jl
Module Structure
OnlineTechnicalIndicators.jl/
├── src/
│ ├── OnlineTechnicalIndicators.jl # Main module
│ ├── patterns/
│ │ ├── PatternTypes.jl # Enums
│ │ ├── PatternValues.jl # Return types
│ │ ├── Doji.jl # Detectors
│ │ └── ...
│ └── indicators/ # Other indicators
└── test/
└── test_patterns.jl # Pattern tests
Exports
All pattern-related symbols are exported from the main module:
# Pattern types
export SingleCandlePatternType, TwoCandlePatternType,
ThreeCandlePatternType, PatternDirection
# Pattern value types
export SingleCandlePatternVal, TwoCandlePatternVal,
ThreeCandlePatternVal, AllPatternsVal
# Detectors
export Doji, Hammer, ShootingStar, Marubozu, SpinningTop,
Engulfing, Harami, PiercingDarkCloud, Tweezer,
Star, ThreeSoldiersCrows, ThreeInside,
CandlestickPatternDetector
No Breaking Changes
The implementation is fully backward compatible:
- All existing functionality preserved
- All existing tests pass (1379/1379)
- No API changes to existing indicators
- Patterns are additive only
Future Enhancements
Potential additions:
- Chart Patterns: Head & Shoulders, Triangles, Flags
- Volume Integration: Volume-weighted confidence
- Trend Context: Automatic trend detection
- Pattern Invalidation: Track failed patterns
- Multi-Timeframe: Cross-timeframe patterns
- Machine Learning: ML-enhanced confidence