← Makalelere geri dön
March 22, 2026
5 dakikalık okuma

Algoritmik İşlem için Bar Türleri ve Birleştirme Yöntemleri

Algoritmik İşlem için Bar Türleri ve Birleştirme Yöntemleri
#algotrading
#mumlar
#piyasa mikroyapısı
#Lopez de Prado
#emir akışı
#backtesting
#araştırma

Binance, TradingView veya herhangi bir borsa arayüzünde gördüğünüz her mum grafiği aynı şekilde oluşturulur: işlemleri sabit bir zaman penceresinde birleştirin — 1 dakika, 5 dakika, 1 saat — ve bir OHLCV barı üretin. Bu o kadar yaygındır ki çoğu işlemci bunu hiç sorgulamaz. Ancak algoritmik işlem için bar türü seçimi ve birleştirme yöntemi iki bağımsız karardır — çoğu sistem bunları birbiriyle karıştırır.

Bu makale mum oluşturmanın iki eksenini ayırır: ne tür bir bar oluşturduğunuz (17 tür) ve bunları daha yüksek zaman dilimlerine nasıl birleştirdiğiniz (3 yöntem). Kombinasyon, backtesting, canlı işlem ve sinyal üretimi için farklı özelliklere sahip 51 olası yapılandırma verir.

Ham işlemlerin standart mumlara dönüşümüne giriş için bkz. İşlem Mumları Açıklandı.


Özet

  • Mum oluşturmanın iki bağımsız ekseni vardır: bar türü ve birleştirme yöntemi
  • 17 temel bar türü: zaman, tick, hacim, dolar, Renko, aralık, oynaklık, Heikin-Ashi, Kagi, Line Break, P&F, tick dengesizliği (TIB), hacim dengesizliği (VIB), run, CUSUM, entropi, delta
  • 3 birleştirme yöntemi: takvime hizalanmış, kayan pencere, adaptif kayan
  • 17 × 3 = 51 olası kombinasyon, her biri farklı özelliklere sahip
  • Çoğu sistem yalnızca tek bir kombinasyon kullanır: takvime hizalanmış zaman barları. Diğer 50'si kullanılmamış durumda.
  • Pratik öneri: katmanlarda birden fazla kombinasyon kullanın — sinyaller için kayan zaman barları, piyasa yapısı için takvim zaman barları, mikroyapı için bilgi odaklı barlar

Mum Oluşturmanın İki Ekseni

Geleneksel görüş tüm bar türlerini düz bir listede sıralar: zaman barları, tick barları, hacim barları, Renko, vb. Bu yanıltıcıdır. Aslında iki dik seçim vardır:

Eksen 1 — Temel Bar Türü (17 tür): Yeni bir barın ne zaman kapanacağına nasıl karar veriyorsunuz? Sabit bir zaman aralığından sonra mı? N işlemden sonra mı? Bir fiyat hareketinin ardından mı? Bilgi içeriği değiştiğinde mi? Bu "bir bar"ın ne anlama geldiğini belirler.

Eksen 2 — Birleştirme Yöntemi (3 yöntem): Temel barları daha yüksek zaman dilimi mumlarında nasıl birleştiriyorsunuz? Takvim sınırlarına hizalayın mı (00:00, 01:00, ...)? Son N barın kayan penceresini mi kullanın? Pencere boyutunu oynaklığa uyarlayın mı?

Bu iki eksen bağımsızdır. Şunlara sahip olabilirsiniz:

  • Takvime hizalanmış tick barları — 14:00 ile 14:59 arasında kapanan tick barlarını tek bir saatlik muma birleştirin
  • Kayan hacim barları — ne zaman kapandıklarına bakılmaksızın son 24 hacim barını alın
  • Adaptif delta barları — delta barları üzerinde oynaklık odaklı bir pencere kullanın

Standart "1 saatlik mum", bu 17×3 matrisinde yalnızca bir noktadır: zaman barları + takvim hizalaması. Diğer her kombinasyon değerlendirmeye değer bir alternatiftir.


1. Zaman Barları (Standart)

Takvim zaman barı sorunu Eşitsiz bilgi yoğunluğu: katı zaman sınırları, 200 işlemlik sakin saatleri 50.000 işlemlik duyuru saatleriyle aynı şekilde değerlendirir.

Varsayılan. Sabit bir zaman aralığından sonra yeni bir bar oluşur: 1 dakika, 5 dakika, 1 saat. Her borsa bunları doğal olarak sağlar.

Özellikler:

  • Asya seansı sırasında (00:00–08:00 UTC), 1 saatlik bir mum 200 işlem içerebilir. Bir Binance listeleme duyurusu sırasında, aynı pencere 50.000 işlem içerebilir. Zaman barları her ikisini de eşdeğer olarak değerlendirir. Bu tür aktivite artışlarını tespit etmek bot koruması için kritiktir — bkz. İşlem Botları için Anomali Tespiti.
  • Tüm piyasa katılımcıları aynı mum sınırlarını görür — bir Schelling noktası. Bu durum zaman barlarını kalabalık davranışını analiz etmek için vazgeçilmez kılar.
  • Kısmi mumlarda hesaplanan göstergeler (yeniden başlatmanın ardından) anlamsız değerler üretir.
from datetime import datetime

def time_until_valid_hourly_candle():
    """Yeniden başlatmanın ardından ilk tam saatlik muma kadar geçen süre."""
    now = datetime.utcnow()
    minutes_into_hour = now.minute
    seconds_into_minute = now.second

    wait_seconds = (60 - minutes_into_hour) * 60 - seconds_into_minute
    wait_seconds += 3600

    return wait_seconds

2–4. Aktivite Tabanlı Barlar

Aktivite tabanlı barlar Tick, hacim ve dolar barları: bar sınırlarını saatin değil piyasa katılımının belirlemesinin üç yolu.

Sabit zaman aralıklarında örnekleme yapmak yerine, sabit miktarda piyasa aktivitesinin ardından örnekleme yapın. Bu, günün saatinden bağımsız olarak yaklaşık eşit "bilgi içeriğine" sahip barlar üretir.

2. Tick Barları

Her N işlemden (tickten) sonra yeni bir bar oluşur. Yüksek aktivite dönemlerinde barlar hızla oluşur. Sakin dönemlerde tek bir bar saatler sürebilir.

from collections import deque
from dataclasses import dataclass

@dataclass
class OHLCV:
    timestamp: int
    open: float
    high: float
    low: float
    close: float
    volume: float

class TickBarGenerator:
    """
    Her `threshold` işlemde bir yeni bar üretir.
    Her bar eşit sayıda piyasa "görüşü" içerir.
    """

    def __init__(self, threshold: int = 1000):
        self.threshold = threshold
        self.trades: list[tuple[float, float]] = []  # (fiyat, miktar)
        self.bars: list[OHLCV] = []

    def on_trade(self, timestamp: int, price: float, qty: float):
        self.trades.append((price, qty))

        if len(self.trades) >= self.threshold:
            self._close_bar(timestamp)

    def _close_bar(self, timestamp: int):
        prices = [t[0] for t in self.trades]
        volumes = [t[1] for t in self.trades]

        bar = OHLCV(
            timestamp=timestamp,
            open=prices[0],
            high=max(prices),
            low=min(prices),
            close=prices[-1],
            volume=sum(volumes),
        )
        self.bars.append(bar)
        self.trades = []
        return bar

Avantajlar: Piyasa aktivitesine doğal olarak uyum sağlar. Tick barlarından elde edilen getiriler, zaman barı getirilerine kıyasla normal dağılıma daha yakın olma eğilimindedir — bu özellik birçok istatistiksel modelin performansını iyileştirir.

Dezavantajlar: Ham işlem akışı gerektirir (tüm veri sağlayıcılarından tarihsel veriler için mevcut değildir). Bar zamanlaması öngörülemezidir — "sonraki bar X'te kapanacak" diyemezsiniz.

3. Hacim Barları

N sözleşme (veya kriptoda coin) işlem gördükten sonra yeni bir bar oluşur. Tick barlarına benzer ancak işlem büyüklüğüne göre ağırlıklandırılmıştır — tek bir 100 BTC'lik işlem, 1 BTC'lik işleme göre 100 kat daha fazla katkıda bulunur.

class VolumeBarGenerator:
    """
    Her `threshold` birim hacimde bir yeni bar üretir.
    İşlem büyüklüğünü normalize eder: büyük bir emir ≠ küçük bir emir.
    """

    def __init__(self, threshold: float = 100.0):
        self.threshold = threshold
        self.accumulated_volume = 0.0
        self.trades: list[tuple[int, float, float]] = []  # (zaman damgası, fiyat, miktar)
        self.bars: list[OHLCV] = []

    def on_trade(self, timestamp: int, price: float, qty: float):
        self.trades.append((timestamp, price, qty))
        self.accumulated_volume += qty

        if self.accumulated_volume >= self.threshold:
            self._close_bar()

    def _close_bar(self):
        prices = [t[1] for t in self.trades]
        volumes = [t[2] for t in self.trades]

        bar = OHLCV(
            timestamp=self.trades[-1][0],
            open=prices[0],
            high=max(prices),
            low=min(prices),
            close=prices[-1],
            volume=sum(volumes),
        )
        self.bars.append(bar)
        self.accumulated_volume = 0.0
        self.trades = []
        return bar

4. Dolar Barları

Sabit bir nominal değer (USD/USDT cinsinden) el değiştirdikten sonra yeni bir bar oluşur. Aktivite tabanlı barların en sağlamlısıdır çünkü hem işlem sayısını hem de fiyat düzeyini normalize eder.

Düşünün: ETH, 1.000dan4.000'dan 4.000'a çıkarsa, 10.000deg˘erindeETHsatmakic\cin4.000 değerinde ETH satmak için 4.000'da 2,5 ETH, 1.000$'da ise 10 ETH gerekir. Hacim barları bunları farklı şekilde değerlendirirdi; dolar barları ise her ikisini de aynı şekilde değerlendirir.

class DollarBarGenerator:
    """
    Her `threshold` dolar (USDT) nominal hacimde bir yeni bar üretir.
    En sağlam normalleştirme: fiyat düzeyinden bağımsız.

    Lopez de Prado (2018), çoğu nicel uygulama için
    dolar barlarını varsayılan olarak önerir.
    """

    def __init__(self, threshold: float = 1_000_000.0):
        self.threshold = threshold
        self.accumulated_dollars = 0.0
        self.trades: list[tuple[int, float, float]] = []
        self.bars: list[OHLCV] = []

    def on_trade(self, timestamp: int, price: float, qty: float):
        self.trades.append((timestamp, price, qty))
        self.accumulated_dollars += price * qty

        if self.accumulated_dollars >= self.threshold:
            self._close_bar()

    def _close_bar(self):
        prices = [t[1] for t in self.trades]
        volumes = [t[2] for t in self.trades]

        bar = OHLCV(
            timestamp=self.trades[-1][0],
            open=prices[0],
            high=max(prices),
            low=min(prices),
            close=prices[-1],
            volume=sum(volumes),
        )
        self.bars.append(bar)
        self.accumulated_dollars = 0.0
        self.trades = []
        return bar

Eşik Değerini Seçmek

Aktivite tabanlı barlar için eşik değeri, günlük yaklaşık olarak değiştirdiğiniz zaman barlarıyla aynı sayıda bar üretmelidir. Binance'teki BTCUSDT için:

Bar Türü Tipik Eşik ~Bar/Gün Eşdeğer Zaman Dilimi
Tick 1.000 işlem ~1.400 ~1d
Tick 50.000 işlem ~28 ~1s
Hacim 100 BTC ~600 ~2-3d
Hacim 2.400 BTC ~25 ~1s
Dolar 1M$ ~1.400 ~1d
Dolar 50M$ ~28 ~1s

Bu rakamlar yaklaşıktır ve piyasa rejimiyle birlikte önemli ölçüde değişir. Bir yükseliş veya çöküş sırasında aktivite tabanlı barlar normalden 5-10 kat daha fazla bar üretecektir — ki bu tam olarak amacıdır.

5–7. Fiyat Tabanlı Barlar

Fiyat tabanlı barlar Renko tuğlaları, aralık barları ve oynaklık barları: yalnızca fiyat yeterince hareket ettiğinde örnekleme yapmak.

Fiyat tabanlı barlar hem zamanı hem de aktiviteyi göz ardı eder. Fiyat belirli bir miktarda hareket ettiğinde yalnızca yeni bir bar oluşur. Bu doğal olarak yatay gürültüyü filtreler ve trendleri vurgular.

5. Renko Barları

Kapanış fiyatı önceki tuğlanın kapanışından en az N birim hareket ettiğinde yeni bir Renko "tuğlası" oluşur. Tuğlalar her zaman aynı büyüklüktedir ve trend yönünün temiz bir görsel temsilini oluşturur.

class RenkoBarGenerator:
    """
    Fiyat hareketine göre Renko tuğlaları üretir.

    Temel özellik: yatay hareket sırasında yeni tuğla oluşmaz.
    Güçlü trendler sırasında tuğlalar hızla oluşur.
    """

    def __init__(self, brick_size: float = 10.0):
        self.brick_size = brick_size
        self.bricks: list[dict] = []
        self.last_close: float | None = None

    def on_price(self, timestamp: int, price: float, volume: float = 0.0):
        if self.last_close is None:
            self.last_close = price
            return []

        new_bricks = []
        diff = price - self.last_close
        num_bricks = int(abs(diff) / self.brick_size)

        if num_bricks == 0:
            return []

        direction = 1 if diff > 0 else -1

        for i in range(num_bricks):
            brick_open = self.last_close
            brick_close = self.last_close + direction * self.brick_size

            brick = {
                'timestamp': timestamp,
                'open': brick_open,
                'high': max(brick_open, brick_close),
                'low': min(brick_open, brick_close),
                'close': brick_close,
                'volume': volume / num_bricks if num_bricks > 0 else 0,
                'direction': direction,
            }
            new_bricks.append(brick)
            self.last_close = brick_close

        self.bricks.extend(new_bricks)
        return new_bricks

Dinamik Renko, sabit bir tuğla boyutu yerine ATR (Average True Range) kullanarak oynaklığa otomatik olarak uyum sağlar.

6. Aralık Barları

Her bar sabit bir yüksek-düşük aralığına sahiptir. Aralık aşıldığında bar kapanır ve yenisi başlar. Renko'nun aksine, aralık barları fitiller içerir ve bar içi oynaklığı gösterebilir.

class RangeBarGenerator:
    """
    Sabit yüksek-düşük aralığıyla barlar üretir.

    Renko'dan farkı: aralık barları yalnızca tuğla yönünü değil,
    aralık içindeki tam OHLC'yi gösterir. Daha fazla bilgi içerir.
    """

    def __init__(self, range_size: float = 20.0):
        self.range_size = range_size
        self.current_high: float | None = None
        self.current_low: float | None = None
        self.current_open: float | None = None
        self.current_volume: float = 0.0
        self.current_start_ts: int = 0
        self.bars: list[OHLCV] = []

    def on_trade(self, timestamp: int, price: float, qty: float):
        if self.current_open is None:
            self.current_open = price
            self.current_high = price
            self.current_low = price
            self.current_start_ts = timestamp

        self.current_high = max(self.current_high, price)
        self.current_low = min(self.current_low, price)
        self.current_volume += qty

        if self.current_high - self.current_low >= self.range_size:
            bar = OHLCV(
                timestamp=timestamp,
                open=self.current_open,
                high=self.current_high,
                low=self.current_low,
                close=price,
                volume=self.current_volume,
            )
            self.bars.append(bar)

            self.current_open = price
            self.current_high = price
            self.current_low = price
            self.current_volume = 0.0
            self.current_start_ts = timestamp

            return bar

        return None

Renko ve Aralık barları arasındaki temel fark: Renko yalnızca kapanış fiyatlarını izler ve yönü gösterir; aralık barları tam fiyat aralığını izler ve bar içindeki yapıyı gösterir. Aralık barları, stop-loss ve kar-al simülasyonu için gereken yüksek-düşük bilgisini koruduğundan genellikle algoritmik işlem için daha kullanışlıdır.

7. Oynaklık Barları

Bar içi oynaklık dinamik bir eşiğe ulaştığında yeni bir bar oluşur — örneğin son ATR'nin bir katı. Aralık barlarının aksine (sabit eşik), oynaklık barları piyasa koşullarına uyum sağlar.

class VolatilityBarGenerator:
    """
    Bar içi oynaklık bir eşiğe ulaştığında barlar üretir.

    Aralık barlarına benzer, ancak eşik kayan ATR ölçüsü kullanılarak
    piyasa koşullarına uyum sağlar. Sakin piyasalarda barların kapanması
    için daha az mutlak hareket gerekir; oynaklı piyasalarda daha fazla.
    """

    def __init__(
        self,
        atr_period: int = 14,
        atr_multiplier: float = 1.0,
        initial_threshold: float = 20.0,
    ):
        self.atr_period = atr_period
        self.atr_multiplier = atr_multiplier
        self.threshold = initial_threshold

        self.recent_ranges: list[float] = []
        self.current_open: float | None = None
        self.current_high: float | None = None
        self.current_low: float | None = None
        self.current_volume: float = 0.0
        self.bars: list[OHLCV] = []

    def on_trade(self, timestamp: int, price: float, qty: float):
        if self.current_open is None:
            self.current_open = price
            self.current_high = price
            self.current_low = price

        self.current_high = max(self.current_high, price)
        self.current_low = min(self.current_low, price)
        self.current_volume += qty

        intra_bar_range = self.current_high - self.current_low

        if intra_bar_range >= self.threshold:
            bar = OHLCV(
                timestamp=timestamp,
                open=self.current_open,
                high=self.current_high,
                low=self.current_low,
                close=price,
                volume=self.current_volume,
            )
            self.bars.append(bar)

            self.recent_ranges.append(intra_bar_range)
            if len(self.recent_ranges) > self.atr_period:
                self.recent_ranges = self.recent_ranges[-self.atr_period:]
            if len(self.recent_ranges) >= self.atr_period:
                avg_range = sum(self.recent_ranges) / len(self.recent_ranges)
                self.threshold = avg_range * self.atr_multiplier

            self.current_open = price
            self.current_high = price
            self.current_low = price
            self.current_volume = 0.0
            return bar

        return None

8. Heikin-Ashi (Düzleştirilmiş Dönüşüm)

Heikin-Ashi dönüşümü Heikin-Ashi: ortalama alma, gürültülü mumları düzgün trend sinyallerine dönüştürür — ancak kesin fiyat bilgisi pahasına.

Heikin-Ashi (Japonca'da "ortalama bar") bir bar türü değildir — herhangi bir temel bar türünün üzerine uygulanabilen bir dönüşümdür. Mevcut ve önceki bar değerlerini ortalayarak mumları düzleştirir:

  • HA Kapanış = (Açılış + Yüksek + Düşük + Kapanış) / 4
  • HA Açılış = (Önceki HA Açılış + Önceki HA Kapanış) / 2
  • HA Yüksek = max(Yüksek, HA Açılış, HA Kapanış)
  • HA Düşük = min(Düşük, HA Açılış, HA Kapanış)

Trendler, alt fitili olmayan (yükseliş trendi) veya üst fitili olmayan (düşüş trendi) aynı renkteki mum dizileri olarak görünür.

class HeikinAshiTransformer:
    """
    Standart OHLCV mumlarını Heikin-Ashi mumlarına dönüştürür.

    HERHANGİ bir bar türünün üzerine uygulanabilir: zaman barları,
    hacim barları, kayan barlar vb. Bu bir örnekleme yöntemi değil,
    bir dönüşümdür.

    UYARI: HA fiyatları sentetiktir — gerçek işlem fiyatlarını temsil etmez.
    Emir yerleştirmek veya K&Z hesaplaması için asla HA kapanışını kullanmayın.
    HA'yı yalnızca sinyal üretimi için kullanın, ardından gerçek fiyatlardan
    işlem yapın.
    """

    def __init__(self):
        self.prev_ha_open: float | None = None
        self.prev_ha_close: float | None = None

    def transform(self, candle: OHLCV) -> OHLCV:
        ha_close = (candle.open + candle.high + candle.low + candle.close) / 4

        if self.prev_ha_open is None:
            ha_open = (candle.open + candle.close) / 2
        else:
            ha_open = (self.prev_ha_open + self.prev_ha_close) / 2

        ha_high = max(candle.high, ha_open, ha_close)
        ha_low = min(candle.low, ha_open, ha_close)

        self.prev_ha_open = ha_open
        self.prev_ha_close = ha_close

        return OHLCV(
            timestamp=candle.timestamp,
            open=ha_open,
            high=ha_high,
            low=ha_low,
            close=ha_close,
            volume=candle.volume,
        )

    def transform_series(self, candles: list[OHLCV]) -> list[OHLCV]:
        """Tüm bir seriyi dönüştürür. Önce durumu sıfırlar."""
        self.prev_ha_open = None
        self.prev_ha_close = None
        return [self.transform(c) for c in candles]


def ha_trend_signal(ha_candles: list[OHLCV], lookback: int = 3) -> int:
    """
    Basit HA trend sinyali.

    Döndürür:
        +1: yükseliş (alt fitili olmayan N ardışık yeşil HA mumu)
        -1: düşüş (üst fitili olmayan N ardışık kırmızı HA mumu)
         0: belirgin trend yok
    """
    if len(ha_candles) < lookback:
        return 0

    recent = ha_candles[-lookback:]

    all_bullish = all(
        c.close > c.open and abs(c.low - min(c.open, c.close)) < 1e-10
        for c in recent
    )

    all_bearish = all(
        c.close < c.open and abs(c.high - max(c.open, c.close)) < 1e-10
        for c in recent
    )

    if all_bullish:
        return 1
    elif all_bearish:
        return -1
    return 0

Backtesting için kritik uyarı: Heikin-Ashi fiyatları sentetiktir. Backtestiniz giriş fiyatı olarak HA kapanışını kullanıyorsa sonuçlar yanlış olacaktır. HA'yı her zaman yalnızca sinyal üretimi için kullanın ve gerçek OHLC fiyatlarından işlem yapın.

HA ne zaman kullanışlıdır: Temiz "içeride kal" sinyallerine ihtiyaç duyan trend takip stratejileri. Yanlış kesişimleri filtrelemek için herhangi bir temel bar türü üzerine — zaman barları, hacim barları, dolar barları — HA uygulayın.

HA ne zaman zararlıdır: Kesin fiyat düzeylerine ihtiyaç duyan herhangi bir strateji — destek/direnç, emir defteri analizi, PIQ (Kuyruktaki Konum). Ortalama alma, kesin fiyat bilgisini yok eder.

9–11. Japon Geri Dönüş Grafikleri

Japon grafik yöntemleri Kagi, Line Break ve Nokta & Şekil: zamanı tamamen göz ardı eden ve yalnızca fiyat yapısına odaklanan grafikler.

Bunlar (Renko ile birlikte) zamanı tamamen atan ve fiyat yapısına odaklanan geleneksel Japon grafik yöntemleridir.

9. Kagi Grafikleri

Kagi grafikleri, fiyat belirli bir miktarda geri döndüğünde yön değiştiren dikey çizgilerden oluşur. Fiyat önceki bir yüksek noktayı kırdığında (kalın = "yang" = talep) veya önceki bir düşük noktayı kırdığında (ince = "yin" = arz) çizgiler kalınlık değiştirir.

class KagiChartGenerator:
    """
    Fiyat geri dönüşlerine göre Kagi grafik çizgileri üretir.

    Renko'nun aksine (sabit tuğla boyutu), Kagi her hareketin
    gerçek büyüklüğünü izler ve kırılma noktalarında çizgi
    kalınlığını değiştirir.

    Zaman gürültüsü olmadan destek/direnç kırılmalarını ve
    arz/talep değişimlerini tespit etmek için kullanışlıdır.
    """

    def __init__(self, reversal_amount: float = 10.0):
        self.reversal_amount = reversal_amount
        self.lines: list[dict] = []
        self.current_direction: int = 0  # 1=yukarı, -1=aşağı
        self.current_price: float | None = None
        self.extreme_price: float | None = None
        self.prev_high: float | None = None
        self.prev_low: float | None = None
        self.line_type: str = 'yang'  # 'yang' (kalın) veya 'yin' (ince)

    def on_price(self, timestamp: int, price: float):
        if self.current_price is None:
            self.current_price = price
            self.extreme_price = price
            return None

        if self.current_direction == 0:
            if price - self.current_price >= self.reversal_amount:
                self.current_direction = 1
                self.extreme_price = price
            elif self.current_price - price >= self.reversal_amount:
                self.current_direction = -1
                self.extreme_price = price
            return None

        if self.current_direction == 1:
            if price > self.extreme_price:
                self.extreme_price = price
                if self.prev_high is not None and price > self.prev_high:
                    self.line_type = 'yang'
            elif self.extreme_price - price >= self.reversal_amount:
                line = {
                    'timestamp': timestamp,
                    'start': self.current_price,
                    'end': self.extreme_price,
                    'direction': 'up',
                    'type': self.line_type,
                }
                self.lines.append(line)
                self.prev_high = self.extreme_price
                self.current_price = self.extreme_price
                self.extreme_price = price
                self.current_direction = -1
                if self.prev_low is not None and price < self.prev_low:
                    self.line_type = 'yin'
                return line
        else:
            if price < self.extreme_price:
                self.extreme_price = price
                if self.prev_low is not None and price < self.prev_low:
                    self.line_type = 'yin'
            elif price - self.extreme_price >= self.reversal_amount:
                line = {
                    'timestamp': timestamp,
                    'start': self.current_price,
                    'end': self.extreme_price,
                    'direction': 'down',
                    'type': self.line_type,
                }
                self.lines.append(line)
                self.prev_low = self.extreme_price
                self.current_price = self.extreme_price
                self.extreme_price = price
                self.current_direction = 1
                if self.prev_high is not None and price > self.prev_high:
                    self.line_type = 'yang'
                return line

        return None

10. Line Break Grafikleri

Line break grafikleri, yalnızca kapanış fiyatı önceki N çizginin (genellikle 3) yüksek veya düşüğünü aştığında yeni bir çizgi (kutu) çizer. Fiyat aralık içinde kalırsa yeni çizgi çizilmez.

class LineBreakGenerator:
    """
    Line Break barları üretir (varsayılan olarak Three Line Break).

    Yeni bir bar yalnızca kapanış son N barın yüksek veya düşüğünü
    aştığında çizilir. Fiyatın çok barlı bir aralığı kırmasını gerektirerek
    küçük gürültüyü filtreler.

    'N' parametresi (line_count) hassasiyeti kontrol eder:
    - N=2: daha hassas, daha fazla bar, daha fazla gürültü
    - N=3: standart (Three Line Break)
    - N=4+: daha az hassas, daha az bar, daha güçlü sinyaller
    """

    def __init__(self, line_count: int = 3):
        self.line_count = line_count
        self.lines: list[dict] = []

    def on_close(self, timestamp: int, close: float) -> dict | None:
        if not self.lines:
            self.lines.append({
                'timestamp': timestamp,
                'open': close,
                'close': close,
                'high': close,
                'low': close,
                'direction': 0,
            })
            return None

        lookback = self.lines[-self.line_count:] if len(self.lines) >= self.line_count else self.lines

        highest = max(l['high'] for l in lookback)
        lowest = min(l['low'] for l in lookback)
        last = self.lines[-1]

        new_line = None

        if close > highest:
            new_line = {
                'timestamp': timestamp,
                'open': last['close'],
                'close': close,
                'high': close,
                'low': last['close'],
                'direction': 1,
            }
        elif close < lowest:
            new_line = {
                'timestamp': timestamp,
                'open': last['close'],
                'close': close,
                'high': last['close'],
                'low': close,
                'direction': -1,
            }

        if new_line:
            self.lines.append(new_line)
            return new_line

        return None

11. Nokta & Şekil Grafikleri

Nokta & Şekil (P&F) grafikleri, X sütunları (yükselen fiyatlar) ve O sütunları (düşen fiyatlar) kullanır. Sütun değişikliği genellikle 3 kutu büyüklüğünde bir geri dönüş gerektirir. Gürültüyü filtrelemek ve destek/direnci belirlemek için en eski yöntemlerden biri.

class PointAndFigureGenerator:
    """
    Nokta & Şekil grafik verileri üretir.

    X sütunu: box_size artışlarıyla yükselen fiyat.
    O sütunu: box_size azalmalarıyla düşen fiyat.
    Sütun değişimi: ters yönde reversal_boxes * box_size hareketi gerektirir.

    Klasik ayar: ATR tabanlı box_size, reversal_boxes = 3.
    """

    def __init__(self, box_size: float = 10.0, reversal_boxes: int = 3):
        self.box_size = box_size
        self.reversal_boxes = reversal_boxes
        self.reversal_amount = box_size * reversal_boxes

        self.columns: list[dict] = []
        self.current_direction: int = 0
        self.current_top: float | None = None
        self.current_bottom: float | None = None

    def on_price(self, timestamp: int, price: float):
        if self.current_top is None:
            box_price = self._round_to_box(price)
            self.current_top = box_price
            self.current_bottom = box_price
            self.current_direction = 1
            return None

        events = []

        if self.current_direction == 1:
            while price >= self.current_top + self.box_size:
                self.current_top += self.box_size
                events.append(('X', self.current_top, timestamp))

            if price <= self.current_top - self.reversal_amount:
                col = {
                    'type': 'X',
                    'top': self.current_top,
                    'bottom': self.current_bottom,
                    'boxes': int((self.current_top - self.current_bottom) / self.box_size) + 1,
                    'timestamp': timestamp,
                }
                self.columns.append(col)
                self.current_direction = -1
                self.current_top = self.current_top - self.box_size
                self.current_bottom = self._round_to_box(price)
                events.append(('new_column', 'O', timestamp))

        else:
            while price <= self.current_bottom - self.box_size:
                self.current_bottom -= self.box_size
                events.append(('O', self.current_bottom, timestamp))

            if price >= self.current_bottom + self.reversal_amount:
                col = {
                    'type': 'O',
                    'top': self.current_top,
                    'bottom': self.current_bottom,
                    'boxes': int((self.current_top - self.current_bottom) / self.box_size) + 1,
                    'timestamp': timestamp,
                }
                self.columns.append(col)
                self.current_direction = 1
                self.current_bottom = self.current_bottom + self.box_size
                self.current_top = self._round_to_box(price)
                events.append(('new_column', 'X', timestamp))

        return events if events else None

    def _round_to_box(self, price: float) -> float:
        return round(price / self.box_size) * self.box_size

Algoritmik işlemde Kagi, Line Break ve P&F: Öncelikle uzun vadeli trend tespiti ve destek/direnç belirleme için kullanılır. Bir filtre katmanı olarak — "Kagi grafiği yin modundayken uzun pozisyon sinyali alma" — işlemleri makro yapıyla hizalayarak değer katarlar.

12–14. Bilgi Odaklı Barlar

Bilgi odaklı barlar Dengesizlik barları, run barları, CUSUM filtreleri ve entropi barları: piyasa bize bir şeylerin değiştiğini söylediğinde örnekleme.

Marcos Lopez de Prado'nun Advances in Financial Machine Learning (2018) kitabından en sofistike yaklaşım. Temel içgörü: sabit aralıklarda değil, piyasaya yeni bilgi geldiğinde örnekleme yapın.

12. Tick Dengesizlik Barları (TIB)

Piyasa denge halindeyse, alıcı başlatmalı ve satıcı başlatmalı işlemler yaklaşık olarak dengelenmelidir. Dengesizlik beklentimizi aştığında bir şeyler değişmiştir. O anda bir bar örnekleyin.

Her işlem, tick kuralı kullanılarak alıcı başlatmalı (+1) veya satıcı başlatmalı (-1) olarak sınıflandırılır. Kümülatif dengesizlik θ'yı izleriz ve |θ| dinamik bir eşiği aştığında bir bar örnekleriz.

class TickImbalanceBarGenerator:
    """
    Kümülatif tick dengesizliği beklenen düzeyleri aştığında barlar üretir
    — yani "yeni bilgi" geldiğinde.

    Lopez de Prado (2018), Bölüm 2'ye dayanmaktadır.
    """

    def __init__(
        self,
        expected_ticks_init: int = 1000,
        ewma_window: int = 100,
        min_ticks: int = 100,
        max_ticks: int = 50000,
    ):
        self.expected_ticks_init = expected_ticks_init
        self.ewma_window = ewma_window
        self.min_ticks = min_ticks
        self.max_ticks = max_ticks

        self.theta = 0.0
        self.prev_price: float | None = None
        self.prev_sign = 1
        self.trades: list[tuple[int, float, float]] = []

        self.bar_lengths: list[int] = []
        self.imbalances: list[float] = []
        self.expected_ticks = float(expected_ticks_init)
        self.expected_imbalance = 0.0

        self.bars: list[OHLCV] = []

    def _tick_sign(self, price: float) -> int:
        """İşlemi tick kuralıyla alış (+1) veya satış (-1) olarak sınıflandırır."""
        if self.prev_price is None:
            self.prev_price = price
            return 1

        if price > self.prev_price:
            sign = 1
        elif price < self.prev_price:
            sign = -1
        else:
            sign = self.prev_sign

        self.prev_price = price
        self.prev_sign = sign
        return sign

    def on_trade(self, timestamp: int, price: float, qty: float):
        sign = self._tick_sign(price)
        self.theta += sign
        self.trades.append((timestamp, price, qty))

        threshold = self.expected_ticks * abs(self.expected_imbalance)
        if threshold == 0:
            threshold = self.expected_ticks_init * 0.5

        if abs(self.theta) >= threshold and len(self.trades) >= self.min_ticks:
            return self._close_bar()

        if len(self.trades) >= self.max_ticks:
            return self._close_bar()

        return None

    def _close_bar(self):
        prices = [t[1] for t in self.trades]
        volumes = [t[2] for t in self.trades]

        bar = OHLCV(
            timestamp=self.trades[-1][0],
            open=prices[0],
            high=max(prices),
            low=min(prices),
            close=prices[-1],
            volume=sum(volumes),
        )
        self.bars.append(bar)

        self.bar_lengths.append(len(self.trades))
        self.imbalances.append(self.theta / len(self.trades))

        if len(self.bar_lengths) >= 2:
            alpha = 2.0 / (self.ewma_window + 1)
            self.expected_ticks = (
                alpha * self.bar_lengths[-1]
                + (1 - alpha) * self.expected_ticks
            )
            self.expected_ticks = max(
                self.min_ticks,
                min(self.max_ticks, self.expected_ticks)
            )
            self.expected_imbalance = (
                alpha * self.imbalances[-1]
                + (1 - alpha) * self.expected_imbalance
            )

        self.theta = 0.0
        self.trades = []
        return bar

13. Hacim Dengesizlik Barları (VIB)

TIB'lerin uzantısı: her işlemi ±1 olarak saymak yerine işaretli hacimle ağırlıklandırın. 100 BTC'lik bir alış +100 katkıda bulunurken, 1 BTC'lik bir satış -1 katkıda bulunur. Birçok küçük işleme bölünmüş büyük bilgili emirleri yakalar.

class VolumeImbalanceBarGenerator:
    """
    TIB'ler gibi, ancak işaretli tick yerine işaretli hacim kullanır.

    100 BTC'lik alış sinyalinin 1 BTC'lik alış sinyalinden 100 kat daha
    bilgilendirici olduğu içgörüsünü yakalar.
    """

    def __init__(
        self,
        expected_ticks_init: int = 1000,
        ewma_window: int = 100,
    ):
        self.expected_ticks_init = expected_ticks_init
        self.ewma_window = ewma_window

        self.theta = 0.0
        self.prev_price: float | None = None
        self.prev_sign = 1
        self.trades: list[tuple[int, float, float]] = []

        self.bar_lengths: list[int] = []
        self.volume_imbalances: list[float] = []
        self.expected_ticks = float(expected_ticks_init)
        self.expected_vol_imbalance = 0.0

        self.bars: list[OHLCV] = []

    def _tick_sign(self, price: float) -> int:
        if self.prev_price is None:
            self.prev_price = price
            return 1
        if price > self.prev_price:
            sign = 1
        elif price < self.prev_price:
            sign = -1
        else:
            sign = self.prev_sign
        self.prev_price = price
        self.prev_sign = sign
        return sign

    def on_trade(self, timestamp: int, price: float, qty: float):
        sign = self._tick_sign(price)
        self.theta += sign * qty
        self.trades.append((timestamp, price, qty))

        threshold = self.expected_ticks * abs(self.expected_vol_imbalance)
        if threshold == 0:
            threshold = self.expected_ticks_init * 0.5

        if abs(self.theta) >= threshold and len(self.trades) >= 10:
            return self._close_bar()
        return None

    def _close_bar(self):
        prices = [t[1] for t in self.trades]
        volumes = [t[2] for t in self.trades]

        bar = OHLCV(
            timestamp=self.trades[-1][0],
            open=prices[0],
            high=max(prices),
            low=min(prices),
            close=prices[-1],
            volume=sum(volumes),
        )
        self.bars.append(bar)

        self.bar_lengths.append(len(self.trades))
        self.volume_imbalances.append(self.theta / len(self.trades))

        alpha = 2.0 / (self.ewma_window + 1)
        if len(self.bar_lengths) >= 2:
            self.expected_ticks = (
                alpha * self.bar_lengths[-1] + (1 - alpha) * self.expected_ticks
            )
            self.expected_vol_imbalance = (
                alpha * self.volume_imbalances[-1]
                + (1 - alpha) * self.expected_vol_imbalance
            )

        self.theta = 0.0
        self.trades = []
        return bar

Patlama Sorunu

Dengesizlik barlarıyla bilinen bir sorun: EWMA tabanlı eşik pozitif bir geri besleme döngüsüne girebilir. Çözüm: min_ticks ve max_ticks sınırlarıyla kısıtlama.


self.expected_ticks = max(
    self.min_ticks,    # Taban: 100 tickten az olamaz
    min(
        self.max_ticks,  # Tavan: 50000 tickten fazla olamaz
        new_expected_ticks
    )
)

14. Run Barları

Run barları, mevcut yönsel serinin uzunluğunu izler — ardışık alış veya satışların en uzun dizisi. Büyük bilgili bir işlemci bir emri birçok küçük işleme böldüğünde, seri olağandışı derecede uzun hale gelir. Run barları bunu tespit eder.

class TickRunBarGenerator:
    """
    Yönsel bir serinin uzunluğu beklentileri aştığında barlar üretir.

    Lopez de Prado (2018), Bölüm 2'ye dayanmaktadır.

    Dengesizlik barlarından farkı:
    - Dengesizlik barları NET dengesizliği izler (alışlar eksi satışlar)
    - Run barları MAKSİMUM seri uzunluğunu izler (ardışık alışlar VEYA satışlar)
    """

    def __init__(
        self,
        expected_ticks_init: int = 1000,
        ewma_window: int = 100,
        min_ticks: int = 100,
        max_ticks: int = 50000,
    ):
        self.expected_ticks_init = expected_ticks_init
        self.ewma_window = ewma_window
        self.min_ticks = min_ticks
        self.max_ticks = max_ticks

        self.prev_price: float | None = None
        self.prev_sign = 1
        self.trades: list[tuple[int, float, float]] = []

        self.buy_run = 0
        self.sell_run = 0
        self.max_buy_run = 0
        self.max_sell_run = 0

        self.bar_lengths: list[int] = []
        self.max_runs: list[float] = []
        self.expected_ticks = float(expected_ticks_init)
        self.expected_max_run = 0.0

        self.bars: list[OHLCV] = []

    def _tick_sign(self, price: float) -> int:
        if self.prev_price is None:
            self.prev_price = price
            return 1
        if price > self.prev_price:
            sign = 1
        elif price < self.prev_price:
            sign = -1
        else:
            sign = self.prev_sign
        self.prev_price = price
        self.prev_sign = sign
        return sign

    def on_trade(self, timestamp: int, price: float, qty: float):
        sign = self._tick_sign(price)
        self.trades.append((timestamp, price, qty))

        if sign == 1:
            self.buy_run += 1
            self.sell_run = 0
        else:
            self.sell_run += 1
            self.buy_run = 0

        self.max_buy_run = max(self.max_buy_run, self.buy_run)
        self.max_sell_run = max(self.max_sell_run, self.sell_run)

        theta = max(self.max_buy_run, self.max_sell_run)
        threshold = self.expected_ticks * self.expected_max_run if self.expected_max_run > 0 else self.expected_ticks_init * 0.3

        if theta >= threshold and len(self.trades) >= self.min_ticks:
            return self._close_bar()

        if len(self.trades) >= self.max_ticks:
            return self._close_bar()

        return None

    def _close_bar(self):
        prices = [t[1] for t in self.trades]
        volumes = [t[2] for t in self.trades]

        bar = OHLCV(
            timestamp=self.trades[-1][0],
            open=prices[0],
            high=max(prices),
            low=min(prices),
            close=prices[-1],
            volume=sum(volumes),
        )
        self.bars.append(bar)

        max_run = max(self.max_buy_run, self.max_sell_run) / len(self.trades)
        self.bar_lengths.append(len(self.trades))
        self.max_runs.append(max_run)

        alpha = 2.0 / (self.ewma_window + 1)
        if len(self.bar_lengths) >= 2:
            self.expected_ticks = alpha * self.bar_lengths[-1] + (1 - alpha) * self.expected_ticks
            self.expected_ticks = max(self.min_ticks, min(self.max_ticks, self.expected_ticks))
            self.expected_max_run = alpha * self.max_runs[-1] + (1 - alpha) * self.expected_max_run

        self.trades = []
        self.buy_run = 0
        self.sell_run = 0
        self.max_buy_run = 0
        self.max_sell_run = 0

        return bar

Run barları hacim serileri ve dolar serileri olarak genişletilebilir.

15. CUSUM Filtre Barları

CUSUM (Kümülatif Toplam) filtresi, kümülatif getirileri izleyerek ne zaman örnekleme yapılacağını belirler. Dengesizlik barlarının aksine (ham işlemler üzerinde çalışır), CUSUM mevcut 1 dakikalık OHLCV verilerine uygulanabilir — tick verisi gerekmez.

class CUSUMFilterBarGenerator:
    """
    Olay tabanlı örnekleme için simetrik CUSUM filtresi.

    Lopez de Prado (2018), Bölüm 2.5'e dayanmaktadır.

    Bollinger Bantlarına göre temel avantaj: CUSUM tetiklenmeden önce
    eşik büyüklüğünde TAM bir seri gerektirir. Bollinger Bantları,
    fiyat banda yakın seyrettiğinde tekrar tekrar tetiklenir.

    1d OHLCV verilerine uygulanabilir — tick verisi gerekmez.
    """

    def __init__(self, threshold: float = 0.01):
        self.threshold = threshold
        self.s_pos = 0.0
        self.s_neg = 0.0
        self.prev_price: float | None = None
        self.buffer: list[OHLCV] = []
        self.bars: list[OHLCV] = []

    def on_candle_1m(self, candle: OHLCV) -> OHLCV | None:
        self.buffer.append(candle)

        if self.prev_price is None:
            self.prev_price = candle.close
            return None

        import math
        log_ret = math.log(candle.close / self.prev_price)
        self.prev_price = candle.close

        self.s_pos = max(0.0, self.s_pos + log_ret)
        self.s_neg = min(0.0, self.s_neg + log_ret)

        triggered = False

        if self.s_pos > self.threshold:
            self.s_pos = 0.0
            triggered = True

        if self.s_neg < -self.threshold:
            self.s_neg = 0.0
            triggered = True

        if triggered and len(self.buffer) >= 2:
            bars = self.buffer
            bar = OHLCV(
                timestamp=bars[-1].timestamp,
                open=bars[0].open,
                high=max(b.high for b in bars),
                low=min(b.low for b in bars),
                close=bars[-1].close,
                volume=sum(b.volume for b in bars),
            )
            self.bars.append(bar)
            self.buffer = []
            return bar

        return None

CUSUM + Üçlü Engel Yöntemi: Lopez de Prado'nun çerçevesinde, CUSUM olayları Üçlü Engel yöntemi için giriş noktaları olarak kullanılır — her olay stop-loss, kar-al ve süre sonu engelleriyle bir işlem tetikler. Bu tür olay odaklı stratejilerin güçlü doğrulaması için bkz. Walk-Forward Optimizasyonu ve Backtesting için Monte Carlo Bootstrap.

16. Entropi Barları

En teorik açıdan zarif yaklaşım: bar içi fiyat serisinin bilgi içeriği (Shannon entropisi) bir eşiği aştığında örnekleme yapın.

class EntropyBarGenerator:
    """
    Bar içi getirilerin entropisi bir eşiği aştığında barlar üretir.

    Shannon'ın bilgi teorisine dayanmaktadır: barlar "yeni bilgi"
    geldiğinde örneklenir; bu mevcut bar içindeki getiri dağılımının
    entropisi olarak ölçülür.

    Bu teorik açıdan en "saf" bilgi odaklı bardır.
    """

    def __init__(
        self,
        entropy_threshold: float = 2.0,
        min_trades: int = 50,
        n_bins: int = 10,
    ):
        self.entropy_threshold = entropy_threshold
        self.min_trades = min_trades
        self.n_bins = n_bins
        self.trades: list[tuple[int, float, float]] = []
        self.bars: list[OHLCV] = []

    def on_trade(self, timestamp: int, price: float, qty: float):
        self.trades.append((timestamp, price, qty))

        if len(self.trades) < self.min_trades:
            return None

        entropy = self._compute_entropy()

        if entropy >= self.entropy_threshold:
            return self._close_bar()

        return None

    def _compute_entropy(self) -> float:
        import math

        prices = [t[1] for t in self.trades]
        if len(prices) < 2:
            return 0.0

        returns = [
            math.log(prices[i] / prices[i-1])
            for i in range(1, len(prices))
            if prices[i-1] > 0
        ]

        if not returns:
            return 0.0

        min_r = min(returns)
        max_r = max(returns)

        if max_r == min_r:
            return 0.0

        bin_width = (max_r - min_r) / self.n_bins
        bins = [0] * self.n_bins

        for r in returns:
            idx = min(int((r - min_r) / bin_width), self.n_bins - 1)
            bins[idx] += 1

        total = sum(bins)
        entropy = 0.0
        for count in bins:
            if count > 0:
                p = count / total
                entropy -= p * math.log2(p)

        return entropy

    def _close_bar(self):
        prices = [t[1] for t in self.trades]
        volumes = [t[2] for t in self.trades]

        bar = OHLCV(
            timestamp=self.trades[-1][0],
            open=prices[0],
            high=max(prices),
            low=min(prices),
            close=prices[-1],
            volume=sum(volumes),
        )
        self.bars.append(bar)
        self.trades = []
        return bar

Pratik not: Entropi barları hesaplama açısından pahalıdır ve öncelikle araştırma ilgisi taşır — ancak ML tabanlı stratejiler için her bar yaklaşık olarak eşit "bilgi" içerdiğinden daha iyi istatistiksel özelliklere sahip özellikler üretirler.

17. Delta Barları (Emir Akışı)

Delta barları ve emir akışı Kümülatif delta: agresif alıcıların ve satıcıların gerçek zamanlı net kuvvetini ölçmek.

Delta barları, kümülatif deltaya — alış hacmi ile satış hacmi arasındaki kümülatif fark — dayalı olarak örnekleme yapar. Dengesizlik barlarının aksine (±1 tick işaretleri kullanan), delta barları gerçek hacim ağırlıklı emir akışını kullanır.

class DeltaBarGenerator:
    """
    Kümülatif emir akışı deltasına göre barlar üretir.

    Delta = Alış Hacmi - Satış Hacmi (agresör tarafına göre sınıflandırılmış).

    Taraf sınıflandırmasıyla işlem düzeyinde veri gerektirir
    (Binance aggTrades, Bybit trades vb. üzerinden mevcut)
    """

    def __init__(self, threshold: float = 500.0):
        self.threshold = threshold
        self.cumulative_delta = 0.0
        self.trades: list[tuple[int, float, float, int]] = []
        self.bars: list[OHLCV] = []

    def on_trade(self, timestamp: int, price: float, qty: float, is_buyer_maker: bool):
        side = -1 if is_buyer_maker else 1
        signed_qty = side * qty

        self.cumulative_delta += signed_qty
        self.trades.append((timestamp, price, qty, side))

        if abs(self.cumulative_delta) >= self.threshold:
            return self._close_bar()

        return None

    def _close_bar(self):
        prices = [t[1] for t in self.trades]
        volumes = [t[2] for t in self.trades]

        bar = OHLCV(
            timestamp=self.trades[-1][0],
            open=prices[0],
            high=max(prices),
            low=min(prices),
            close=prices[-1],
            volume=sum(volumes),
        )
        bar.delta = self.cumulative_delta  # type: ignore
        bar.buy_volume = sum(t[2] for t in self.trades if t[3] == 1)  # type: ignore
        bar.sell_volume = sum(t[2] for t in self.trades if t[3] == -1)  # type: ignore

        self.bars.append(bar)
        self.cumulative_delta = 0.0
        self.trades = []
        return bar

Delta diverjansı: En güçlü sinyallerden biri — kümülatif delta negatifken yükselen fiyat (satıcılar agresif ama fiyat yine de yükseliyor, bu limit alış emrinin absorpsiyonuna işaret eder). Dijital Parmak İzi: İşlemci Tanımlama makalesinde açıklanan davranışsal parmak izi yaklaşımıyla doğrudan ilgilidir. Avellaneda-Stoikov modelini kullanan piyasa yapıcılar için delta barları, envanter riski ve agresör baskısının gerçek zamanlı bir görünümünü sağlar.


Kayan pencere birleştirmesi Temel barların döngüsel bir tamponu: yeni veriler girer, eski veriler çıkar ve birleştirilmiş mum her zaman geçerlidir.

Birleştirme yöntemleri, temel barların daha yüksek zaman dilimi (HTF) mumlarına nasıl birleştirileceğini belirler. Bar türünden bağımsızdır — herhangi bir birleştirme yöntemini herhangi bir bar türüne uygulayabilirsiniz.

Yöntem A: Takvime Hizalanmış Birleştirme

Sabit bir takvim sınırı içinde düşen tüm temel barları birleştirin. "1 saatlik" mum, 14:00:00 ile 14:59:59 arasındaki tüm barları kapsar.

Özellikler:

  • Tüm piyasa katılımcıları aynı sınırları görür — piyasa yapısı analizi, destek/direnç, PIQ tetikleyicileri için gerekli
  • Soğuk başlangıç sorunu: yeniden başlatma sonrası kısmi mum
  • Zaman barları için doğal (borsaların doğal olarak sağladığı budur)
  • Zaman dışı barlar için de çalışır: "14:00 ile 15:00 arasında kapanan tüm hacim barları" = hacim barlarından takvime hizalanmış saatlik mum

Yöntem B: Kayan Pencere Birleştirmesi

Her yeni barda yeniden hesaplanan son N kapalı temel barı birleştirin. "1 saatlik" kayan mum = son 60 kapalı 1 dakikalık zaman barı, her dakika güncellenir.

Atomik birim kapalı temel bardır. Bu tasarım seçimi şunu sağlar:

  1. Soğuk başlangıç yok. N bardan sonra mum geçerlidir. Kısmi mum gürültüsü yoktur.
  2. Backtest eşliği. Canlı işlem, backtest motoruyla aynı atomik birimi kullanıyorsa sinyaller özdeştir.
  3. Basit doğrulama. Tek kural: tampon dolmadıysa: atla.
import numpy as np

class RollingCandleAggregator:
    """
    Kapalı temel barlardan kayan yüksek zaman dilimi mumları üretir.

    HERHANGİ bir bar türüyle çalışır: zaman barları, tick barları,
    hacim barları, dolar barları, delta barları — OHLCV çıktısı
    üreten her şey.

    Örnek: 1d zaman barlarıyla RollingCandleAggregator(window=60),
    her dakika güncellenen "1s" mumu üretir.

    Örnek: Hacim barlarıyla RollingCandleAggregator(window=24),
    son 24 hacim barını kapsayan bir mum üretir.
    """

    def __init__(self, window: int):
        self.window = window
        self.buffer: deque[OHLCV] = deque(maxlen=window)

    def push(self, bar: OHLCV) -> OHLCV | None:
        """
        Kapalı temel bar ekle. Yalnızca tampon dolduğunda
        (= mum geçerli olduğunda) birleştirilmiş mumu döndürür.
        """
        self.buffer.append(bar)

        if len(self.buffer) < self.window:
            return None

        return self._aggregate()

    def _aggregate(self) -> OHLCV:
        bars = list(self.buffer)
        return OHLCV(
            timestamp=bars[-1].timestamp,
            open=bars[0].open,
            high=max(b.high for b in bars),
            low=min(b.low for b in bars),
            close=bars[-1].close,
            volume=sum(b.volume for b in bars),
        )

    @property
    def is_valid(self) -> bool:
        return len(self.buffer) == self.window

Faz kayması ödünleşimi: :37'de başladıysanız kayan mumlar :37'de kapanır, herkes gibi :00'da değil. Bu, kalabalık tarafından görülebilen seviyelere bağlı stratejiler için önemlidir. Çözüm: ikisini de kullanın — piyasa yapısı için takvim, sinyaller için kayan.

Yöntem C: Adaptif Kayan Birleştirme

Kayan gibi, ancak pencere boyutu mevcut oynaklığa uyum sağlar. Sakin piyasalar → daha geniş pencere (daha fazla düzleştirme). Oynaklı piyasalar → daha dar pencere (daha hızlı tepki).

class AdaptiveRollingAggregator:
    """
    Pencere boyutunun oynaklığa uyum sağladığı kayan pencere.

    Herhangi bir temel bar türüyle çalışır. Oynaklık ölçüsü olarak
    son barların ATR'sini kullanır.

    Düşük oynaklık → daha geniş pencere (daha fazla düzleştirme, daha az sinyal)
    Yüksek oynaklık → daha dar pencere (daha hızlı tepki)
    """

    def __init__(
        self,
        base_window: int = 60,
        min_window: int = 15,
        max_window: int = 240,
        atr_period: int = 14,
        atr_base: float | None = None,
    ):
        self.base_window = base_window
        self.min_window = min_window
        self.max_window = max_window
        self.atr_period = atr_period
        self.atr_base = atr_base

        self.all_candles: deque[OHLCV] = deque(maxlen=max_window)
        self.atr_values: deque[float] = deque(maxlen=atr_period * 2)
        self.current_window = base_window

    def push(self, bar: OHLCV) -> OHLCV | None:
        self.all_candles.append(bar)

        tr = bar.high - bar.low
        self.atr_values.append(tr)

        if len(self.atr_values) < self.atr_period:
            return None

        current_atr = sum(list(self.atr_values)[-self.atr_period:]) / self.atr_period

        if self.atr_base is None and len(self.atr_values) >= self.atr_period * 2:
            self.atr_base = sum(self.atr_values) / len(self.atr_values)

        if self.atr_base is None or self.atr_base == 0:
            return None

        vol_ratio = current_atr / self.atr_base
        self.current_window = int(self.base_window / vol_ratio)
        self.current_window = max(self.min_window, min(self.max_window, self.current_window))

        if len(self.all_candles) < self.current_window:
            return None

        bars = list(self.all_candles)[-self.current_window:]
        return OHLCV(
            timestamp=bars[-1].timestamp,
            open=bars[0].open,
            high=max(b.high for b in bars),
            low=min(b.low for b in bars),
            close=bars[-1].close,
            volume=sum(b.volume for b in bars),
        )

Her temel bar türü her birleştirme yöntemiyle birleştirilebilir. Bazı kombinasyonlar standarttır (takvim zaman barları = borsaların sağladığı), diğerleri egzotik ama güçlüdür.

Kombinasyon Örnekleri

Temel Bar Türü Takvim Kayan Adaptif
Zaman Standart borsa mumları Her zaman geçerli HTF, soğuk başlangıç yok Oynaklık-adaptif zaman dilimi
Hacim "Bu saatteki tüm hacim barları" Son 24 hacim barı Sakin piyasalarda daha geniş pencere
Dolar Saatlik dolar barı toplamı Son N dolar barı Adaptif dolar pencereleri
Tick Dengesizliği Saatlik dengesizlik toplamı Son N dengesizlik olayı Oynaklı rejimlerde hızlı tepki
Delta Saatlik net emir akışı Kayan delta anlık görüntüsü Adaptif akış penceresi
Renko "Bu saatteki tuğlalar" Son N tuğla Adaptif tuğla sayısı

Hibrit Motor: Takvim + Kayan

Pratikte hem takvim hem de kayan birleştirmeyi eş zamanlı olarak istersiniz. Bellek yükü ihmal edilebilir — sembol başına zaman dilimi başına iki deque tamponu.

class HybridCandleEngine:
    """
    Herhangi bir temel bar türü için hem takvime hizalanmış hem de
    kayan mumları korur.

    Takvim mumları: piyasa yapısı, destek/direnç, PIQ için.
    Kayan mumlar: göstergeler, sinyal üretimi, girişler/çıkışlar için.
    """

    def __init__(self):
        self.rolling = {
            '1h': RollingCandleAggregator(60),
            '4h': RollingCandleAggregator(240),
        }
        self.calendar: dict[str, list[OHLCV]] = {
            '1h': [],
            '4h': [],
        }
        self._calendar_buffer: dict[str, list[OHLCV]] = {
            '1h': [],
            '4h': [],
        }

    def on_bar(self, bar: OHLCV):
        """Herhangi bir temel bar türünü işle — zaman, hacim, tick, delta vb."""
        rolling_results = {}
        for tf, agg in self.rolling.items():
            rolling_results[tf] = agg.push(bar)

        self._update_calendar(bar)

        return rolling_results

    def _update_calendar(self, bar: OHLCV):
        from datetime import datetime
        ts = datetime.utcfromtimestamp(bar.timestamp)

        for tf, minutes in [('1h', 60), ('4h', 240)]:
            self._calendar_buffer[tf].append(bar)

            total_minutes = ts.hour * 60 + ts.minute
            if (total_minutes + 1) % minutes == 0:
                bars = self._calendar_buffer[tf]
                if bars:
                    agg = OHLCV(
                        timestamp=bars[-1].timestamp,
                        open=bars[0].open,
                        high=max(b.high for b in bars),
                        low=min(b.low for b in bars),
                        close=bars[-1].close,
                        volume=sum(b.volume for b in bars),
                    )
                    self.calendar[tf].append(agg)
                    self._calendar_buffer[tf] = []

Zaman-Hacim Hibrit: Hacim Bölmeli Takvim

Özel bir birleştirme varyantı: hacim bir eşiği aştığında erken kapanan takvime hizalanmış mumlar. Aktivite artışlarına uyum sağlarken zaman senkronizasyonunu korur.

class TimeVolumeHybridGenerator:
    """
    Hacim artışlarında bölünen takvime hizalanmış mumlar.

    Kural: mumu takvim sınırında VEYA biriken hacim vol_threshold'u
    aştığında kapat, hangisi önce gelirse.

    Herhangi bir temel bar türüyle çalışır — hacim tetikleyicisi,
    takvim hizalamasının üzerine ekstra bir bölme boyutu ekler.
    """

    def __init__(
        self,
        interval_minutes: int = 60,
        vol_threshold: float = 5000.0,
    ):
        self.interval_minutes = interval_minutes
        self.vol_threshold = vol_threshold

        self.buffer: list[OHLCV] = []
        self.accumulated_volume = 0.0
        self.bars: list[OHLCV] = []

    def on_bar(self, bar: OHLCV) -> OHLCV | None:
        self.buffer.append(bar)
        self.accumulated_volume += bar.volume

        from datetime import datetime
        ts = datetime.utcfromtimestamp(bar.timestamp)
        total_minutes = ts.hour * 60 + ts.minute
        at_boundary = (total_minutes + 1) % self.interval_minutes == 0

        vol_spike = self.accumulated_volume >= self.vol_threshold

        if at_boundary or vol_spike:
            return self._close_bar(split_reason='volume' if vol_spike else 'time')

        return None

    def _close_bar(self, split_reason: str) -> OHLCV:
        bars = self.buffer
        bar = OHLCV(
            timestamp=bars[-1].timestamp,
            open=bars[0].open,
            high=max(b.high for b in bars),
            low=min(b.low for b in bars),
            close=bars[-1].close,
            volume=sum(b.volume for b in bars),
        )
        bar.split_reason = split_reason  # type: ignore
        bar.num_bars = len(bars)  # type: ignore

        self.bars.append(bar)
        self.buffer = []
        self.accumulated_volume = 0.0
        return bar

Pratik Birleştirme: Kademeli Ön Yükleme

Kademeli birleştirme Kademeli ön yükleme: günlük mumları saatlik, saatlik mumları dakikalık mumlardan oluşturmak — API sınırlarını aşmak.

Borsalar ne kadar tarihsel veri sunduklarını sınırlar. Binance REST isteği başına ~1000 mum sunarken OKX 300 ile sınırlıdır. Kayan 1G muma (1440 dakika) ihtiyacınız varsa, her zaman yeterli 1d geçmiş elde edemezsiniz. WebSocket aracılığıyla işlemler ve emir defterlerinin gerçek zamanlı akışı için bkz. CCXT Pro WebSocket Yöntemleri.

Çözüm: kademeli birleştirme — her derinlikte mevcut en yüksek çözünürlükten daha yüksek zaman dilimlerini oluşturun, ardından birleştirin.

Kayan 1H mum:
├── 6 tamamlanmış 1G mum ← REST /klines?interval=1d'den getir
├── 1 kısmi gün:
│   ├── 23 tamamlanmış 1s mum ← REST /klines?interval=1h'den getir
│   └── 1 kısmi saat:
│       └── N tamamlanmış 1d mum ← REST /klines?interval=1m'den getir
└── Canlı: her yeni kapalı 1d mum tüm zinciri günceller

Bu çalışır çünkü OHLCV birleştirmesi birleştirilebilirdir: 1G mumun yüksek değeri 24 saatlik yüksek değerlerin maksimumudur, bu da 1440 dakikalık yüksek değerlerin maksimumudur.

Çok Borsa Sınırları

Borsa Maks. 1d Mum Maks. 1s Mum Önemli Aralıklar
Binance 1.000 1.000 1d–1A, tam aralık
Bybit 1.000 1.000 1–720, G/H/A
OKX 300 300 1d–1A (daha kısıtlayıcı)
Gate.io 1.000 1.000 10sn–30g

Birleştirme Tutarlılık Kontrolü

REST API'dan alınan 1 saatlik mum, 60 adet 1 dakikalık mumdan hesaplayacağınız mumla eşleşmeyebilir. Her zaman doğrulayın:

def validate_aggregation(
    candle_htf: OHLCV,
    candles_ltf: list[OHLCV],
    tolerance_pct: float = 0.001,
) -> dict[str, bool]:
    agg = OHLCV(
        timestamp=candles_ltf[-1].timestamp,
        open=candles_ltf[0].open,
        high=max(c.high for c in candles_ltf),
        low=min(c.low for c in candles_ltf),
        close=candles_ltf[-1].close,
        volume=sum(c.volume for c in candles_ltf),
    )

    def close_enough(a: float, b: float) -> bool:
        if a == 0 and b == 0:
            return True
        return abs(a - b) / max(abs(a), abs(b)) < tolerance_pct

    return {
        'open': close_enough(candle_htf.open, agg.open),
        'high': close_enough(candle_htf.high, agg.high),
        'low': close_enough(candle_htf.low, agg.low),
        'close': close_enough(candle_htf.close, agg.close),
        'volume': close_enough(candle_htf.volume, agg.volume),
    }

Doğrulama tutarlı bir şekilde başarısız olursa, her zaman 1d'den kendiniz birleştirinbacktesting eşliği için borsanın HTF mumuna asla güvenmeyin.


Karşılaştırma Matrisi

Eksen 1: Temel Bar Türleri

# Bar Türü Tetikleyici Tick Verisi Gerekli En İyi Kullanım
1 Zaman Sabit aralık Hayır Piyasa yapısı, kalabalık davranışı
2 Tick N işlem Evet ML özellikleri, eşit-görüş örneklemesi
3 Hacim N birim işlem Evet Normalize edilmiş aktivite analizi
4 Dolar N$ nominal Evet Çapraz varlık karşılaştırması
5 Renko Fiyat ± N birim Hayır Trend takibi, gürültü filtreleme
6 Aralık Yüksek-Düşük ≥ N Evet Kırılım tespiti
7 Oynaklık Adaptif aralık Evet Rejim-adaptif analiz
8 Heikin-Ashi Dönüşüm Hayır Trend onayı (sentetik fiyatlar!)
9 Kagi Fiyat geri dönüşü Hayır Arz/talep yapısı
10 Line Break N-çizgi kırılımı Hayır Makro trend filtresi
11 Nokta & Şekil Kutu + geri dönüş Hayır Destek/direnç haritası
12 TIB Tick dengesizliği Evet Bilgili akış tespiti
13 VIB Hacim dengesizliği Evet Büyük emir tespiti
14 Run Seri uzunluğu Evet Emir bölme tespiti
15 CUSUM Kümülatif getiri Hayır (1d kapanışları) Yapısal kırılma olayları
16 Entropi Shannon entropisi Evet ML araştırması, özellik saflığı
17 Delta Emir akışı deltası Evet (aggTrades) Agresör akış analizi

Eksen 2: Birleştirme Yöntemleri

Yöntem Hizalama Soğuk Başlangıç Faz Kayması En İyi Kullanım
Takvim Duvar saati Kısmi bar riski Yok (kalabalık hizalı) Piyasa yapısı, PIQ, D/D
Kayan N bar Yok (ısınma sonrası) Evet (:00'dan kaymış) Göstergeler, sinyaller
Adaptif Oynaklık odaklı N ATR kalibrasyonundan sonra Evet Oynaklık-adaptif stratejiler

Pratik Öneriler

Katmanlı mimari Dört katmanlı mum mimarisi: kayan sinyaller, takvim yapısı, mikroyapı akışı ve trend filtreleri.

Backtest motorunuz 1d OHLCV verisinde çalışıyorsa:

  1. Kayan zaman barları — en basit yükseltme. Ek veri yok. Soğuk başlangıcı ortadan kaldırır.
  2. Hibrit (kayan + takvim) zaman barları — piyasa yapısı için takvim, sinyaller için kayan.
  3. CUSUM filtresi — 1d kapanışlarında çalışır, tick verisi gerekmez. "Yeterince hareket etti, ilginç."

Tick/işlem veriniz varsa:

  1. Dolar barları + kayan — nicel finans literatüründen önerilen varsayılan.
  2. Hacim dengesizlik barları + kayan — bilgili akışı tespit eder, önemli olaylar sırasında daha fazla örnekleme yapar.
  3. Delta barları + takvim — agresör tarafı sınıflandırmanız varsa, piyasayı kimin ittiğinin en doğrudan görünümü.

Filtreler olarak (Heikin-Ashi veya Line Break'i herhangi bir temel+birleştirme kombinasyonunun üzerine uygulayın):

  1. Kayan hacim barları üzerine Heikin-Ashi — aktivite normalize edilmiş veride temiz trend sinyalleri.
  2. Günlük takvim barları üzerine Line Break / Kagi — makro trend filtresi.

Marketmaker.cc için özellikle — katmanlı bir yaklaşım:

  • Katman 1 (sinyaller): Göstergeler ve giriş/çıkış sinyalleri için zaman barlarının kayan birleştirmesi. Soğuk başlangıç yok, mükemmel backtest eşliği.
  • Katman 2 (piyasa yapısı): Destek/direnç, saatlik kapanış analizi ve PIQ tetikleyicileri için takvime hizalanmış zaman barları.
  • Katman 3 (mikroyapı): Ham işlem akışından bilgili akışı tespit etmek, emir bölmeyi ve büyük hareketleri öngörmek için hacim dengesizlik barları + delta barları. Emir akışı verilerinde davranışsal örüntü tanıma için ayrıca bkz. Dijital Parmak İzi: İşlemci Tanımlama.
  • Katman 4 (trend filtresi): Sinyalleri makro yönle hizalamak için kayan barlar üzerine Heikin-Ashi dönüşümü veya 4s takvim kapanışları üzerine Line Break.

Sonuç

Mum oluşturma tek bir seçim değildir — iki bağımsız karardır:

  1. Ne tür bir bar? Zaman, saat aralıklarını yakalar. Aktivite (tick, hacim, dolar) piyasa katılımını yakalar. Fiyat (Renko, aralık, oynaklık) hareketleri yakalar. Bilgi (dengesizlik, run'lar, CUSUM, entropi) yeni bilginin varışını yakalar. Emir akışı (delta) agresif baskıyı yakalar.

  2. Daha yüksek zaman dilimlerine nasıl birleştirileceği? Takvim kalabalıkla hizalanır. Kayan soğuk başlangıcı ortadan kaldırır. Adaptif oynaklığa tepki verir.

Standart "Binance'ten 1 saatlik mum" bir 17×3 matrisinde yalnızca tek bir hücredir. Diğer 50 kombinasyon bunları uygulamaya istekli olan herkes için mevcuttur. Bir üretim sistemi için yanıt şudur: "karar motorunuzun her katmanı için doğru kombinasyonu seçin."

Atomik birim — kapalı temel bar — temel olmaya devam eder. Geri kalan her şey birleştirmedir.

Ayrıntılı verilerle backtest doğruluğu hakkında daha fazla bilgi için bkz. Adaptif Detaylandırma: Değişken Granülerlikle Backtest. Çok zaman dilimli stratejilerde gösterge ön hesaplamasının etkisi için bkz. Birleştirilmiş Parquet Önbelleği.


Faydalı Bağlantılar

  1. Lopez de Prado — Finansal Makine Öğrenmesinde Gelişmeler (2018)
  2. Easley, Lopez de Prado, O'Hara — Hacim Saati: Yüksek Frekanslı Paradigmaya İçgörüler (2012)
  3. mlfinlab — Bilgi odaklı barları uygulayan Python kütüphanesi
  4. Binance — Tarihsel Piyasa Verisi
  5. Apache Parquet — sütun depolama formatı

Atıf

@article{soloviov2026bartypes,
  author = {Soloviov, Eugen},
  title = {Bar Types and Aggregation Methods for Algorithmic Trading},
  year = {2026},
  url = {https://marketmaker.cc/tr/blog/post/beyond-time-bars-candle-construction},
  description = {Mum oluşturmanın iki eksenli sınıflandırması: 17 temel bar türü × 3 birleştirme yöntemi = 51 kombinasyon; kripto algotrading için uygulama kodu ve pratik önerilerle.}
}
Sorumluluk Reddi: Bu makalede sağlanan bilgiler yalnızca eğitim ve bilgilendirme amaçlıdır ve finansal, yatırım veya ticaret tavsiyesi niteliği taşımaz. Kripto para ticareti önemli bir kayıp riski içerir.

Yazarlar

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Piyasanın Önünde Olun

Özel yapay zeka ticaret içgörüleri, piyasa analizi ve platform güncellemeleri için bültenimize abone olun.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.