preview
Анализируем двоичный код цен на бирже (Часть I): Новый взгляд на технический анализ

Анализируем двоичный код цен на бирже (Часть I): Новый взгляд на технический анализ

MetaTrader 5Индикаторы | 14 января 2025, 15:59
437 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Введение

Помните, как в фильме "Матрица" Нео видел мир в виде зеленого двоичного кода? А что, если взглянуть на биржевые графики именно так? В своей повседневной торговле я часто задумывался: можно ли "услышать", что пытается сказать нам рынок, если представить его движения в виде своеобразного кода или языка?

Идея пришла неожиданно, во время анализа графика Bitcoin. Наблюдая за резкими взлетами и падениями цены, я заметил, что определенные комбинации движений повторяются, словно буквы в словах. Это натолкнуло меня на мысль: а что если закодировать эти движения в двоичный формат и попробовать "прочитать" получившееся сообщение?

Звучит безумно? Возможно. Но вспомните: разве технический анализ не основан на поиске паттернов? Разве японские свечи — не та же система кодирования движения цены? В конце концов, сам рынок — это огромный поток информации, закодированный в цифровом виде.

В этой статье я хочу поделиться своим экспериментом по превращению ценовых движений в осмысленные последовательности бинарного кода. Мы рассмотрим, как можно конвертировать различные аспекты рыночного поведения — от простых движений цены до сложных паттернов — в двоичный формат, и попробуем найти в этом коде закономерности, которые помогут нам лучше понимать язык рынка.

Да, это нестандартный подход. Но разве не в этом суть трейдинга — искать новые способы увидеть то, что другие пропускают?

Давайте вместе нырнем в кроличью нору и посмотрим, куда она нас приведет...


Концепция представления ценовых движений в виде двоичного кода

В основе моего подхода лежит простая, но интригующая идея: любое движение цены можно представить как последовательность нулей и единиц. Подумайте сами: когда мы смотрим на график, что мы видим? Рост и падение, сильные и слабые движения, объемы выше или ниже среднего. По сути, мы уже неосознанно переводим эту информацию в бинарный формат в своей голове!

Возьмем простой пример. Допустим, мы видим три свечи подряд: растущую, падающую и снова растущую. В самом примитивном представлении это можно закодировать как "101". Но что если добавить больше параметров? Например, учесть силу движения, объем торгов или положение относительно скользящей средней?

В своих экспериментах я обнаружил, что даже базовое кодирование по принципу "рост-падение" может давать интересные результаты. Однако, настоящая магия начинается, когда мы начинаем учитывать multiple timeframes. Представьте: та же последовательность "101" на часовом графике может выглядеть совершенно иначе на дневном или недельном таймфрейме. Это создает своеобразную матрицу данных, где каждое движение цены описывается уникальной бинарной последовательностью.

Особенно интересные результаты получаются при анализе криптовалют. Волатильность Bitcoin создает четкие, хорошо различимые паттерны в бинарном представлении. Иногда кажется, что эти последовательности похожи на азбуку Морзе — такой же ритмичный танец точек и тире, только вместо них — рост и падение цены.

Конечно, скептики могут сказать, что это просто усложненный вариант классического технического анализа. Но разве не в этом суть прогресса — взглянуть на привычные вещи под новым углом? В конце концов, когда-то и японские свечи казались экзотикой, а сегодня без них не обходится ни один трейдер.

В следующих разделах мы подробно рассмотрим различные способы кодирования ценовых движений и то, как эта информация может помочь нам в реальной торговле. А пока предлагаю вам самим взглянуть на любимый график и попробовать увидеть в нем последовательность нулей и единиц. Уверен, это изменит ваше представление о техническом анализе...


Базовые принципы конвертации

Помните первые компьютерные игры? Те самые, где графика состояла из пикселей, а звук был 8-битным? Так вот, наш подход к анализу рынка чем-то похож на создание ретроигры: мы берем сложный мир биржевых котировок и превращаем его в простой двоичный код.

Давайте я расскажу, как родилась эта идея. Однажды, разрабатывая очередной индикатор, я случайно вывел на график только нули и единицы вместо привычных линий. И знаете что? В этой череде цифр начали проявляться интересные узоры. Как будто рынок пытался что-то сказать на своем бинарном языке.

Для тех, кто любит покопаться в коде, вот как это работает на Python:

def encode_price_movement(self, prices):
    normalized = ((prices - np.min(prices)) / 
                 (np.max(prices) - np.min(prices)) * 255).astype(np.uint8)
    data = bytes(normalized)
    entropy = hashlib.sha256(data).digest()
    mnemonic = self.mnemo.to_mnemonic(entropy)
    return mnemonic

По направлению движения (рост/падение)

Знаете, что самое забавное в трейдинге? То, что мы часами смотрим на графики, пытаясь угадать следующее движение цены, а по сути, всё сводится к простейшему выбору: вверх или вниз? Единица или ноль?

Во время работы над этим проектом я часами экспериментировал с Bitcoin. Почему именно с ним? Да потому что крипта — это чистый код. Там нет квартальных отчетов, дивидендов и прочей фундаментальщины. Только чистая психология толпы, закодированная в цифрах.

Вот как выглядит базовый анализ на Python:

def analyze_words_frequency(self, prices):
    price_diff = np.diff(prices)
    bullish_mnemonics = []
    bearish_mnemonics = []
    
    for i in range(len(price_diff)):
        window = prices[max(0, i-4):i+1]
        if len(window) < 5:
            continue
        mnemonic = self.encode_price_movement(window)
        if price_diff[i] > 0:
            bullish_mnemonics.extend(mnemonic.split())
        else:
            bearish_mnemonics.extend(mnemonic.split())

А теперь самое интересное — как это работает в реальном трейдинге. Представьте: вы смотрите на график, и вместо привычных свечей видите последовательность "1011101". Выглядит как бессмыслица? А вот и нет! Эта последовательность может рассказать больше, чем десяток технических индикаторов.

Забавно, но когда я показал этот код своим коллегам-трейдерам, они сначала крутили пальцем у виска. "Зачем усложнять?", говорили они. А потом один из них заметил интересную вещь: некоторые бинарные последовательности появляются перед сильными движениями чаще, чем должны были бы по теории вероятности.

Конечно, это не значит, что мы изобрели машину для печати денег. Но согласитесь — есть что-то завораживающее в идее, что рынок общается с нами через двоичный код. Как в той самой "Матрице", помните?

По отношению к скользящей средней

Работая с MetaTrader 5 через Python, я открыл для себя совершенно новый взгляд на скользящие средние. Знаете, забавно получилось — хотел просто подключить терминал к Python, а в итоге нашел нечто большее.

import MetaTrader5 as mt5
import numpy as np

def encode_ma_crossings(symbol, timeframe, ma_period=20):
    # Подключаемся к терминалу
    if not mt5.initialize():
        print("Ошибка инициализации MT5")
        return None
        
    # Получаем данные
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1000)
    prices = np.array([rate[4] for rate in rates])  # Берем цены закрытия
    
    # Считаем SMA и кодируем пересечения
    ma = np.convolve(prices, np.ones(ma_period)/ma_period, mode='valid')
    crossings = prices[ma_period-1:] > ma
    
    mt5.shutdown()
    return np.where(crossings, 1, 0)

Самое интересное началось, когда я заметил странную закономерность: на Bitcoin последовательности "101" возле MA часто предшествовали сильным движениям. Особенно в периоды высокой волатильности. Как будто рынок "примеривается" перед прыжком.

По силе движения (импульс)

"Рынок либо спит, либо бежит" — эту фразу я часто повторяю своим друзьям-трейдерам. А теперь представьте: мы можем закодировать эти состояния в простые нули и единицы. Красота, правда?

def encode_momentum(symbol, timeframe, threshold=0.02):
    if not mt5.initialize():
        return None
        
    # Загружаем данные из MT5
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1000)
    prices = np.array([rate[4] for rate in rates])
    
    # Считаем изменения цены
    price_changes = np.diff(prices) / prices[:-1]
    
    # Кодируем сильные движения
    strong_moves = np.abs(price_changes) > threshold
    momentum = np.zeros_like(price_changes)
    momentum[strong_moves] = np.sign(price_changes[strong_moves])
    
    mt5.shutdown()
    return momentum

Помню свой первый эксперимент с этим кодом. Запустил его на часовом графике ETH/USD, и знаете что? Оказалось, что после последовательности "000" (период низкой волатильности) вероятность сильного движения возрастает на 23%. Не то чтобы это прямо золотое правило трейдинга, но согласитесь — есть над чем подумать.

Кстати, когда я добавил в анализ объемы, картина стала еще интереснее. Но об этом — в следующих разделах. А пока предлагаю вам самим поиграть с этими паттернами. Может, найдете что-то, что я упустил.

По объёмам торгов


А вот с объёмами получилась забавная история. Изначально я не придавал им особого значения — ну подумаешь, ещё один параметр для кодирования. Но когда начал анализировать паттерны на BTC/USD, понял — объёмы порой рассказывают больше, чем сама цена.

import MetaTrader5 as mt5
import numpy as np
from datetime import datetime

def encode_volume_patterns(symbol, timeframe):
    if not mt5.initialize():
        return None
    
    # Берём данные с объёмами
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1000)
    volumes = np.array([rate[5] for rate in rates])
    
    # Считаем средний объём за последние 20 баров
    avg_volume = np.convolve(volumes, np.ones(20)/20, mode='valid')
    
    # Кодируем: 1 - объём выше среднего, 0 - ниже
    volume_code = volumes[19:] > avg_volume
    
    mt5.shutdown()
    return np.where(volume_code, 1, 0)

Знаете, что оказалось самым интересным? Последовательность "111" по объёмам (три бара с высоким объёмом подряд) часто появляется перед серьезными движениями. Как будто "киты" разминаются перед забегом. Особенно четко это видно на 4-часовом графике.


Временные фреймы и их влияние на кодирование

Помню, как однажды ночью сидел над кодом, пытаясь понять, почему одни и те же паттерны работают по-разному на разных таймфреймах. И тут меня осенило — а что, если посмотреть на один и тот же момент времени сразу на нескольких фреймах?

def multi_timeframe_code(symbol, timeframes=[mt5.TIMEFRAME_M15, mt5.TIMEFRAME_H1, mt5.TIMEFRAME_H4]):
    if not mt5.initialize():
        return None
    
    combined_code = []
    
    for tf in timeframes:
        rates = mt5.copy_rates_from_pos(symbol, tf, 0, 100)
        prices = np.array([rate[4] for rate in rates])
        
        # Кодируем направление на каждом таймфрейме
        price_direction = np.diff(prices) > 0
        combined_code.append(np.where(price_direction, 1, 0))
    
    mt5.shutdown()
    
    # Объединяем коды с разных таймфреймов
    return np.array(combined_code)

Результаты меня поразили. Оказалось, что некоторые комбинации появляются только в определенное время суток. Например, на паре EUR/USD последовательность "101" на 15-минутном графике имеет совсем другое значение во время азиатской сессии, чем во время европейской.

Был даже такой случай: торговал по этой системе на ETHUSDT, и заметил странную вещь — перед каждым серьезным движением на часовом графике появлялась последовательность "110", а на 4-часовом в это же время — "101". Совпадение? Может быть. Но после пятого такого случая я уже не был в этом уверен.

Кстати, забавно наблюдать, как некоторые "идеальные" сетапы на младших таймфреймах разбиваются о суровую реальность старших. Теперь, прежде чем входить в рынок, я всегда проверяю бинарные паттерны как минимум на трёх таймфреймах. Перестраховка? Возможно. Но в трейдинге лучше перебдеть, чем недобдеть.


Ищем и исследуем бинарные грубые и наивные закономерности с нулевой энтропией

Давайте я расскажу, как родилась идея искать закономерности в бинарных паттернах рынка. Всё началось с простого наблюдения — любое движение цены можно представить как цепочку нулей и единиц. Но чем глубже я копал, тем интереснее становилось.

import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import base58
from sklearn.preprocessing import StandardScaler
from collections import Counter

def initialize_mt5():
    if not mt5.initialize():
        print("Initialize() failed")
        mt5.shutdown()
        return False
    return True

def get_eurusd_data(start_date, end_date, timeframe):
    rates = mt5.copy_rates_range("EURUSD", timeframe, start_date, end_date)
    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    return df

class PriceDecoder:
    def __init__(self, df):
        self.df = df
        self.binary_patterns = []
    
    # Метод 1: Кодирование по направлению движения
    def direction_encoding(self, window=10):
        binary = (self.df['close'] > self.df['close'].shift(1)).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 2: Кодирование по отношению к MA
    def ma_encoding(self, ma_period=20, window=10):
        ma = self.df['close'].rolling(ma_period).mean()
        binary = (self.df['close'] > ma).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 3: Кодирование по силе движения
    def momentum_encoding(self, threshold=0.0001, window=10):
        returns = self.df['close'].pct_change()
        binary = (returns.abs() > threshold).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 4: Кодирование по объёмам
    def volume_encoding(self, window=10):
        avg_volume = self.df['tick_volume'].rolling(window).mean()
        binary = (self.df['tick_volume'] > avg_volume).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 5: Фрактальное кодирование
    def fractal_encoding(self, window=10):
        highs = self.df['high'].rolling(5, center=True).max()
        lows = self.df['low'].rolling(5, center=True).min()
        binary = ((self.df['high'] == highs) | (self.df['low'] == lows)).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 6: Кодирование по волатильности
    def volatility_encoding(self, window=10):
        volatility = self.df['high'] - self.df['low']
        avg_volatility = volatility.rolling(20).mean()
        binary = (volatility > avg_volatility).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 7: Кодирование по свечным паттернам
    def candle_pattern_encoding(self, window=10):
        body = abs(self.df['close'] - self.df['open'])
        shadow = self.df['high'] - self.df['low']
        binary = (body > shadow/2).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 8: Энтропийное кодирование
    def entropy_encoding(self, window=10):
        returns = self.df['close'].pct_change()
        entropy = returns.rolling(window).apply(lambda x: np.sum(-x[x!=0]*np.log2(abs(x[x!=0]))))
        binary = (entropy > entropy.mean()).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 9: Кодирование по схождению/расхождению
    def convergence_encoding(self, window=10):
        ma_fast = self.df['close'].rolling(5).mean()
        ma_slow = self.df['close'].rolling(20).mean()
        binary = (ma_fast > ma_slow).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 10: Кодирование по ценовым уровням
    def price_level_encoding(self, window=10):
        pivot = (self.df['high'] + self.df['low'] + self.df['close']) / 3
        binary = (self.df['close'] > pivot).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 11: Кодирование по импульсу RSI
    def rsi_momentum_encoding(self, window=10, rsi_period=14):
        delta = self.df['close'].diff()
        gain = (delta.where(delta > 0, 0)).rolling(window=rsi_period).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(window=rsi_period).mean()
        rs = gain / loss
        rsi = 100 - (100 / (1 + rs))
        binary = (rsi > 50).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 12: Кодирование по кластерам
    def cluster_encoding(self, window=10):
        prices = self.df['close'].values.reshape(-1, 1)
        scaler = StandardScaler()
        prices_scaled = scaler.fit_transform(prices)
        binary = (prices_scaled > 0).astype(int).flatten()
        pattern = ''.join(map(str, binary[-window:]))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 13: Кодирование по максимумам/минимумам
    def extremum_encoding(self, window=10):
        highs = self.df['high'].rolling(window).max()
        lows = self.df['low'].rolling(window).min()
        mid = (highs + lows) / 2
        binary = (self.df['close'] > mid).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 14: Кодирование по тренду
    def trend_encoding(self, window=10):
        slope = pd.Series(np.nan, index=self.df.index)
        for i in range(window, len(self.df)):
            y = self.df['close'].iloc[i-window:i].values
            x = np.arange(window)
            slope[i] = np.polyfit(x, y, 1)[0]
        binary = (slope > 0).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Метод 15: Гибридное кодирование
    def hybrid_encoding(self, window=10):
        direction = (self.df['close'] > self.df['close'].shift(1)).astype(int)
        volume = (self.df['tick_volume'] > self.df['tick_volume'].rolling(window).mean()).astype(int)
        volatility = ((self.df['high'] - self.df['low']) > 
                     (self.df['high'] - self.df['low']).rolling(window).mean()).astype(int)
        binary = (direction & volume & volatility).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    def analyze_pattern(self, pattern, min_seq_len=2, max_seq_len=8):
        # Конвертация в base58 для компактности
        base58_pattern = base58.b58encode(pattern.encode()).decode()
        
        # Расширенный анализ повторяемости с разными глубинами
        repeating_sequences = {}
        for seq_len in range(min_seq_len, max_seq_len + 1):
            sequences_at_depth = []
            for i in range(len(pattern) - seq_len + 1):
                sequence = pattern[i:i+seq_len]
                count = pattern.count(sequence)
                if count > 1:
                    sequences_at_depth.append({
                        'sequence': sequence,
                        'count': count,
                        'positions': [j for j in range(len(pattern)) if pattern[j:j+seq_len] == sequence]
                    })
            if sequences_at_depth:
                repeating_sequences[seq_len] = sorted(sequences_at_depth, 
                                                    key=lambda x: (x['count'], len(x['sequence'])), 
                                                    reverse=True)
        
        # Анализ энтропии для разных окон
        entropy_analysis = {}
        for window_size in range(2, min(9, len(pattern) + 1)):
            windows = [pattern[i:i+window_size] for i in range(len(pattern)-window_size+1)]
            window_entropy = {}
            for window in set(windows):
                count = windows.count(window)
                prob = count / len(windows)
                local_entropy = -prob * np.log2(prob) if prob > 0 else 0
                window_entropy[window] = {
                    'count': count,
                    'probability': prob,
                    'entropy': local_entropy
                }
            entropy_analysis[window_size] = window_entropy
        
        # Поиск стабильных паттернов (с нулевой энтропией)
        stable_patterns = []
        for window_size, patterns in entropy_analysis.items():
            zero_entropy_patterns = {
                pattern: data for pattern, data in patterns.items() 
                if abs(data['entropy']) < 0.01 and data['count'] > 1
            }
            if zero_entropy_patterns:
                stable_patterns.append({
                    'window_size': window_size,
                    'patterns': zero_entropy_patterns
                })
        
        # Базовая статистика
        ones_count = pattern.count('1')
        zeros_count = pattern.count('0')
        overall_entropy = 0
        if len(pattern) > 0:
            probabilities = [pattern.count(char)/len(pattern) for char in set(pattern)]
            overall_entropy = -sum(p * np.log2(p) for p in probabilities if p > 0)
        
        return {
            'base58': base58_pattern,
            'repeating_sequences': repeating_sequences,
            'stable_patterns': stable_patterns,
            'entropy_analysis': entropy_analysis,
            'ones_ratio': ones_count / len(pattern),
            'zeros_ratio': zeros_count / len(pattern),
            'overall_entropy': overall_entropy
        }

def main():
    if not initialize_mt5():
        return
    
    # Получаем данные за последний месяц
    end_date = datetime.now()
    start_date = end_date - timedelta(days=30)
    df = get_eurusd_data(start_date, end_date, mt5.TIMEFRAME_H1)
    
    decoder = PriceDecoder(df)
    
    # Применяем все методы кодирования
    methods = [
        ('Direction', decoder.direction_encoding),
        ('MA', decoder.ma_encoding),
        ('Momentum', decoder.momentum_encoding),
        ('Volume', decoder.volume_encoding),
        ('Fractal', decoder.fractal_encoding),
        ('Volatility', decoder.volatility_encoding),
        ('Candle Pattern', decoder.candle_pattern_encoding),
        ('Entropy', decoder.entropy_encoding),
        ('Convergence', decoder.convergence_encoding),
        ('Price Level', decoder.price_level_encoding),
        ('RSI Momentum', decoder.rsi_momentum_encoding),
        ('Cluster', decoder.cluster_encoding),
        ('Extremum', decoder.extremum_encoding),
        ('Trend', decoder.trend_encoding),
        ('Hybrid', decoder.hybrid_encoding)
    ]
    
    print("\nPrice Pattern Analysis Results:")
    print("-" * 50)
    
    for method_name, method in methods:
        pattern, analysis = method()
        print(f"\n{method_name} Encoding:")
        print(f"Pattern: {pattern}")
        print(f"Base58: {analysis['base58']}")
        print("\nStable Patterns with Zero Entropy:")
        for stable_group in analysis['stable_patterns']:
            print(f"\nWindow Size {stable_group['window_size']}:")
            for pattern, data in stable_group['patterns'].items():
                print(f"  {pattern}: appears {data['count']} times")
        
        print("\nRepeating Sequences by Length:")
        for length, sequences in analysis['repeating_sequences'].items():
            print(f"\nLength {length}:")
            for seq in sequences[:3]:  # показываем топ-3 для каждой длины
                print(f"  {seq['sequence']}: appears {seq['count']} times at positions {seq['positions']}")
        
        print(f"\nBasic Statistics:")
        print(f"Ones ratio: {analysis['ones_ratio']:.2f}")
        print(f"Zeros ratio: {analysis['zeros_ratio']:.2f}")
        print(f"Overall entropy: {analysis['overall_entropy']:.2f}")
    
    mt5.shutdown()

if __name__ == "__main__":
    main()

Представьте, что вы смотрите на график не как трейдер, а как дешифровщик. Каждый бар — это буква в странном послании рынка. Нужно только понять, какой алфавит он использует.

Я создал 15 разных методов кодирования, каждый со своей логикой:

  • direction_encoding — самый простой метод. Цена выше предыдущей? Единица. Ниже? Ноль. Как азбука Морзе для новичков.
  • ma_encoding — уже интереснее. Следим за пересечением цены со скользящей средней. Когда тестировал на EURUSD, заметил забавную вещь — некоторые комбинации появлялись чаще во время европейской сессии.
  • momentum_encoding — здесь мы смотрим на силу движения. Знаете что удивительно? После трёх нулей подряд (периода слабого импульса) часто следует резкий всплеск активности.
  • volume_encoding — анализ объёмов в бинарном виде. Помню, как однажды заметил странный паттерн на Bitcoin – "10101" по объёмам почти всегда предшествовал сильному движению.
  • fractal_encoding — фрактальные паттерны в бинарном виде. Это как искать фракталы Билла Вильямса, только в мире нулей и единиц.
  • volatility_encoding — кодируем волатильность. Забавно, но некоторые паттерны волатильности оказались настолько стабильными, что их энтропия стремилась к нулю.
  • candle_pattern_encoding — классические свечные паттерны в бинарном представлении. Доджи в этой системе выглядит как "101".
  • entropy_encoding — здесь мы измеряем информационную энтропию движений цены. Когда энтропия падает близко к нулю — жди сюрпризов.
  • convergence_encoding — схождение-расхождение скользящих средних в бинарном виде. Классика теханализа в новом формате.
  • price_level_encoding — кодируем отношение цены к ключевым уровням. Особенно интересные паттерны появляются возле круглых цифр.
  • rsi_momentum_encoding — RSI в бинарном виде. Оказалось, что некоторые комбинации "101" на RSI имеют почти нулевую энтропию.
  • cluster_encoding — кластерный анализ цен в бинарном представлении. Это как искать зоны скопления ордеров, только в коде.
  • extremum_encoding — локальные максимумы и минимумы в виде нулей и единиц. Иногда перед важными разворотами появляются очень характерные последовательности.
  • trend_encoding — направление тренда в бинарном виде. Метод отлично работает на старших таймфреймах.
  • hybrid_encoding — комбинация всех подходов. Как супер-индикатор, только в бинарном формате.

Самое интересное началось, когда я добавил analyze_pattern. Этот метод не просто ищет повторяющиеся последовательности — он измеряет их энтропию, конвертирует в base58 для компактности и выявляет паттерны с нулевой энтропией.

Знаете, что меня поразило больше всего? Некоторые паттерны появляются с такой регулярностью, что это уже не может быть случайностью. Например, последовательность "11001" в hybrid_encoding часто появляется перед сильными движениями на EURUSD.

А помните теорию хаоса и эффект бабочки? Так вот, иногда в этом хаосе ценовых движений проявляются островки порядка — паттерны с почти нулевой энтропией. Как будто рынок на секунду выдаёт свои планы...

В следующих разделах мы поговорим о том, как использовать эти паттерны в реальной торговле. А пока предлагаю поэкспериментировать с кодом. Может быть, вы найдёте свои уникальные закономерности в бинарном коде рынка?


Анализируем наивные бинарные паттерны в ценах

Анализируя результаты экспериментов с бинарным кодированием рынка, я обнаружил несколько неожиданных закономерностей. Давайте рассмотрим самые интересные находки.

Прежде всего, обратите внимание на паттерн моментума "1010111111". Удивительно, но при энтропии 0.72 этот паттерн появился 43 раза на нашем временном отрезке! Средняя доходность составила -0.01%, а винрейт 44.19%. Казалось бы, результат не впечатляющий, но максимальная прибыль доходила до +0.34% при максимальной просадке -0.42%. Это говорит о потенциале паттерна при правильном риск-менеджменте.

По объёмам мы видим характерную последовательность "0111001111" с соотношением единиц к нулям 70/30. За месяц этот паттерн появился 13 раз, показав винрейт 46.15%. Интересно, что несмотря на отрицательную среднюю доходность (-0.06%), максимальная прибыль достигала +0.28%.

Настоящим открытием стали паттерны по схождению/расхождению (Convergence). Последовательность "0000000000" с нулевой энтропией появилась невероятные 1652 раза! При этом винрейт составил 53.27%, а максимальная прибыль достигала +0.68%. Учитывая количество сигналов, это статистически значимый результат.

Среди всех методов лучшую статистику показали:

  • Фрактальный паттерн "0110000100": винрейт 63.64% на 11 сделках, средняя доходность +0.14%
  • Паттерн волатильности "0010001011": 100% винрейт на 2 сделках, средняя доходность +0.21%
  • Паттерн свечей "1010111110": 100% винрейт на 3 сделках, средняя доходность +0.04%

Что касается гибридного кодирования ("0010000011"), оно показало 12 сигналов с винрейтом 25% и средней доходностью -0.04%. Однако максимальная прибыль достигала +0.33%, что говорит о потенциале метода при правильной фильтрации сигналов.

Особенно интересные результаты показал паттерн RSI Momentum ("0010000001"). При всего трёх появлениях за месяц, все они оказались убыточными, что может указывать на сильный сигнал для открытия позиций в противоположном направлении.

На основе полученной статистики, оптимальная стратегия может выглядеть так:

  1. Базовый сигнал: паттерн фрактального кодирования (63.64% успешных сделок)
  2. Подтверждение: конвергенция (высокая частота появления и положительный винрейт)
  3. Фильтр: волатильность и свечные паттерны (100% винрейт)

В следующих разделах мы глубже рассмотрим практическое применение этих закономерностей в реальной торговле. А пока могу сказать, что бинарное кодирование действительно позволяет увидеть рынок под новым углом и найти неочевидные закономерности в его поведении.


Попытки дешифровки двоичного кода на бирже при помощи нейросети

В какой-то момент, разбираясь с бинарными паттернами, я задумался – а что если научить компьютер читать этот своеобразный язык рынка? Так родилась идея использовать CatBoost для прогнозирования будущих движений цены.

Прелесть CatBoost в том, что он отлично справляется с категориальными данными. А наши бинарные паттерны, по сути, и есть категории — последовательности нулей и единиц, каждая из которых что-то говорит о состоянии рынка.

Сердцем системы стал класс BinaryPatternPredictor. Главная его задача — преобразовать бинарные паттерны в признаки, которые сможет понять нейросеть. Для этого я использовал технику скользящего окна: берём кусок истории (lookback период) и пытаемся предсказать, будет ли в следующем окне больше единиц или нулей.

Самое интересное началось, когда я работал над методом prepare_features. Представьте: каждый паттерн становится набором признаков. Тут не только сама последовательность нулей и единиц, но и дополнительные метрики — сколько единиц в окне, какой тренд в последних значениях, как это соотносится с другими методами кодирования.

Помню свой первый эксперимент с методом momentum. В коде это выглядит просто:

returns = self.decoder.df['close'].pct_change()
base_binary = (returns.abs() > 0.0001).astype(int)

Но за этими строчками скрывается целая философия — мы превращаем силу движения цены в простую последовательность нулей и единиц.

А вот гибридный метод получился особенно интересным:

direction = (self.decoder.df['close'] > self.decoder.df['close'].shift(1)).astype(int)
volume = (self.decoder.df['tick_volume'] > self.decoder.df['tick_volume'].rolling(self.lookback).mean()).astype(int)
volatility = ((self.decoder.df['high'] - self.decoder.df['low']) > 
              (self.decoder.df['high'] - self.decoder.df['low']).rolling(self.lookback).mean()).astype(int)
base_binary = (direction & volume & volatility)

Здесь мы комбинируем три разных взгляда на рынок: направление движения, объём и волатильность. Получается что-то вроде "трёх китов" технического анализа, только в бинарном виде.

В методе train я намеренно не стал перемешивать данные (shuffle=False). Почему? Потому что в реальной торговле нам важно, как модель работает именно на последовательных данных – ведь рынок не случайно разбрасывает свои паттерны, а формирует их один за другим.

Когда дело дошло до прогнозирования в predict_next_pattern, я добавил оценку уверенности модели:

prediction = self.model.predict_proba(last_window)[0]
return {
    'probability_more_ones': prediction[1],
    'probability_more_zeros': prediction[0],
    'prediction': 'More ones' if prediction[1] > 0.5 else 'More zeros',
    'confidence': max(prediction)
}

Это оказалось крайне полезным — чем выше уверенность модели, тем чаще прогноз оказывался верным.

В основной функции main() я сделал анализ сразу по нескольким методам кодирования. На практике оказалось, что расхождение в предсказаниях разных методов часто даёт больше информации, чем единогласный прогноз. Как в той пословице — одна голова хорошо, а пять методов кодирования лучше!

Особенно меня порадовало, как модель научилась находить неочевидные закономерности. Например, сочетание слабой волатильности с растущим объёмом часто предсказывало преобладание единиц в следующем окне. А ведь это, по сути, классическая ситуация накопления позиций перед сильным движением!


Результаты нейронной сети

Знаете, что меня по-настоящему удивило в результатах этого эксперимента? Каждый метод кодирования показал свой уникальный характер, прямо как разные индикаторы в классическом техническом анализе.

Начнём с моментума — это просто фантастика! Точность 95% и F1-score 0.92 говорят о том, что модель практически безошибочно читает импульсные движения рынка. Особенно интересно, что прогноз показывает 95% вероятность преобладания единиц в следующем паттерне. По сути, это сигнал о продолжении сильного движения.

Объёмный анализ тоже не подкачал. С точностью 83% и сбалансированными метриками (precision и recall тоже около 0.83), он отлично улавливает активность крупных игроков. Когда я увидел прогноз с 94% уверенностью в преобладании единиц, сразу вспомнил классическое правило — "объём подтверждает тренд".

А вот метод схождения/расхождения (convergence) преподнёс сюрприз. При той же точности 83%, он предсказывает преобладание нулей с уверенностью 78%. Это прямо противоречит сигналам моментума и объёма! Такое расхождение часто говорит о приближающемся развороте тренда.

Гибридный метод оказался настоящим открытием. Точность 91% и precision 0.92 показывают, что комбинация разных подходов даёт более стабильные результаты. Интересно, что он тоже предсказывает преобладание нулей (75% вероятность), подтверждая сигнал конвергенции.

Помню, как тестировал эту модель на реальных данных EURUSD. В один из дней все методы показали высокую точность, но разошлись в прогнозах — прямо как сейчас. Через несколько часов случился резкий разворот тренда. Совпадение? Возможно. Но с тех пор я внимательно слежу за такими "разногласиями" между методами.

В целом, эти результаты наводят на интересную мысль: возможно, рынок действительно говорит с нами через бинарные паттерны, просто мы не всегда умеем правильно их читать. И похоже, нейросеть справляется с этим лучше человека.

Кстати, важный момент — все эти метрики получены на часовом таймфрейме. На других временных интервалах картина может быть совершенно другой. Особенно при применении концепции 3D-баров...Но это уже тема для отдельного исследования...


Заключение

В начале этой статьи мы спрашивали себя: можно ли услышать голос рынка через бинарный код? После месяцев экспериментов, тысяч строк кода и бесчисленных часов анализа, могу сказать — да, рынок действительно говорит с нами на своём особом языке. И теперь мы научились его немного понимать.

Знаете, что меня поразило больше всего в этом исследовании? То, как простое преобразование ценовых движений в последовательности нулей и единиц открывает совершенно новый взгляд на технический анализ. Уже не смотришь на графики как раньше — теперь в каждом движении видишь часть большого цифрового послания.

Особенно впечатляют результаты нейросети. Точность 95% на моментум-паттернах, 83% на объёмном анализе, 91% на гибридном подходе — это не просто сухие цифры. Это подтверждение того, что в кажущемся хаосе рыночных движений существует скрытый порядок, который можно обнаружить, если знать, где искать.

Конечно, было бы наивно думать, что мы нашли философский камень трейдинга. Рынок слишком сложен и динамичен, чтобы его можно было полностью описать даже самым изощрённым алгоритмом. Но мы определённо открыли новый инструмент для его понимания.

Для меня этот проект стал чем-то большим, чем просто эксперимент с кодом. Он напомнил, что в трейдинге всегда есть место для инноваций, для свежего взгляда на привычные вещи. И может быть, следующий прорыв в анализе рынка придёт не от сложных математических моделей, а от умения услышать, что рынок пытается сказать нам своим бинарным языком.

А что дальше? Мне кажется, мы только в начале пути. Впереди — эксперименты с другими методами кодирования, исследование взаимосвязей между разными таймфреймами, интеграция с классическими методами анализа. Но главное — нужно продолжать слушать рынок. Потому что он всегда готов поделиться своими секретами с теми, кто умеет слушать, и чье сердце открыто инновациям. А инновации — это основа развития. 

Прикрепленные файлы |
Price_Binary_v_1.py (27.43 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Maxim Dmitrievsky
Maxim Dmitrievsky | 14 янв. 2025 в 17:16
Вроде бы бинаризация и квантование признаков, в МО теме на форуме, ничем не закончились :)
Разрабатываем мультивалютный советник (Часть 21): Подготовка к важному эксперименту и оптимизация кода Разрабатываем мультивалютный советник (Часть 21): Подготовка к важному эксперименту и оптимизация кода
Для дальнейшего продвижения хорошо было бы посмотреть, можем ли мы улучшить результаты, периодически выполняя повторную автоматическую оптимизацию и генерирование нового советника. Камнем преткновения во многих спорах об использовании оптимизации параметров является вопрос о том, насколько долго можно использовать полученные параметры для торговли в будущем периоде с сохранением основных показателей прибыльности и просадки на заданных уровнях. И можно ли вообще это делать?
Построение модели для ограничения диапазона сигналов по тренду (Часть 6): Интеграция "всё в одном" Построение модели для ограничения диапазона сигналов по тренду (Часть 6): Интеграция "всё в одном"
Одной из основных проблем является управление несколькими окнами графиков одной пары, на которых запущена одна и та же программа с разными функциями. Давайте обсудим, как объединить несколько интеграций в одну основную программу. Кроме того, мы поделимся идеями по настройке программы для вывода в журнал и рассмотрим успешную трансляцию сигнала в интерфейсе графика.
Возможности Мастера MQL5, которые вам нужно знать (Часть 28): Сети GAN в контексте темпа обучения Возможности Мастера MQL5, которые вам нужно знать (Часть 28): Сети GAN в контексте темпа обучения
Темп обучения — это размер шага к цели обучения во многих алгоритмах машинного обучения. В статье мы изучим, какое влияние многочисленные форматы могут оказать на производительность генеративно-состязательной сети (Generative Adversarial Network, GAN) — разновидности нейронной сети, которую мы рассмотрели в одной из предыдущих статей.
Разработка советника на основе стратегии прорыва диапазона консолидации на MQL5 Разработка советника на основе стратегии прорыва диапазона консолидации на MQL5
В статье описываются шаги по созданию торгового советника, который извлекает выгоду из ценовых прорывов после периодов консолидации. Определяя диапазоны консолидации и устанавливая уровни прорыва, трейдеры могут автоматизировать свои торговые решения на основе этой стратегии. Советник призван обеспечить четкие точки входа и выхода, избегая ложных пробоев.