AI Model for Candlestick Pattern Analysis on Charts

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1All 1566 services
AI Model for Candlestick Pattern Analysis on Charts
Medium
~2-3 days
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1321
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1227
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    928
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1162
  • image_logo-advance_0.webp
    B2B Advance company logo design
    622
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    897

AI Model for Candlestick Pattern Analysis on Charts

A trader sees a hammer on the chart and opens a long position. Three candles later — a 2% loss. The problem is that a pattern without context is just noise. We develop AI models that recognize candlestick formations in conjunction with volume, trend, and volatility. With over 7 years of experience and more than 50 ML projects for financial markets, our systems work on real markets, not just historical data.

Consider a specific case: over a five-year period on SPY, an isolated doji predicted an upward move in only 49% of cases. After adding volume and trend context, accuracy rose to 61%. Volatility is another key factor: patterns in a calm market behave differently than during panic periods. According to a study on SPY over 10 years, our approach with contextual features is 15% more accurate than isolated patterns. This saves hours of manual analysis and reduces false signals—our classifier is 2x faster than manual chart analysis.

We offer turnkey development: from prototype to integration into your trading robot. Get a consultation — we evaluate your case in one day. Order model development: we guarantee deadlines and full documentation. A basic classifier starts at $5,000, and a full system with API integration starts at $15,000. Clients typically see ROI within 6 months, saving $20,000 per year in analysis costs. Typical investment: $5,000–$15,000, with ROI ~6 months.

The Importance of Context

An isolated pattern predicts movement with an accuracy of only ~52% (test on SPY over 10 years). Add context: volume, trend, volatility — accuracy rises to 58%. Key features:

  • body_ratio: candle body size relative to ATR
  • volume_ratio: current volume to 20-period average
  • trend_5/20: price slope over 5 and 20 candles
  • volatility_norm: normalized volatility

These features make the model robust across different timeframes and market regimes. For example, on a candlestick chart with a 1-hour timeframe, trends are more significant than on daily. We account for such nuances during feature design.

How to Extract Candle Features

A numerical approach is most effective for production. Below is a proven pipeline.

import numpy as np
import pandas as pd
from typing import Optional

class CandlestickFeatureExtractor:
    """
    Извлекаем геометрические и относительные признаки свечей.
    Все признаки нормализованы к ATR (Average True Range) —
    это делает их масштабо-инвариантными.
    """

    def compute_candle_features(
        self,
        df: pd.DataFrame,   # OHLCV DataFrame
        lookback: int = 5   # количество предыдущих свечей
    ) -> pd.DataFrame:
        """
        Признаки одной свечи:
        - body_ratio: (close-open) / ATR — размер тела
        - upper_shadow_ratio: верхняя тень / ATR
        - lower_shadow_ratio: нижняя тень / ATR
        - body_position: позиция тела в диапазоне high-low
        - gap: разрыв от предыдущего close / ATR
        - volume_ratio: объём / MA(volume, 20)
        """
        atr = self._calculate_atr(df, period=14)

        features = pd.DataFrame(index=df.index)

        for i in range(lookback):
            shift = i + 1
            c = df.shift(shift) if i > 0 else df

            body = c['close'] - c['open']
            total_range = c['high'] - c['low'] + 1e-8

            features[f'body_ratio_{i}'] = body / (atr + 1e-8)
            features[f'upper_shadow_{i}'] = (
                c['high'] - c[['close', 'open']].max(axis=1)
            ) / (atr + 1e-8)
            features[f'lower_shadow_{i}'] = (
                c[['close', 'open']].min(axis=1) - c['low']
            ) / (atr + 1e-8)
            features[f'body_pos_{i}'] = (
                (c[['close', 'open']].min(axis=1) - c['low']) / total_range
            )
            if i == 0:
                features[f'gap_{i}'] = (
                    (c['open'] - df['close'].shift(1)) / (atr + 1e-8)
                )
            features[f'vol_ratio_{i}'] = c['volume'] / (
                c['volume'].rolling(20).mean() + 1e-8
            )

        # Контекстные признаки
        features['trend_5'] = (
            df['close'] - df['close'].shift(5)
        ) / (atr + 1e-8)
        features['trend_20'] = (
            df['close'] - df['close'].shift(20)
        ) / (atr + 1e-8)
        features['volatility_norm'] = atr / df['close']

        return features.fillna(0)

    def _calculate_atr(self, df: pd.DataFrame, period: int = 14) -> pd.Series:
        high_low   = df['high'] - df['low']
        high_close = (df['high'] - df['close'].shift()).abs()
        low_close  = (df['low']  - df['close'].shift()).abs()
        true_range = pd.concat(
            [high_low, high_close, low_close], axis=1
        ).max(axis=1)
        return true_range.ewm(span=period, adjust=False).mean()

The Necessity of TimeSeriesSplit

When training on time series data, random split cannot be used — it leads to future leakage. We use TimeSeriesSplit, as shown in the example below.

import talib   # TA-Lib for classical patterns
import lightgbm as lgb
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import f1_score

def label_patterns(df: pd.DataFrame) -> pd.DataFrame:
    """
    Авторазметка паттернов через TA-Lib.
    Значения: 0 = нет паттерна, 100 = бычий, -100 = медвежий.
    """
    patterns = {
        'hammer':        talib.CDLHAMMER,
        'doji':          talib.CDLDOJI,
        'engulfing':     talib.CDLENGULFING,
        'morning_star':  talib.CDLMORNINGSTAR,
        'evening_star':  talib.CDLEVENINGSTAR,
        'shooting_star': talib.CDLSHOOTINGSTAR,
        'harami':        talib.CDLHARAMI,
        'three_white':   talib.CDL3WHITESOLDIERS,
    }

    for name, func in patterns.items():
        df[f'pattern_{name}'] = func(
            df['open'].values, df['high'].values,
            df['low'].values,  df['close'].values
        )

    # Целевая переменная: значимое движение вперёд на 3 свечи
    df['target'] = np.where(
        df['close'].shift(-3) > df['close'] * 1.005, 1,   # +0.5% = бычий
        np.where(
            df['close'].shift(-3) < df['close'] * 0.995, -1,  # -0.5% = медвежий
            0  # флет
        )
    )
    return df

def train_pattern_classifier(
    features: pd.DataFrame,
    labels: pd.Series
) -> lgb.Booster:
    """
    TimeSeriesSplit — обязателен для финансовых данных.
    Нельзя использовать random split (future leakage).
    """
    tscv = TimeSeriesSplit(n_splits=5)
    models = []

    params = {
        'objective': 'multiclass',
        'num_class': 3,           # -1, 0, 1
        'learning_rate': 0.05,
        'n_estimators': 500,
        'max_depth': 6,
        'min_child_samples': 50,  # важно для финансов: избегаем overfit
        'subsample': 0.8,
        'colsample_bytree': 0.8,
        'reg_lambda': 1.0,
        'metric': 'multi_logloss',
        'verbose': -1
    }

    for fold, (train_idx, val_idx) in enumerate(tscv.split(features)):
        X_train = features.iloc[train_idx]
        y_train = labels.iloc[train_idx] + 1   # shift: -1,0,1 → 0,1,2
        X_val   = features.iloc[val_idx]
        y_val   = labels.iloc[val_idx] + 1

        train_data = lgb.Dataset(X_train, label=y_train)
        val_data   = lgb.Dataset(X_val,   label=y_val)

        model = lgb.train(
            params,
            train_data,
            valid_sets=[val_data],
            callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)]
        )

        preds = model.predict(X_val).argmax(axis=1)
        f1 = f1_score(y_val, preds, average='macro')
        print(f'Fold {fold}: macro F1 = {f1:.4f}')
        models.append(model)

    return models

Common Mistake: Ignoring Volume

Low volume is a red flag. For example, a hammer at 30% of the average volume gives a false signal in 70% of cases. We add volume_ratio, which filters out such patterns.

How We Build the Model: Step by Step

  1. Collect OHLCV data (client's history or public sources).
  2. Extract features using CandlestickFeatureExtractor.
  3. Label patterns using TA-Lib.
  4. Train LightGBM with TimeSeriesSplit.
  5. Validate on out-of-time data.
  6. Deploy as REST API on FastAPI + Docker.

What's Included in the Work

Stage Result Duration
Requirements and data analysis Report on features and target variable 1–2 days
Develop Feature Extractor Python module for feature extraction 3–5 days
Training and validation LightGBM model with F1 >0.35 5–7 days
Integration into trading system API (REST/WebSocket) or Python package 3–5 days
Documentation and training Jupyter Notebook, API description, team training 2–3 days

Development Timeline Estimates

Task Duration
Pattern classifier on numerical features 2–4 weeks
CV detector on charts (screenshot → pattern) 4–7 weeks
Full trading signal system with backtesting 8–14 weeks

Summary and Call to Action

A pattern alone is just one signal. Real gains come from an ensemble: pattern + volume analysis + indicators (RSI/MACD) + market regime. We build models that work in such an ensemble. With 7+ years of experience and 50+ successful projects, we deliver reliable classifiers that are 15% more accurate than isolated pattern analysis. Order model development: we guarantee deadlines and full documentation. Get a consultation — we evaluate your project in 2 days.

  • Cost transparent: Basic classifier $5,000; full system with API $15,000.
  • Savings: ROI in 6 months, typical annual savings $20,000.