Русский Português
preview
Analizamos el código binario de los precios en bolsa (Parte I): Una nueva visión del análisis técnico

Analizamos el código binario de los precios en bolsa (Parte I): Una nueva visión del análisis técnico

MetaTrader 5Indicadores |
81 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Introducción

¿Recuerda que en la película Matrix, Neo veía el mundo como un código binario verde? ¿Y si intentamos mirar los gráficos de acciones de la misma forma? En mi negociación diaria, a menudo me preguntaba: ¿podríamos "oír" lo que el mercado intenta decirnos si imaginamos sus movimientos como una especie de código o lenguaje?

La idea surgió inesperadamente mientras analizaba un gráfico de Bitcoin. Mientras observaba las bruscas subidas y bajadas de la cotización, me di cuenta de que ciertas combinaciones de movimientos se repetían como las letras de una palabra. Y esto me dio una idea: ¿y si codificamos estos movimientos en formato binario e intentamos "leer" el mensaje resultante?

¿Le parece una locura? Tal vez, pero recuerde: ¿no se basa el análisis técnico en la búsqueda de patrones? ¿No son las propias velas japonesas el mismo sistema para codificar el movimiento de los precios? Al fin y al cabo, el mercado es un enorme flujo de información codificada digitalmente.

En este artículo, quiero compartir mi experimento para convertir los movimientos de precios en secuencias significativas de código binario. Asimismo, veremos cómo podemos convertir diversos aspectos del comportamiento del mercado -desde simples movimientos de precios hasta patrones complejos- a formato binario, e intentaremos encontrar patrones en este código que nos ayuden a comprender mejor el lenguaje del mercado.

Sí, este enfoque resulta poco convencional, pero, ¿acaso no trata de eso el trading: de encontrar nuevas formas de ver lo que otros pasan por alto?

Entremos juntos en la madriguera del conejo y veamos adónde nos lleva...


El concepto de representación de los movimientos de precios como un código binario

Mi enfoque se basa en una idea simple pero intrigante: cualquier movimiento de precios puede visualizarse como una secuencia de ceros y unos. Piense: al mirar un gráfico, ¿qué vemos? Subidas y bajadas, movimientos fuertes y débiles, volúmenes por encima o por debajo de la media. De hecho, ¡ya estamos convirtiendo inconscientemente esta información a un formato binario en nuestras cabezas!

Pongamos un ejemplo sencillo. Digamos que vemos tres velas seguidas: una ascendente, otra descendente y de nuevo otra ascendente. Desde el punto de vista más rudimentario, esto podría codificarse como "101". Pero, ¿y si añadimos más parámetros? Por ejemplo, ¿y si consideramos la fuerza del movimiento, el volumen comercial o la posición en relación con una media móvil?

En mis experimentos, he descubierto que incluso una codificación básica de ascenso y descenso puede producir resultados interesantes. Sin embargo, la verdadera magia comienza cuando empezamos a considerar múltiples marcos temporales. Imagínese: la propia secuencia "101" en un gráfico de horas puede parecer completamente diferente en un marco temporal diario o semanal. Así se crea una especie de matriz de datos en la que cada movimiento de los precios se describe mediante una secuencia binaria única.

Podemos obtener resultados especialmente interesantes al analizar las criptomonedas. La volatilidad de Bitcoin crea patrones claros y muy discernibles en la representación binaria. A veces parece que estas secuencias son como el código Morse: la misma danza rítmica de puntos y rayas, solo que en lugar de ellos hay una subida y bajada de precios.

Obviamente, los escépticos pueden decir que no es más que una versión complicada del análisis técnico clásico. Pero, ¿no es en eso en lo que consiste el progreso: en mirar las cosas conocidas desde un nuevo punto de vista? Después de todo, las velas japonesas parecían exóticas, pero hoy en día ningún tráder puede prescindir de ellas.

En los siguientes apartados, examinaremos más detenidamente las distintas formas de codificar los movimientos de los precios y cómo esta información puede ayudarnos en la negociación real. Mientras tanto, le sugiero que eche por sí mismo un vistazo a su gráfico favorito e intente ver la secuencia de ceros y unos que hay en él. Estoy seguro de que cambiará su forma de pensar sobre el análisis técnico...


Principios básicos de la conversión

¿Recuerda los primeros juegos de ordenador? ¿Aquellos en los que los gráficos estaban hechos de píxeles y el sonido era de 8 bits? Así pues, nuestro enfoque del análisis de los mercados será algo parecido a la creación de un juego retro: tomaremos el complejo mundo de las cotizaciones bursátiles y lo convertiremos en un sencillo código binario.

Permítame contarle cómo nació la idea. Una vez, mientras desarrollaba otro indicador, accidentalmente tracé solo ceros y unos en lugar de las líneas habituales. ¿Y sabe qué? En esta sucesión de números comenzaron a surgir patrones interesantes. Era como si el mercado intentara decir algo en su lenguaje binario.

Para los que les guste profundizar en el código, aquí tenemos cómo funciona en 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

Según la dirección del movimiento (crecimiento/descenso)

¿Sabe qué es lo curioso del trading? El hecho de que nos pasemos horas mirando los gráficos intentando adivinar el próximo movimiento de los precios, y esencialmente todo se reduzca a la elección más simple: ¿hacia arriba o hacia abajo? ¿Uno o cero?

Mientras trabajaba en este proyecto, pasé horas experimentando con Bitcoin. ¿Por qué Bitcoin? Porque las criptomonedas son puro código. No hay informes trimestrales, dividendos ni otras cosas fundamentales. Pura psicología de multitudes codificada en números.

Este será el aspecto de un análisis básico en 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())

Ahora viene lo divertido: cómo funciona en el trading real. Imagine que está mirando un gráfico y, en lugar de las velas habituales, ve la secuencia "1011101". ¿Le parece que no tiene sentido? ¡Pues no! Esta secuencia puede indicarle más de una docena de indicadores técnicos.

Es algo curioso, pero cuando mostré este código a mis colegas tráders, lo primero que hicieron fue insinuarme que estoy un poco loco. "¿Para qué complicarse así?", decían. Y entonces uno de ellos notó algo interesante: algunas secuencias binarias aparecen antes de los movimientos fuertes más a menudo de lo que deberían según la teoría de la probabilidad.

Obviamente, eso no significa que hayamos inventado una máquina de imprimir dinero. Pero estoy de acuerdo con que hay algo fascinante en la idea de que el mercado se comunique con nosotros a través de un código binario. Como en Matrix, ¿recuerda?

En relación con la media móvil

Trabajando con MetaTrader 5 a través de Python, descubrí una forma totalmente nueva de ver las medias móviles. Sabe, es curioso: simplemente quería conectar un terminal a Python, pero acabé encontrando algo más que eso.

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)

La parte más interesante comenzó cuando noté un patrón extraño: en Bitcoin, las secuencias de "101" cerca de la MA con frecuencia precedían a movimientos fuertes, especialmente en periodos de alta volatilidad. Es como si el mercado "ensayase" antes de saltar.

Según la fuerza del movimiento (impulso)

"El mercado o duerme o corre" es una frase que repito a menudo a mis amigos tráders. Ahora imagine: podemos codificar estos estados en simples ceros y unos. Es precioso, ¿verdad?

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

Recuerdo mi primer experimento con este código: lo ejecuté en el gráfico de horas de ETH/USD, y ¿adivine qué? Resultó que después de la secuencia "000" (un periodo de baja volatilidad), la probabilidad de un movimiento fuerte aumenta un 23%. No es que sea una regla de oro del trading, pero estará de acuerdo en da que pensar.

Por cierto, cuando añadí volúmenes al análisis, el panorama se tornó aún más interesante. Pero hablaremos más sobre ello en las siguientes secciones. Mientras tanto, le sugiero que juegue un poco por sí mismo con estos patrones. Quizá encuentre algo que se me haya pasado.

Según el volumen comercial


Y hay una anécdota curiosa sobre los volúmenes. Al principio no les presté mucha atención: solo eran un parámetro más para la codificación. Pero cuando comencé a analizar patrones en BTC/USD, me di cuenta de que los volúmenes a veces dicen más que el propio precio.

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)

¿Sabe cuál fue la parte más interesante? La secuencia de volumen "111" (tres barras de volumen alto seguidas) suele surgir antes de movimientos importantes. Es como si las "ballenas" estuvieran calentando antes de una carrera. Esto resulta particularmente claro en el gráfico de 4 horas.


Los marcos temporales y su impacto en la codificación

Recuerdo que una noche me senté a estudiar el código y traté de averiguar por qué las mismas pautas funcionan de forma diferente en distintos marcos temporales. Entonces se me ocurrió: ¿y si miro el mismo momento en varios fotogramas a la vez?

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)

Y los resultados me asombraron. Resulta que algunas combinaciones solo surgen a determinadas horas del día. Por ejemplo, en el par EUR/USD, la secuencia "101" en el gráfico de 15 minutos durante la sesión asiática tiene un significado muy distinto al de la sesión europea.

Incluso se dio un caso así: yo estaba negociando con este sistema en ETHUSDT, y me di cuenta de una cosa extraña; antes de cada movimiento serio, la secuencia "110" aparecía en el gráfico horario, mientras que "101" aparecía en el gráfico de 4 horas al mismo tiempo. ¿Coincidencia? Tal vez, pero después del quinto incidente de este tipo, ya no estaba tan seguro.

Por cierto, resulta curioso ver como algunos setups "perfectos" en los marcos temporales inferiores son destrozados por la cruda realidad de los marcos mayores. Ahora, siempre pruebo los patrones binarios en al menos tres marcos temporales antes de entrar en el mercado. ¿Desconfianza? Tal vez, pero en el comercio, es mejor pasarse que quedarse corto.


Buscamos e investigamos patrones binarios crudos e ingenuos de entropía cero

Permítame contarle cómo se me ocurrió buscar patrones en las pautas del mercado binario. Todo empezó con una simple observación: cualquier movimiento de precios puede visualizarse como una cadena de ceros y unos. Sin embargo, cuanto más profundizaba, más interesante resultaba.

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()

Imagine que no mira el gráfico como un tráder, sino como un descifrador de códigos. Cada barra supone una letra del extraño mensaje del mercado. Solo tiene que averiguar qué alfabeto utiliza.

He creado 15 métodos de codificación distintos, cada uno con su propia lógica:

  • direction_encoding — es el método más simple. ¿El precio está por encima del anterior? Uno. ¿Por debajo? Cero. Como el código Morse para principiantes.
  • ma_encoding — resulta más interesante. Observe el cruce del precio con la media móvil. Al hacer pruebas con EURUSD, me di cuenta de algo curioso: algunas combinaciones aparecían con más frecuencia durante la sesión europea.
  • momentum_encoding — aquí nos fijamos en la fuerza del movimiento. ¿Sabe lo que es asombroso? Tres ceros seguidos (un periodo de impulso débil) suelen ir seguidos de un repentino estallido de actividad.
  • volume_encoding — analiza los volúmenes en forma binaria. Recuerdo que una vez noté un patrón extraño en Bitcoin - "10101", por su volumen casi siempre precedía a un movimiento fuerte.
  • fractal_encoding — patrones fractales en forma binaria. Es como buscar los fractales de Bill Williams, solo que en un mundo de ceros y unos.
  • volatility_encoding — codifica la volatilidad. Curiosamente, algunos patrones de volatilidad eran tan estables que su entropía tendía hacia cero.
  • candle_pattern_encoding — patrones clásicos de velas en representación binaria. En este sistema, Doji tiene aspecto de "101".
  • entropy_encoding — aquí medimos la entropía informativa de los movimientos de los precios. Cuando la entropía cae cerca de cero, esperaremos sorpresas.
  • convergence_encoding — convergencia-divergencia de medias móviles en forma binaria. Un clásico del análisis técnico en un nuevo formato.
  • price_level_encoding — codifica la relación entre el precio y los niveles clave. Aparecen patrones especialmente interesantes cerca de los números redondos.
  • rsi_momentum_encoding — RSI en forma binaria. Resulta que algunas combinaciones de "101" en RSI poseen una entropía casi nula.
  • cluster_encoding — análisis clústeres de precios en representación binaria. Es como buscar zonas de agrupación de órdenes, solo que en código.
  • extremum_encoding — máximos y mínimos locales como ceros y unos. A veces aparecen secuencias muy distintivas antes de retrocesos importantes.
  • trend_encoding — dirección de la tendencia en forma binaria. El método funciona bien en marcos temporales mayores.
  • hybrid_encoding — es una combinación de todos los enfoques. Como un superindicador, solo que en formato binario.

La parte más interesante comenzó cuando agregué analyse_pattern. Este método solo busca secuencias repetidas, sino que también mide su entropía, las convierte a base58 para compactarlas e identifica patrones con entropía cero.

¿Sabe lo que más me sorprendió? Algunos patrones aparecen con tal regularidad que ya no puede ser una coincidencia. Por ejemplo, la secuencia "11001" en hybrid_encoding suele surgir antes de movimientos fuertes en EURUSD.

¿Y recuerda la teoría del caos y el efecto mariposa? Así, en este caos de movimientos de precios, a veces aparecen islas de orden -patrones con entropía casi nula-. Es como si el mercado estuviera desvelando sus planes por un segundo....

En los siguientes apartados hablaremos de cómo utilizar estos patrones en el trading real. Mientras tanto, le sugiero que experimente con el código por su cuenta. Quizá encuentre sus propios patrones únicos en el código binario del mercado.


Analizamos patrones binarios ingenuos en los precios

Mientras analizaba los resultados de los experimentos con la codificación binaria del mercado, encontré algunos patrones inesperados. Veamos los más interesantes.

En primer lugar, observe el patrón de impulso "1010111111". Sorprendentemente, con una entropía de 0,72, este patrón apareció 43 veces en nuestro marco temporal. El rendimiento medio fue del -0,01% y el porcentaje de victorias del 44,19%. Parece que el resultado no es impresionante, pero el beneficio máximo alcanzó el +0,34% con una reducción máxima del -0,42%. Y esto demuestra el potencial del patrón con una gestión adecuada del riesgo.

En términos de volúmenes, vemos una secuencia característica de "0111001111" con una proporción de 70/30 de unos respecto a ceros. Este patrón apareció 13 veces durante el mes, con un porcentaje de victorias del 46,15%. Curiosamente, a pesar de la rentabilidad media negativa (-0,06%), la rentabilidad máxima alcanzó el +0,28%.

La verdadera revelación fueron los patrones de convergencia/divergencia. ¡La secuencia de entropía cero "0000000000" ha aparecido la friolera de 1.652 veces! Al mismo tiempo, el porcentaje de victorias fue del 53,27%, y el beneficio máximo alcanzó el +0,68%. Dado el número de señales, hablamos de un resultado estadísticamente significativo.

Entre todos los métodos, las mejores estadísticas las mostraron:

  • Patrón fractal "0110000100": porcentaje de victorias del 63,64% en 11 transacciones, rendimiento medio del +0,14%.
  • Patrón de volatilidad "0010001011": 100% de victorias en 2 transacciones, rendimiento medio del +0,21%.
  • Patrón de velas "1010111110": 100% de victorias en 3 transacciones, rendimiento medio del +0,04%.

En cuanto a la codificación híbrida ("0010000011"), mostró 12 señales con un porcentaje de victorias del 25% y un rendimiento medio del -0,04%. Sin embargo, el beneficio máximo alcanzó el +0,33%, lo cual indica el potencial del método con el filtrado correcto de las señales.

El patrón RSI Momentum ("0010000001") mostró resultados especialmente interesantes. Con solo tres apariciones en un mes, todas ellas fueron poco rentables, lo que puede indicar una intensa señal para abrir posiciones en sentido contrario.

Según las estadísticas obtenidas, la estrategia óptima podría ser la que sigue:

  1. Señal básica: patrón de codificación fractal (63,64% de las transacciones con éxito)
  2. Confirmación: convergencia (alta frecuencia de aparición y porcentaje de victorias positivo)
  3. Filtro: volatilidad y patrones de velas (100% de porcentaje de victorias)

En las siguientes secciones, profundizaremos en la aplicación práctica de estos patrones en el comercio real. Mientras tanto, puedo decir que la codificación binaria realmente permite mirar el mercado desde un nuevo punto de vista y encontrar patrones no evidentes en su comportamiento.


Intentos de descifrar el código binario de la bolsa usando redes neuronales

En algún momento, mientras trabajaba con patrones binarios, pensé: ¿y si pudiera enseñar a un ordenador a leer este peculiar lenguaje del mercado? Así nació la idea de utilizar CatBoost para predecir los futuros movimientos de los precios.

Lo bueno de CatBoost es que hace un gran trabajo con datos categóricos. Y nuestros patrones binarios son esencialmente categorías: secuencias de ceros y unos, cada una de las cuales nos cuenta algo sobre el estado del mercado.

El corazón del sistema es la clase BinaryPatternPredictor. Su principal tarea consiste en convertir patrones binarios en características que una red neuronal pueda entender. Para ello, utilicé la técnica de la ventana deslizante: tomamos un trozo de la historia (periodo retrospectivo) e intentamos predecir si la siguiente ventana tendrá más unos o más ceros.

La parte más interesante comenzó cuando estaba trabajando con el método prepare_features. Imagínese: cada patrón se convierte en un conjunto de características. No se trata solo de la secuencia de ceros y unos en sí, sino de parámetros adicionales: cuántas unidades hay en la ventana, cuál es la tendencia de los valores recientes, cómo se relaciona esto con otros métodos de codificación.

Recuerdo mi primer experimento con el método del impulso. El código se diría sencillo:

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

Pero estas líneas ocultan toda una filosofía en la que convertimos la fuerza del movimiento de los precios en una simple secuencia de ceros y unos.

El método híbrido, por su parte, resultó muy interesante:

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)

Aquí combinamos tres puntos de vista diferentes del mercado: la dirección del movimiento, el volumen y la volatilidad. Sería algo así como las "tres ballenas" del análisis técnico, solo que en forma binaria.

En el método de entrenamiento, no barajamos intencionadamente los datos (shuffle=False). ¿Y por qué? Porque en el trading real resulta importante ver cómo funciona el patrón en datos secuenciales, ya que el mercado no dispersa sus patrones al azar, sino que los forma uno tras otro.

A la hora de predecir en predict_next_pattern, añadí una puntuación de confianza del modelo:

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)
}

Esto resultó extremadamente útil: cuanto mayor era la confianza del modelo, más a menudo la predicción resultaba ser correcta.

En la función main(), realicé un análisis de varios métodos de codificación a la vez. En la práctica, comprobé que una divergencia en las predicciones de distintos métodos suele aportar más información que una predicción unánime. Como dice el refrán: una cabeza es buena, pero cinco métodos de codificación son mejores.

Me gustó especialmente cómo el modelo aprendió a hallar patrones no evidentes. Por ejemplo, la combinación de una volatilidad débil y un volumen creciente predecía a menudo el predominio de los unos en la siguiente ventana. De hecho, ¡se trata de una situación clásica de acumulación de posiciones antes de un movimiento intenso!


Resultados de la red neuronal

¿Sabe lo que realmente me sorprendió de los resultados de este experimento? Cada método de codificación mostraba su propio carácter único, al igual que los distintos indicadores en el análisis técnico clásico.

Empecemos por el impulso: ¡es simplemente fantástico! La precisión del 95% y la puntuación F1 de 0,92 indican que el modelo lee los movimientos impulsivos del mercado prácticamente sin errores. Lo que resulta especialmente interesante es que la previsión muestra una probabilidad del 95% de que las unidades predominen en el próximo patrón. En esencia, supone una señal de un fuerte movimiento continuado.

El análisis volumétrico tampoco flaqueó. Con una precisión del 83% y unas métricas equilibradas (precisión y recall también sobre 0,83), realiza un gran trabajo a la hora de captar la actividad de los grandes actores. Cuando vi una previsión con un 94% de confianza de que predominarían las unidades, recordé inmediatamente la regla clásica: "el volumen confirma la tendencia".

Pero el método de convergencia resultó sorprendente. Con la misma precisión del 83%, predice una predominancia de ceros con un 78% de confianza. Esto contradice directamente las señales de impulso y volumen. Una divergencia de esta clase suele indicar un inminente cambio de tendencia.

El método híbrido resultó todo un descubrimiento. El 91% de exactitud y la precisión de 0,92 muestran que la combinación de distintos enfoques ofrece resultados más estables. Curiosamente, también predice una predominancia de ceros (75% de probabilidad), lo que confirma la señal de convergencia.

Recuerdo probar este modelo con datos reales de EURUSD. Un buen día, todos los métodos mostraron una gran precisión, pero divergieron en sus predicciones, igual que ahora. Unas horas más tarde se produjo un brusco cambio de tendencia. ¿Coincidencia? Tal vez, pero desde entonces, he estado muy atento a estos "desacuerdos" entre métodos.

En general, estos resultados sugieren una idea interesante: quizá el mercado nos hable a través de patrones binarios, solo que no siempre sabemos leerlos correctamente. Y la red neuronal parece hacerlo mejor que un humano.

Por cierto, un punto importante a considerar: todas estas métricas se obtienen en el marco de horas. En otros marcos temporales, el panorama puede ser muy distinto. Especialmente al aplicar el concepto de barras 3D... Pero ese es un tema para otro estudio...


Conclusión

Al principio de este artículo nos preguntábamos: ¿puede oírse la voz del mercado a través de código binario? Tras meses de experimentos, miles de líneas de código e incontables horas de análisis, puedo afirmar que sí, que el mercado realmente nos habla en su propio lenguaje especial. Y ahora hemos aprendido a comprenderlo un poco.

¿Sabe qué es lo que más me ha llamado la atención de este estudio? Cómo la simple transformación de los movimientos de los precios en secuencias de ceros y unos descubre una perspectiva totalmente nueva del análisis técnico. Ya no miro los gráficos como antes, ahora veo cada movimiento como parte de un mensaje digital más amplio.

Los resultados de la red neuronal resultan especialmente impresionantes. Una precisión del 95% en los patrones de impulso, un 83% en el análisis volumétrico y un 91% en el enfoque híbrido: esas no son simplemente cifras a secas. Son la confirmación de que, en el aparente caos de los movimientos del mercado, existe un orden oculto que puede descubrirse si sabemos dónde buscar.

Obviamente, sería ingenuo pensar que hemos encontrado la piedra filosofal del comercio. El mercado es demasiado complejo y dinámico para ser descrito al completo, ni siquiera por el algoritmo más sofisticado. Pero sin duda hemos descubierto una nueva herramienta para entenderlo.

Para mí, este proyecto se ha convertido en algo más que un experimento de código. Me ha recordado que en el comercio siempre hay lugar para la innovación, para una mirada nueva a las cosas conocidas. Y puede que el próximo gran avance en el análisis de los mercados no provenga de complejos modelos matemáticos, sino de la capacidad de escuchar lo que el mercado trata de decirnos con su lenguaje binario.

¿Y ahora qué? Creo que estamos al principio de un viaje. Por delante quedan los experimentos con otros métodos de codificación, la investigación de las interrelaciones entre distintos marcos temporales y la integración con los métodos de análisis clásicos. Pero lo más importante es seguir prestando oído al mercado. Porque siempre está dispuesto a compartir sus secretos con quienes sepan escuchar y tengan el corazón abierto a las innovaciones. Y estas son la base del desarrollo. 

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/16741

Archivos adjuntos |
Price_Binary_v_1.py (27.43 KB)
Maxim Dmitrievsky
Maxim Dmitrievsky | 14 ene 2025 en 17:16
Parece que la binarización y cuantificación de rasgos, en el hilo de MO en el foro, no acabó en nada :)
Del básico al intermedio: Struct (III) Del básico al intermedio: Struct (III)
En este artículo, veremos qué es un código estructurado. Muchas personas confunden el código estructurado con el código organizado. Sin embargo, existe una diferencia entre ambos conceptos. Esto se explicará en este artículo. A pesar de la aparente complejidad que se notará en el primer contacto con este tipo de codificación, he intentado abordar el tema de la mejor manera posible. Pero este artículo es solo el primer paso hacia algo más grande.
Del básico al intermedio: Indicador (IV) Del básico al intermedio: Indicador (IV)
En este artículo, veremos lo fácil que es crear e implementar una metodología operativa para teñir velas. Este es un concepto muy apreciado por los operadores. Es necesario tener cuidado al implementar este tipo de cosas para que las barras o velas mantengan su apariencia original y no se dificulte la lectura vela por vela.
De Python a MQL5: Un viaje hacia los sistemas de trading inspirados en la cuántica De Python a MQL5: Un viaje hacia los sistemas de trading inspirados en la cuántica
El artículo analiza el desarrollo de un sistema de negociación inspirado en la cuántica, pasando de un prototipo en Python a una implementación en MQL5 para la negociación en el mundo real. El sistema utiliza principios de computación cuántica, como la superposición y el entrelazamiento, para analizar los estados del mercado, aunque funciona en ordenadores clásicos utilizando simuladores cuánticos. Las características principales incluyen un sistema de tres qubits para analizar ocho estados del mercado simultáneamente, períodos de revisión de 24 horas y siete indicadores técnicos para el análisis del mercado. Aunque los índices de precisión puedan parecer modestos, proporcionan una ventaja significativa cuando se combinan con estrategias adecuadas de gestión de riesgos.
Redes neuronales en el trading: Agente multimodal con herramientas complementarias (FinAgent) Redes neuronales en el trading: Agente multimodal con herramientas complementarias (FinAgent)
Hoy querríamos presentarle el FinAgent, un framework de agente multimodal para el comercio financiero diseñado para analizar distintos tipos de datos que reflejan la dinámica del mercado y los patrones comerciales históricos.