English Русский Português
preview
Visión por computadora para el trading (Parte 1): Creamos una funcionalidad básica sencilla

Visión por computadora para el trading (Parte 1): Creamos una funcionalidad básica sencilla

MetaTrader 5Sistemas comerciales |
86 0
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Introducción

En el silencioso parpadeo de los monitores, donde los gráficos de velas trazan las sinuosas trayectorias de los flujos financieros, está por nacer una nueva era del trading. ¿Se ha preguntado alguna vez qué siente una red neuronal al observar el mercado EURUSD? ¿Cómo percibe cada pico de volatilidad, cada cambio de tendencia, cada escurridiza formación de patrones?

Imagínese: una computadora que no se limita a aplicar sin pensar reglas preprogramadas, sino que realmente ve el mercado y capta los sutiles matices de los movimientos de los precios que son inaccesibles al ojo humano. Inteligencia artificial que mira el gráfico del EURUSD como un capitán experimentado mira el horizonte del mar, anticipando la llegada de una tormenta mucho antes de los primeros signos de mal tiempo.

Hoy les propongo un viaje a la vanguardia de la tecnología financiera, donde la visión por computadora se une al análisis bursátil. Así, crearemos un sistema que no se limite a analizar el mercado, sino que lo entienda visualmente, reconociendo patrones complejos de movimiento de precios con la misma naturalidad con la que se reconoce la cara de un amigo entre la multitud.

En un mundo en el que los milisegundos deciden el destino de millones de personas, nuestro modelo basado en redes neuronales convolucionales abre la puerta a una nueva dimensión del análisis técnico. No usa indicadores estándar, sino que aprende por sí mismo a encontrar e interpretar señales directamente a partir de los datos OHLC sin procesar. Y lo sorprendente es que podemos mirar dentro de su "mente" y ver exactamente qué fragmentos del gráfico le llaman la atención antes de tomar su decisión.


Historia y aplicaciones de los modelos de visión por computadora

La visión por computadora nació en los años 60 en el MIT, cuando los investigadores intentaron por primera vez enseñar a las máquinas a interpretar la información visual. Al principio, el desarrollo fue lento: los primeros sistemas solo podían reconocer las formas y contornos más simples.

El verdadero avance se produjo en 2012 con la llegada de las redes neuronales convolucionales profundas (CNN), que revolucionaron el sector. La arquitectura AlexNet demostró en la competición ImageNet una precisión sin precedentes en el reconocimiento de imágenes, iniciando la era del aprendizaje profundo.

Hoy en día, la visión por computadora ha transformado multitud de industrias. En medicina, los algoritmos analizan radiografías y resonancias magnéticas con una precisión comparable a la de radiólogos experimentados. En la industria del automóvil, los sistemas de asistencia al conductor y los pilotos automáticos usan CV para reconocer objetos de la carretera. Los smartphones utilizan esta tecnología para la mejora de fotos y el desbloqueo mediante reconocimiento facial, usado por los sistemas de seguridad para controlar el acceso.

La aplicación de la visión por computadora al análisis financiero es un campo nuevo y prometedor. Los algoritmos de CV son capaces de reconocer patrones y formaciones complejas en los gráficos que no están al alcance de los indicadores técnicos tradicionales, lo que abre nuevos horizontes al trading algorítmico.


Base tecnológica: conexión con los datos del mercado

Nuestro viaje comienza estableciendo una conexión en vivo con el mercado. Como terminaciones nerviosas que buscan el pulso de los flujos financieros, nuestro código se conecta al terminal MetaTrader 5, una herramienta clásica de los tráders que ahora se ha transformado en un portal para la inteligencia artificial:

import MetaTrader5 as mt5
import matplotlib
matplotlib.use('Agg')  # Использование бэкенда Agg для работы без GUI
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Dense, Conv1D, MaxPooling1D, Flatten, Dropout, BatchNormalization, Input

# Подключение к терминалу MetaTrader5
def connect_to_mt5():
    if not mt5.initialize():
        print("Error initializing MetaTrader5")
        mt5.shutdown()
        return False
    return True

Las primeras líneas de código suponen el apretón de manos entre el mundo de los algoritmos y el de las finanzas. Parece una simple llamada a una función, pero detrás se esconde la creación de un canal por el que fluirán gigabytes de datos de mercado, cotizaciones formadas en la intersección de las decisiones de millones de tráders de todo el mundo.

Nuestro sistema usará el backend de Agg para la visualización sin interfaz gráfica, un detalle pequeño pero importante que le permite funcionar en segundo plano en servidores remotos sin interrumpir ni un segundo su vigilancia del mercado.


Inmersión en la historia: recogida y preparación de datos

Cualquier inteligencia artificial parte de los datos. Para nuestro sistema, hablamos de miles de velas horarias de EURUSD, testigos silenciosos de las batallas de precios pasadas, que contienen patrones que tratamos de descubrir:

def get_historical_data(symbol="EURUSD", timeframe=mt5.TIMEFRAME_H1, num_bars=1000):
    now = datetime.now()
    from_date = now - timedelta(days=num_bars/24)  # Approximate for hourly bars
    
    rates = mt5.copy_rates_range(symbol, timeframe, from_date, now)
    if rates is None or len(rates) == 0:
        print("Error loading historical quotes")
        return None
    
    # Convert to pandas DataFrame
    rates_frame = pd.DataFrame(rates)
    rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')
    rates_frame.set_index('time', inplace=True)
    
    return rates_frame

En esta función comienza la magia: los bytes en bruto se convierten en un flujo ordenado de datos. Cada vela, cada fluctuación de precios se convierte en un punto de un espacio multidimensional que nuestra red neuronal debe explorar. Nosotros vamos a cargar 2.000 barras de la historia, suficientes para que el modelo vea una cierta variedad de condiciones de mercado, desde tendencias tranquilas hasta periodos caóticos de alta volatilidad.

Pero la visión por computadora no solo necesita números, sino también imágenes. El siguiente paso consiste en transformar las series temporales en "imágenes" que nuestra red convolucional pueda analizar:

def create_images(data, window_size=48, prediction_window=24):
    images = []
    targets = []
    
    # Using OHLC data to create images
    for i in range(len(data) - window_size - prediction_window):
        window_data = data.iloc[i:i+window_size]
        target_data = data.iloc[i+window_size:i+window_size+prediction_window]
        
        # Normalize data in the window
        scaler = MinMaxScaler(feature_range=(0, 1))
        window_scaled = scaler.fit_transform(window_data[['open', 'high', 'low', 'close']])
        
        # Predict price direction (up/down) for the forecast period
        price_direction = 1 if target_data['close'].iloc[-1] > window_data['close'].iloc[-1] else 0
        
        images.append(window_scaled)
        targets.append(price_direction)
    
    return np.array(images), np.array(targets)

Esta es la alquimia de datos en estado puro. Así, tomamos una ventana de 48 barras de precios consecutivas, normalizamos sus valores entre 0 y 1 y las convertimos en una imagen multicanal en la que cada canal se corresponde con uno de los componentes OHLC. Para cada uno de estos "fotogramas" de la historia del mercado definimos un valor objetivo: la dirección del movimiento del precio en 24 barras en el futuro.

Una ventana deslizante se desplaza secuencialmente por todo el conjunto de datos, creando miles de ejemplos para el entrenamiento. Es como si un tráder experimentado hojeara los gráficos históricos, se empapara de las pautas del mercado y aprendiera a reconocer las situaciones que preceden a movimientos significativos.


Construyendo un "ojo" neuronal: la arquitectura del modelo

Vamos a pasar ahora a construir la red neuronal propiamente dicha: un ojo artificial que se adentrará en el caos de los datos del mercado en busca de un orden oculto. Nuestra arquitectura se inspira en los recientes avances de la visión por computadora, pero adaptada a la especificidad de las series temporales financieras:

def train_cv_model(images, targets):
    # Split data into training and validation sets
    X_train, X_val, y_train, y_val = train_test_split(images, targets, test_size=0.2, shuffle=True, random_state=42)
    
    # Create model with improved architecture
    inputs = Input(shape=X_train.shape[1:])
    
    # First convolutional block
    x = Conv1D(filters=64, kernel_size=3, padding='same', activation='relu')(inputs)
    x = BatchNormalization()(x)
    x = Conv1D(filters=64, kernel_size=3, padding='same', activation='relu')(x)
    x = BatchNormalization()(x)
    x = MaxPooling1D(pool_size=2)(x)
    x = Dropout(0.2)(x)
    
    # Second convolutional block
    x = Conv1D(filters=128, kernel_size=3, padding='same', activation='relu')(x)
    x = BatchNormalization()(x)
    x = Conv1D(filters=128, kernel_size=3, padding='same', activation='relu')(x)
    x = BatchNormalization()(x)
    x = MaxPooling1D(pool_size=2)(x)
    feature_maps = x  # Store feature maps for visualization
    x = Dropout(0.2)(x)
    
    # Output block
    x = Flatten()(x)
    x = Dense(128, activation='relu')(x)
    x = BatchNormalization()(x)
    x = Dropout(0.3)(x)
    x = Dense(64, activation='relu')(x)
    x = BatchNormalization()(x)
    x = Dropout(0.3)(x)
    outputs = Dense(1, activation='sigmoid')(x)
    
    # Create the full model
    model = Model(inputs=inputs, outputs=outputs)
    
    # Create a feature extraction model for visualization
    feature_model = Model(inputs=model.input, outputs=feature_maps)

En este caso, vamos a usar la API funcional de Keras en lugar de un modelo secuencial, esto nos da la flexibilidad de crear un modelo adicional que extrae vistas intermedias para la visualización. Este es un elemento clave de nuestro planteamiento: no solo estamos construyendo un sistema de predicción, sino también una herramienta para mirar dentro de la "conciencia" de la inteligencia artificial.

La arquitectura consta de dos bloques convolucionales, cada uno de los cuales contiene dos capas convolucionales con normalización por lotes. El primer bloque funciona con 64 filtros, y el segundo con 128, revelando gradualmente patrones cada vez más complejos. Tras la extracción de características, los datos pasan por varias capas completamente conectadas que integran la información obtenida para la predicción final.

Entrenar un modelo no consiste únicamente en minimizar la función de pérdida, sino que es un auténtico viaje a través del paisaje de parámetros en el que cada iteración nos acerca más al objetivo:

# Early stopping to prevent overfitting
early_stopping = EarlyStopping(
    monitor='val_loss',
    patience=10,
    restore_best_weights=True,
    verbose=1
)

# Train model
history = model.fit(
    X_train, y_train,
    epochs=50,
    batch_size=32,
    validation_data=(X_val, y_val),
    callbacks=[early_stopping],
    verbose=1
)

Usamos Early Stopping para evitar el sobreentrenamiento: la red neuronal dejará de entrenarse cuando deje de mejorar en el conjunto de validación. Esto nos permite encontrar la media de oro entre el infraentrenamiento y el sobreentrenamiento, el punto de máxima generalizabilidad.


La mente de la inteligencia artificial

La singularidad de nuestro enfoque reside en la capacidad de visualizar exactamente cómo el modelo "ve" el mercado. Hemos desarrollado dos funciones especiales que permiten escrutar los misterios de la computación neuronal:

def visualize_model_perception(feature_model, last_window_scaled, window_size=48):
    # Get feature maps for the last window
    feature_maps = feature_model.predict(np.array([last_window_scaled]))[0]
    
    # Plot feature maps
    plt.figure(figsize=(7, 10))
    
    # Plot original data
    plt.subplot(5, 1, 1)
    plt.title("Original Price Data (Normalized)")
    plt.plot(np.arange(window_size), last_window_scaled[:, 0], label='Open', alpha=0.7)
    plt.plot(np.arange(window_size), last_window_scaled[:, 1], label='High', alpha=0.7)
    plt.plot(np.arange(window_size), last_window_scaled[:, 2], label='Low', alpha=0.7)
    plt.plot(np.arange(window_size), last_window_scaled[:, 3], label='Close', color='black', linewidth=2)
    
    # Plot candlestick representation
    plt.subplot(5, 1, 2)
    plt.title("Candlestick Representation")
    
    # Width of the candles
    width = 0.6
    
    for i in range(len(last_window_scaled)):
        # Candle color
        if last_window_scaled[i, 3] >= last_window_scaled[i, 0]:  # close >= open
            color = 'green'
            body_bottom = last_window_scaled[i, 0]  # open
            body_height = last_window_scaled[i, 3] - last_window_scaled[i, 0]  # close - open
        else:
            color = 'red'
            body_bottom = last_window_scaled[i, 3]  # close
            body_height = last_window_scaled[i, 0] - last_window_scaled[i, 3]  # open - close
        
        # Candle body
        plt.bar(i, body_height, bottom=body_bottom, color=color, width=width, alpha=0.5)
        
        # Candle wicks
        plt.plot([i, i], [last_window_scaled[i, 2], last_window_scaled[i, 1]], color='black', linewidth=1)
    
    # Plot feature maps visualization (first 3 channels)
    plt.subplot(5, 1, 3)
    plt.title("Feature Map Channel 1 - Pattern Recognition")
    plt.plot(time_indices, feature_maps[:, 0], color='blue')
    
    plt.subplot(5, 1, 4)
    plt.title("Feature Map Channel 2 - Trend Detection")
    plt.plot(time_indices, feature_maps[:, 1], color='orange')
    
    plt.subplot(5, 1, 5)
    plt.title("Feature Map Channel 3 - Volatility Detection")
    plt.plot(time_indices, feature_maps[:, 2], color='green')

Esta función crea una visualización multipanel que muestra los datos de origen, la representación en velas y las activaciones de los tres primeros canales del filtro convolucional. Cada canal se especializa en un aspecto concreto de la dinámica del mercado: uno capta las pautas que se repiten, otro se centra en las tendencias y otro reacciona ante la volatilidad. Es como descubrir la especialización de las distintas áreas del cerebro de un tráder experimentado.

Los resultados del mapa de calor de la atención resultan aún más interesantes:

def visualize_attention_heatmap(feature_model, last_window_scaled, window_size=48):
    # Get feature maps for the last window
    feature_maps = feature_model.predict(np.array([last_window_scaled]))[0]
    
    # Average activation across all channels to get a measure of "attention"
    avg_activation = np.mean(np.abs(feature_maps), axis=1)
    
    # Normalize to [0, 1] for visualization
    attention = (avg_activation - np.min(avg_activation)) / (np.max(avg_activation) - np.min(avg_activation))
    
    # Upsample attention to match original window size
    upsampled_attention = np.zeros(window_size)
    ratio = window_size / len(attention)
    
    for i in range(len(attention)):
        start_idx = int(i * ratio)
        end_idx = int((i+1) * ratio)
        upsampled_attention[start_idx:end_idx] = attention[i]
    
    # Plot the heatmap
    plt.figure(figsize=(7, 6))
    
    # Price plot with attention heatmap
    plt.subplot(2, 1, 1)
    plt.title("Model Attention Heatmap")
    
    # Plot close prices
    time_indices = np.arange(window_size)
    plt.plot(time_indices, last_window_scaled[:, 3], color='black', linewidth=2, label='Close Price')
    
    # Add shading based on attention
    plt.fill_between(time_indices, last_window_scaled[:, 3].min(), last_window_scaled[:, 3].max(), 
                     alpha=upsampled_attention * 0.5, color='red')
                     
    # Highlight points with high attention
    high_attention_threshold = 0.7
    high_attention_indices = np.where(upsampled_attention > high_attention_threshold)[0]
    plt.scatter(high_attention_indices, last_window_scaled[high_attention_indices, 3], 
               color='red', s=50, zorder=5, label='High Attention Points')

Esta función crea un verdadero mapa de la mente del modelo, mostrando a qué partes del gráfico le presta más atención a la hora de tomar una decisión. Las zonas rojas de mayor atención suelen coincidir con niveles clave y puntos pivote, lo cual confirma que nuestro modelo sí ha aprendido a destacar formaciones de precios significativas.


De la previsión al beneficio: la aplicación práctica

El colofón final de nuestro sistema es la visualización de la previsión y su presentación de forma comprensible para los tráders:

def plot_prediction(data, window_size=48, prediction_window=24, direction="UP ▲"):
    plt.figure(figsize=(7, 4.2))
    
    # Get the last window of data for visualization
    last_window = data.iloc[-window_size:]
    
    # Create time index for prediction
    last_date = last_window.index[-1]
    future_dates = pd.date_range(start=last_date, periods=prediction_window+1, freq=data.index.to_series().diff().mode()[0])[1:]
    
    # Plot closing prices
    plt.plot(last_window.index, last_window['close'], label='Historical Data')
    
    # Add marker for current price
    current_price = last_window['close'].iloc[-1]
    plt.scatter(last_window.index[-1], current_price, color='blue', s=100, zorder=5)
    plt.annotate(f'Current price: {current_price:.5f}', 
                 xy=(last_window.index[-1], current_price),
                 xytext=(10, -30),
                 textcoords='offset points',
                 fontsize=10,
                 arrowprops=dict(arrowstyle='->', color='black'))
    
    # Visualize the predicted direction
    arrow_start = (last_window.index[-1], current_price)
    
    # Calculate range for the arrow (approximately 10% of price range)
    price_range = last_window['high'].max() - last_window['low'].min()
    arrow_length = price_range * 0.1
    
    # Up or down prediction
    if direction == "UP ▲":
        arrow_end = (future_dates[-1], current_price + arrow_length)
        arrow_color = 'green'
    else:
        arrow_end = (future_dates[-1], current_price - arrow_length)
        arrow_color = 'red'
    
    # Direction arrow
    plt.annotate('', 
                 xy=arrow_end,
                 xytext=arrow_start,
                 arrowprops=dict(arrowstyle='->', lw=2, color=arrow_color))
    
    plt.title(f'EURUSD - Forecast for {prediction_window} periods: {direction}')

Esta visualización convierte una predicción abstracta en una señal comercial clara. Las flechas verdes indican una subida prevista, mientras que las flechas rojas indican una caída. Los tráders puede evaluar instantáneamente la situación actual y tomar una decisión respaldada por un análisis de inteligencia artificial.

Pero nuestro sistema va más allá de la simple predicción de la dirección. Cuantifica la confianza en la previsión:

# Predict on the last available window
def make_prediction(model, data, window_size=48):
    # Get the last window of data
    last_window = data.iloc[-window_size:][['open', 'high', 'low', 'close']]
    
    # Normalize data
    scaler = MinMaxScaler(feature_range=(0, 1))
    last_window_scaled = scaler.fit_transform(last_window)
    
    # Prepare data for the model
    last_window_reshaped = np.array([last_window_scaled])
    
    # Get prediction
    prediction = model.predict(last_window_reshaped)[0][0]
    
    # Interpret result
    direction = "UP ▲" if prediction > 0.5 else "DOWN ▼"
    confidence = prediction if prediction > 0.5 else 1 - prediction
    
    return direction, confidence * 100, last_window_scaled

El valor de salida de la red neuronal se interpreta no solo como una respuesta binaria, sino como una probabilidad de movimiento alcista. Esto permite al tráder tomar decisiones informadas considerando no solo la dirección, sino también la confianza del sistema en su previsión. Una previsión con un 95% de confianza puede merecer una postura más agresiva que una previsión con un 55%.


Orquestación de procesos: la función principal

Todos los elementos del sistema confluyen en una función principal que coordina todo el proceso, desde la conexión a los datos hasta la visualización de los resultados:

def main():
    print("Starting EURUSD prediction system with computer vision")
    
    # Connect to MT5
    if not connect_to_mt5():
        return
    
    print("Successfully connected to MetaTrader5")
    
    # Load historical data
    bars_to_load = 2000  # Load more than needed for training
    data = get_historical_data(num_bars=bars_to_load)
    if data is None:
        mt5.shutdown()
        return
    
    print(f"Loaded {len(data)} bars of EURUSD history")
    
    # Convert data to image format
    print("Converting data for computer vision processing...")
    images, targets = create_images(data)
    print(f"Created {len(images)} images for training")
    
    # Train model
    print("Training computer vision model...")
    model, history, feature_model = train_cv_model(images, targets)
    
    # Visualize training process
    plot_learning_history(history)
    
    # Prediction
    direction, confidence, last_window_scaled = make_prediction(model, data)
    print(f"Forecast for the next 24 periods: {direction} (confidence: {confidence:.2f}%)")
    
    # Visualize prediction
    plot_prediction(data, direction=direction)
    
    # Visualize how the model "sees" the market
    visualize_model_perception(feature_model, last_window_scaled)
    
    # Visualize attention heatmap
    visualize_attention_heatmap(feature_model, last_window_scaled)
    
    # Disconnect from MT5
    mt5.shutdown()
    print("Work completed")

Esta función es como un director de orquesta que gestiona una compleja sinfonía de datos y algoritmos. Nos lleva secuencialmente a través de todo el proceso, desde la primera conexión al terminal hasta las visualizaciones finales que revelan el mundo interior de la inteligencia artificial.


Conclusión: una nueva era de negociación algorítmica

Hoy hemos creado algo más que otro indicador técnico o un sistema comercial más. Nuestro modelo de visión por computadora supone una nueva forma de percibir el mercado, una herramienta que nos permite mirar más allá de la visión humana y descubrir patrones ocultos en el ruido de las fluctuaciones de precio.

No es solo una caja negra que emite señales de origen desconocido. Gracias a las visualizaciones, podemos ver cómo percibe el mercado el modelo, a qué características de los movimientos de precios presta atención y cómo se forman sus previsiones. Y esto crea un nuevo nivel de confianza y entendimiento entre el tráder y el algoritmo.

En una era en la que los algoritmos de alta frecuencia ejecutan millones de transacciones por segundo, nuestro sistema representa un enfoque diferente y más profundo del trading algorítmico, un enfoque basado no en la velocidad de ejecución, sino en una comprensión profunda de la dinámica del mercado a través de la lente de la visión por computadora.

El código es abierto, la tecnología está disponible y los resultados son impresionantes. Su viaje al mundo de la visión por computadora para el trading no ha hecho más que empezar. Y quién sabe, quizá esta misma visión (a través del prisma algorítmico de las redes neuronales) le permita ver lo que antes permanecía invisible y encontrar la clave para entender los movimientos siempre cambiantes de las cotizaciones de las divisas.

Imagine una sesión comercial del futuro: en su pantalla no solo aparecerán los tradicionales gráficos de velas y líneas indicadoras, sino también una visualización en directo de cómo la inteligencia artificial percibe la situación actual del mercado, qué patrones destaca y a qué niveles de precios presta especial atención. No es ciencia ficción: es tecnología disponible hoy en día. Solo le queda dar un paso hacia la nueva era del trading algorítmico.

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

Archivos adjuntos |
CV_Model.py (15.71 KB)
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Del básico al intermedio: Indicador (V) Del básico al intermedio: Indicador (V)
En este artículo, veremos cómo podemos lidiar con solicitudes del usuario para cambiar el modo de trazado del gráfico. Esto, para que podamos lograr que un indicador, orientado a usar el modo de trazado gráfico actual, no quede extraño ni diferente de lo que el usuario de MetaTrader 5 esperaría.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Creación de interfaces gráficas dinámicas MQL5 mediante el escalado de imágenes basado en recursos con interpolación bicúbica en gráficos de trading Creación de interfaces gráficas dinámicas MQL5 mediante el escalado de imágenes basado en recursos con interpolación bicúbica en gráficos de trading
En este artículo exploramos las interfaces gráficas dinámicas MQL5, utilizando interpolación bicúbica para un escalado de imágenes de alta calidad en los gráficos de trading. Detallamos opciones de posicionamiento flexibles que permiten el centrado dinámico o el anclaje en esquina con desplazamientos personalizados.