
Uso conjunto de PSAR, Heiken Ashi y Deep Learning para el trading
Introducción
Encontrar una ventaja puede ser la diferencia entre el éxito y el fracaso. Con tantos datos disponibles, los comerciantes recurren cada vez más a tecnologías avanzadas como el aprendizaje profundo para obtener una ventaja. Este viaje implica combinar herramientas tradicionales de análisis técnico con modelos modernos de inteligencia artificial (IA) para evaluar y adaptarse rápidamente a las condiciones del mercado. Para explorar este potencial, hemos desarrollado un script en Python diseñado para combinar el poder predictivo del aprendizaje profundo con indicadores comerciales probados y verdaderos. Este script no pretende ser un sistema de trading completo; más bien, es una herramienta para la experimentación rápida: una forma de probar rápidamente si una estrategia de trading tiene el potencial de ser rentable. Paralelamente, un script de MetaTrader 5 utiliza principios similares para llevar este enfoque a un entorno comercial en vivo, cerrando la brecha entre las pruebas retrospectivas teóricas y la aplicación en el mundo real.
Creación del modelo ONNX de aprendizaje profundo
En el acelerado mundo del trading de divisas, mantenerse a la vanguardia es crucial. Por eso hemos desarrollado un script en Python que aprovecha el poder del aprendizaje profundo para predecir los movimientos de los pares de divisas. ¡Hagamos un recorrido amistoso por este emocionante fragmento de código y veamos cómo hace su magia!
Nuestro script comienza importando todas las herramientas necesarias: piense en ello como si reuniera ingredientes para una receta compleja. Utilizamos bibliotecas como TensorFlow y Keras, que son los motores detrás de muchas aplicaciones de IA modernas. También utilizamos MetaTrader 5 para obtener datos reales de Forex.
import MetaTrader5 as mt5 import tensorflow as tf import numpy as np import pandas as pd import tf2onnx import keras
A continuación, configuramos algunos parámetros importantes. Estamos analizando 120 días de datos históricos para el par EURUSD. ¿Por qué 120? Es un punto óptimo que nos brinda suficiente historial para detectar patrones sin abrumar nuestro modelo.
Luego utilizamos MetaTrader 5 para obtener estos datos. Imagínese enviar a un viajero en el tiempo para recopilar información de precios de los últimos 120 días. Una vez que tenemos estos datos, los organizamos cuidadosamente en un formato que nuestra IA pueda entender.
inp_history_size = 120 sample_size = inp_history_size*3*20 symbol = "EURUSD" optional = "D1_2024" inp_model_name = str(symbol)+"_"+str(optional)+".onnx" if not mt5.initialize(): print("initialize() failed, error code =",mt5.last_error()) quit() # we will save generated onnx-file near the our script to use as resource from sys import argv data_path=argv[0] last_index=data_path.rfind("\\")+1 data_path=data_path[0:last_index] print("data path to save onnx model",data_path) # and save to MQL5\Files folder to use as file terminal_info=mt5.terminal_info() file_path=terminal_info.data_path+"\\MQL5\\Files\\" print("file path to save onnx model",file_path) # set start and end dates for history data from datetime import timedelta, datetime #end_date = datetime.now() end_date = datetime(2024, 1, 1, 0) start_date = end_date - timedelta(days=inp_history_size*20*3) # print start and end dates print("data start date =",start_date) print("data end date =",end_date) # get rates eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size)
Los datos financieros sin procesar pueden ser confusos, por eso los limpiamos. Nos centramos en los precios de cierre, es decir, el precio final de cada día de negociación. También escalamos estos datos para que estén entre 0 y 1. Este paso es crucial porque ayuda a nuestro modelo de IA a aprender de forma más efectiva, como si tradujera todo a un lenguaje común.
from sklearn.preprocessing import MinMaxScaler scaler=MinMaxScaler(feature_range=(0,1)) scaled_data = scaler.fit_transform(data)
No queremos que nuestro modelo memorice los datos, por lo que lo dividimos en dos partes: datos de entrenamiento (80%) y datos de prueba (20%). Es como darle a nuestro estudiante de IA un libro de texto para estudiar (datos de entrenamiento) y luego una prueba separada para evaluar sus conocimientos (datos de prueba).
# scale data from sklearn.preprocessing import MinMaxScaler scaler=MinMaxScaler(feature_range=(0,1)) scaled_data = scaler.fit_transform(data) # training size is 80% of the data training_size = int(len(scaled_data)*0.80) print("Training_size:",training_size) train_data_initial = scaled_data[0:training_size,:] test_data_initial = scaled_data[training_size:,:1] # split a univariate sequence into samples def split_sequence(sequence, n_steps): X, y = list(), list() for i in range(len(sequence)): # find the end of this pattern end_ix = i + n_steps # check if we are beyond the sequence if end_ix > len(sequence)-1: break # gather input and output parts of the pattern seq_x, seq_y = sequence[i:end_ix], sequence[end_ix] X.append(seq_x) y.append(seq_y) return np.array(X), np.array(y) # split into samples time_step = inp_history_size x_train, y_train = split_sequence(train_data_initial, time_step) x_test, y_test = split_sequence(test_data_initial, time_step) # reshape input to be [samples, time steps, features] which is required for LSTM x_train =x_train.reshape(x_train.shape[0],x_train.shape[1],1) x_test = x_test.reshape(x_test.shape[0],x_test.shape[1],1)
¡Ahora viene la parte emocionante: construir nuestro modelo de IA! Estamos utilizando una combinación de diferentes técnicas de IA:
- Redes neuronales convolucionales (CNN): son excelentes para detectar patrones en secuencias, de la misma manera que nuestros ojos detectan patrones en imágenes.
- Redes de memoria a corto y largo plazo (LSTM): son excelentes para comprender las dependencias a largo plazo en los datos y son perfectas para detectar tendencias a lo largo del tiempo.
- Capas densas: ayudan a nuestro modelo a realizar sus predicciones finales.
También agregamos algunas capas de abandono para evitar que nuestro modelo se vuelva demasiado confiado y "sobreajuste" los datos.
# define model from keras.models import Sequential from keras.layers import Dense, Activation, Conv1D, MaxPooling1D, Dropout, Flatten, LSTM from keras.metrics import RootMeanSquaredError as rmse from tensorflow.keras import callbacks model = Sequential() model.add(Conv1D(filters=256, kernel_size=2, strides=1, padding='same', activation='relu', input_shape=(inp_history_size,1))) model.add(MaxPooling1D(pool_size=2)) model.add(LSTM(100, return_sequences = True)) model.add(Dropout(0.3)) model.add(LSTM(100, return_sequences = False)) model.add(Dropout(0.3)) model.add(Dense(units=1, activation = 'sigmoid')) model.compile(optimizer='adam', loss= 'mse' , metrics = [rmse()]) # Set up early stopping early_stopping = callbacks.EarlyStopping( monitor='val_loss', patience=20, restore_best_weights=True, ) # model training for 300 epochs history = model.fit(x_train, y_train, epochs = 300 , validation_data = (x_test,y_test), batch_size=32, callbacks=[early_stopping], verbose=2)Entrenar el modelo es como enviar nuestra IA a la escuela. Le mostramos nuestros datos de entrenamiento muchas veces (hasta 300 épocas) y gradualmente aprende a predecir precios futuros basándose en patrones pasados. Usamos algo llamado "detención temprana" para asegurarnos de que nuestra IA no estudie durante demasiado tiempo y comience a memorizar en lugar de aprender.
Después del entrenamiento, probamos el rendimiento de nuestro modelo. Utilizamos métricas como RMSE (error cuadrático medio) para ver qué tan cerca están nuestras predicciones de los precios reales. Es como calificar los resultados de las pruebas de nuestra IA.
# evaluate training data train_loss, train_rmse = model.evaluate(x_train,y_train, batch_size = 32) print(f"train_loss={train_loss:.3f}") print(f"train_rmse={train_rmse:.3f}") # evaluate testing data test_loss, test_rmse = model.evaluate(x_test,y_test, batch_size = 32) print(f"test_loss={test_loss:.3f}") print(f"test_rmse={test_rmse:.3f}")
El paso final es convertir nuestro modelo entrenado al formato ONNX. ONNX es como un lenguaje universal para modelos de IA: permite que nuestro modelo se utilice en diferentes entornos, incluidos nuestros scripts de MetaTrader.
# Define a function that represents your model @tf.function(input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)]) def model_function(x): return model(x) output_path = data_path+inp_model_name # Convert the model to ONNX onnx_model, _ = tf2onnx.convert.from_function( model_function, input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)], opset=13, output_path=output_path ) print(f"Saved ONNX model to {output_path}") # save model to ONNX output_path = data_path+inp_model_name onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path) print(f"saved model to {output_path}") output_path = file_path+inp_model_name onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path) print(f"saved model to {output_path}")No nos detenemos sólo en números: creamos varios gráficos para visualizar el rendimiento de nuestro modelo. Estos gráficos muestran cosas como el progreso de aprendizaje del modelo y cómo sus predicciones se comparan con los precios reales.
Al final de este script, tendremos un poderoso modelo ONNX listo para ser utilizado en nuestras estrategias de trading de divisas. Está entrenado con datos históricos de EURUSD y puede ayudar a predecir movimientos de precios futuros.
Recuerde que, si bien este modelo es sofisticado, no es una bola de cristal. El mercado Forex está influenciado por muchos factores complejos. Utilice siempre las predicciones de IA como una herramienta entre muchas en su conjunto de herramientas comerciales y nunca olvide la importancia de la gestión de riesgos.
Script de Python para probar una estrategia
Este script de Python es esencialmente una forma rápida de probar si una estrategia comercial tiene el potencial de ser rentable. No está pensado para ser un sistema comercial completo, sino más bien una herramienta para una experimentación rápida. Al combinar un modelo de aprendizaje profundo con análisis técnico, se busca ver si existe una ventaja viable en el mercado sin empantanarse de inmediato en optimizaciones complejas.
El script comienza conectándose a MetaTrader 5 para extraer datos de precios históricos del EURUSD. Utiliza datos horarios para capturar movimientos a corto plazo y aplica velas Heikin Ashi para suavizar el ruido en el mercado. Esto ayuda a identificar tendencias con mayor claridad, lo que es crucial cuando se prueba rápidamente una estrategia. Luego agrega algunos indicadores técnicos conocidos como PSAR, SMA, RSI y ATR. Estos indicadores sirven como una verificación rápida de las condiciones del mercado, brindando al modelo contexto para tomar decisiones de compra y venta.
import MetaTrader5 as mt5 import pandas as pd import numpy as np import onnxruntime as ort from sklearn.preprocessing import MinMaxScaler from ta.trend import PSARIndicator, SMAIndicator from ta.momentum import RSIIndicator from ta.volatility import AverageTrueRange import matplotlib.pyplot as plt # Inicializar conexión con MetaTrader5 if not mt5.initialize(): print("Inicialización fallida") mt5.shutdown() def get_historical_data(symbol, timeframe, start_date, end_date): rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date) df = pd.DataFrame(rates) df['time'] = pd.to_datetime(df['time'], unit='s') df.set_index('time', inplace=True) return df def calculate_heikin_ashi(df): ha_close = (df['open'] + df['high'] + df['low'] + df['close']) / 4 ha_open = (df['open'].shift(1) + df['close'].shift(1)) / 2 ha_high = df[['high', 'open', 'close']].max(axis=1) ha_low = df[['low', 'open', 'close']].min(axis=1) df['ha_close'] = ha_close df['ha_open'] = ha_open df['ha_high'] = ha_high df['ha_low'] = ha_low return df def add_indicators(df): # Calcular Heikin Ashi df = calculate_heikin_ashi(df) # PSAR con parámetros ajustados psar = PSARIndicator(df['high'], df['low'], df['close'], step=0.02, max_step=0.2) df['psar'] = psar.psar() # Añadir SMA sma = SMAIndicator(df['close'], window=50) df['sma'] = sma.sma_indicator() # Añadir RSI rsi = RSIIndicator(df['close'], window=14) df['rsi'] = rsi.rsi() # Añadir ATR para medir volatilidad atr = AverageTrueRange(df['high'], df['low'], df['close'], window=14) df['atr'] = atr.average_true_range() # Añadir filtro de tendencia simple df['trend'] = np.where(df['close'] > df['sma'], 1, -1) return df
El corazón del script radica en el uso de un modelo de aprendizaje profundo ONNX previamente entrenado. Al escalar los datos y alimentar este modelo con ellos, el script genera predicciones sobre hacia dónde podría dirigirse el mercado a continuación. Combina estas predicciones con los indicadores técnicos para crear un conjunto básico de reglas comerciales. Por ejemplo, busca oportunidades para entrar en largo cuando se dan ciertas condiciones, como que el precio esté por encima de la media móvil y el RSI muestre que el mercado no está sobrecomprado. Por el contrario, señala una posición corta cuando se cumplen las condiciones opuestas. El script incluso agrega mecanismos adaptativos de stop-loss y take-profit para gestionar el riesgo, aunque estos se mantienen relativamente simples para mantener el enfoque del script en las pruebas rápidas.
def prepare_data(df, window_size=120): scaler = MinMaxScaler(feature_range=(0, 1)) scaled_data = scaler.fit_transform(df[['close']]) X = [] for i in range(window_size, len(scaled_data)): X.append(scaled_data[i-window_size:i]) return np.array(X), scaler def load_onnx_model(model_path): return ort.InferenceSession(model_path) def predict_with_onnx(model, input_data): input_name = model.get_inputs()[0].name output_name = model.get_outputs()[0].name return model.run([output_name], {input_name: input_data})[0] def backtest(df, model, scaler, window_size=120, initial_balance=10000): scaled_data = scaler.transform(df[['close']]) predictions = [] for i in range(window_size, len(scaled_data)): X = scaled_data[i-window_size:i].reshape(1, window_size, 1) pred = predict_with_onnx(model, X.astype(np.float32)) predictions.append(scaler.inverse_transform(pred.reshape(-1, 1))[0, 0]) df['predictions'] = [np.nan]*window_size + predictions # Nueva lógica de trading df['position'] = 0 long_condition = ( (df['close'] > df['predictions']) & (df['close'] > df['psar']) & (df['close'] > df['sma']) & (df['rsi'] < 60) & # Condición RSI menos estricta (df['ha_close'] > df['ha_open']) & (df['ha_close'].shift(1) > df['ha_open'].shift(1)) & (df['trend'] == 1) # Solo comprar en tendencia alcista ) short_condition = ( (df['close'] < df['predictions']) & (df['close'] < df['psar']) & (df['close'] < df['sma']) & (df['rsi'] > 40) & # Condición RSI menos estricta (df['ha_close'] < df['ha_open']) & (df['ha_close'].shift(1) < df['ha_open'].shift(1)) & (df['trend'] == -1) # Solo vender en tendencia bajista ) df.loc[long_condition, 'position'] = 1 # Compra df.loc[short_condition, 'position'] = -1 # Venta # Implementar stop-loss y take-profit adaptativos sl_atr_multiple = 2 tp_atr_multiple = 3 for i in range(window_size, len(df)): if df['position'].iloc[i-1] != 0: entry_price = df['close'].iloc[i-1] current_atr = df['atr'].iloc[i-1] if df['position'].iloc[i-1] == 1: # Posición larga sl_price = entry_price - sl_atr_multiple * current_atr tp_price = entry_price + tp_atr_multiple * current_atr if df['low'].iloc[i] < sl_price or df['high'].iloc[i] > tp_price: df.loc[df.index[i], 'position'] = 0 else: # Posición corta sl_price = entry_price + sl_atr_multiple * current_atr tp_price = entry_price - tp_atr_multiple * current_atr if df['high'].iloc[i] > sl_price or df['low'].iloc[i] < tp_price: df.loc[df.index[i], 'position'] = 0 df['returns'] = df['close'].pct_change() df['strategy_returns'] = df['position'].shift(1) * df['returns'] # Calcular balance df['cumulative_returns'] = (1 + df['strategy_returns']).cumprod() df['balance'] = initial_balance * df['cumulative_returns'] return df
En cuanto a los resultados, el script ofrece una instantánea rápida del rendimiento de la estrategia. En este caso, obtuvo una rentabilidad total del 1,35%, lo que significa que convirtió 10.000 dólares en 10.135,02 dólares durante el periodo de prueba. Si bien no se trata de una ganancia espectacular, es un resultado positivo que sugiere que la estrategia tiene cierto mérito. El índice de Sharpe, una medida común de rendimiento ajustado al riesgo, fue de 0,39. Esta cifra indica que, si bien hubo ganancias, éstas conllevaron un riesgo considerable. Para una prueba rápida, esta relación es una forma útil de evaluar si vale la pena seguir con la estrategia.
Retorno total: 1.35% Ratio de Sharpe: 0.39 Balance final: $10135.02
Las visualizaciones generadas por el script proporcionan una descripción general útil de cómo funcionó la estrategia. El primer gráfico muestra la interacción entre el precio del mercado, las predicciones del modelo y las señales de compra y venta. El gráfico Heikin Ashi ofrece una visión más clara de las tendencias del mercado, ayudándole a ver si la estrategia está capturando los momentos adecuados para entrar y salir de las operaciones. Por último, la curva de capital ofrece una visión directa de cómo ha cambiado el saldo de la cuenta a lo largo del tiempo, destacando tanto los períodos de crecimiento como las caídas.
En resumen, este script está diseñado para una experimentación rápida. Le ayuda a evaluar rápidamente si una estrategia tiene potencial sin tener que profundizar demasiado en ajustes. Si bien demuestra que la estrategia puede generar algunas ganancias, el modesto índice de Sharpe sugiere que aún queda trabajo por hacer. Esto lo convierte en un punto de partida útil: una forma de determinar rápidamente si una estrategia merece un desarrollo y una optimización más profundos.
Asesor experto
Este script de MetaTrader 5 está diseñado para probar rápidamente si una estrategia comercial puede ser rentable mediante el uso de una combinación de análisis técnico y un modelo de aprendizaje profundo. La idea aquí no es crear un sistema de comercio infalible, sino experimentar combinando indicadores de comercio tradicionales con el poder del aprendizaje automático para ver si hay una ventaja en el mercado.
El script se centra en el par de divisas EURUSD y utiliza el marco temporal H6 (6 horas). Comienza configurando un puñado de indicadores técnicos familiares como el RSI, SMA, PSAR y ATR. Estas son herramientas comunes que utilizan los comerciantes para medir el impulso del mercado, la dirección de la tendencia y la volatilidad. Además, utiliza velas Heikin Ashi, que ayudan a suavizar la acción del precio y hacen que las tendencias sean más fáciles de detectar al reducir el ruido del mercado. Esta configuración inicial sienta las bases de lo que el script intenta lograr: una forma sistemática de buscar oportunidades de compra y venta.
int handleRSI, handleSMA, handlePSAR, handleATR; double rsiBuffer[], smaBuffer[], psarBuffer[], atrBuffer[]; double haOpen[], haClose[], haHigh[], haLow[]; CTrade trade;
handleRSI = iRSI(Symbol, Timeframe, RSIPeriod, PRICE_CLOSE); handleSMA = iMA(Symbol, Timeframe, SMAPeriod, 0, MODE_SMA, PRICE_CLOSE); handlePSAR = iSAR(Symbol, Timeframe, PSARStep, PSARMaximum); handleATR = iATR(Symbol, Timeframe, ATRPeriod); if(handleRSI == INVALID_HANDLE || handleSMA == INVALID_HANDLE || handlePSAR == INVALID_HANDLE || handleATR == INVALID_HANDLE) { Print("Error creating indicators"); return(INIT_FAILED); } ArraySetAsSeries(rsiBuffer, true); ArraySetAsSeries(smaBuffer, true); ArraySetAsSeries(psarBuffer, true); ArraySetAsSeries(atrBuffer, true); ArrayResize(haOpen, 3); ArrayResize(haClose, 3); ArrayResize(haHigh, 3); ArrayResize(haLow, 3); ArraySetAsSeries(haOpen, true); ArraySetAsSeries(haClose, true); ArraySetAsSeries(haHigh, true); ArraySetAsSeries(haLow, true);
IndicatorRelease(handleRSI); IndicatorRelease(handleSMA); IndicatorRelease(handlePSAR); IndicatorRelease(handleATR);
Lo que distingue a este script es el uso de un modelo de aprendizaje profundo ONNX previamente entrenado. En lugar de confiar únicamente en el análisis técnico tradicional, el script aprovecha el poder predictivo del aprendizaje automático para pronosticar los movimientos de precios. Carga este modelo directamente en el entorno comercial, donde toma datos recientes del mercado, los normaliza y predice si el precio subirá, bajará o se mantendrá igual. Esta predicción se clasifica luego en una de tres categorías: un movimiento ascendente, un movimiento descendente o ningún movimiento significativo en absoluto. Al combinar estas predicciones con indicadores técnicos, el script tiene como objetivo tomar decisiones comerciales más informadas.
#define SAMPLE_SIZE 120 long ExtHandle=INVALID_HANDLE; int ExtPredictedClass=-1; datetime ExtNextBar=0; datetime ExtNextDay=0; float ExtMin=0.0; float ExtMax=0.0; CTrade ExtTrade; int dlsignal=-1; //--- price movement prediction #define PRICE_UP 0 #define PRICE_SAME 1 #define PRICE_DOWN 2 #resource "/Files/EURUSD_D1_2024.onnx" as uchar ExtModel[]
//--- create a model from static buffer ExtHandle=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT); if(ExtHandle==INVALID_HANDLE) { Print("OnnxCreateFromBuffer error ",GetLastError()); return(INIT_FAILED); } //--- since not all sizes defined in the input tensor we must set them explicitly //--- first index - batch size, second index - series size, third index - number of series (only Close) const long input_shape[] = {1,SAMPLE_SIZE,1}; if(!OnnxSetInputShape(ExtHandle,ONNX_DEFAULT,input_shape)) { Print("OnnxSetInputShape error ",GetLastError()); return(INIT_FAILED); }
//--- check new day if(TimeCurrent() >= ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay = TimeCurrent(); ExtNextDay -= ExtNextDay % PeriodSeconds(PERIOD_D1); ExtNextDay += PeriodSeconds(PERIOD_D1); } //--- check new bar if(TimeCurrent() < ExtNextBar) return; //--- set next bar time ExtNextBar = TimeCurrent(); ExtNextBar -= ExtNextBar % PeriodSeconds(); ExtNextBar += PeriodSeconds(); //--- check min and max float close = (float)iClose(_Symbol, _Period, 0); if(ExtMin > close) ExtMin = close; if(ExtMax < close) ExtMax = close;
void PredictPrice(void) { static vectorf output_data(1); // vector to get result static vectorf x_norm(SAMPLE_SIZE); // vector for prices normalize //--- check for normalization possibility if(ExtMin>=ExtMax) { Print("ExtMin>=ExtMax"); ExtPredictedClass=-1; return; } //--- request last bars if(!x_norm.CopyRates(_Symbol,_Period,COPY_RATES_CLOSE,1,SAMPLE_SIZE)) { Print("CopyRates ",x_norm.Size()); ExtPredictedClass=-1; return; } float last_close=x_norm[SAMPLE_SIZE-1]; //--- normalize prices x_norm-=ExtMin; x_norm/=(ExtMax-ExtMin); //--- run the inference if(!OnnxRun(ExtHandle,ONNX_NO_CONVERSION,x_norm,output_data)) { Print("OnnxRun"); ExtPredictedClass=-1; return; } //--- denormalize the price from the output value float predicted=output_data[0]*(ExtMax-ExtMin)+ExtMin; //--- classify predicted price movement float delta=last_close-predicted; if(fabs(delta)<=0.00001) ExtPredictedClass=PRICE_SAME; else { if(delta<0) ExtPredictedClass=PRICE_UP; else ExtPredictedClass=PRICE_DOWN; } } //+------------------------------------------------------------------+ //| Gets Min and Max values | //+------------------------------------------------------------------+ void GetMinMax(void) { vectorf close; close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,0,SAMPLE_SIZE); ExtMin=close.Min(); ExtMax=close.Max(); }
La lógica comercial es bastante sencilla pero sofisticada. Para una señal de compra (larga), se buscan múltiples condiciones para alinearse: el precio actual debe estar por encima del PSAR y la SMA, el RSI debe estar por debajo de 60 (lo que indica que el mercado no está sobrecomprado) y las velas Heikin Ashi deben sugerir una tendencia alcista. Es fundamental que el modelo de aprendizaje profundo también prediga un movimiento de precios ascendente. Si todos estos factores se alinean, el script abre una posición de compra. De manera similar, busca condiciones opuestas para abrir una posición de venta (corta). Este enfoque en capas tiene como objetivo filtrar el ruido y las señales falsas, asegurándose de que las operaciones solo se realicen cuando varios indicadores y el modelo coincidan en la dirección.
void CalculateHeikinAshi() { double close[], open[], high[], low[]; ArraySetAsSeries(close, true); ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); int copied = CopyClose(Symbol(), Timeframe, 0, 3, close); copied = MathMin(copied, CopyOpen(Symbol(), Timeframe, 0, 3, open)); copied = MathMin(copied, CopyHigh(Symbol(), Timeframe, 0, 3, high)); copied = MathMin(copied, CopyLow(Symbol(), Timeframe, 0, 3, low)); if(copied < 3) { Print("Not enough data for Heikin Ashi calculation"); return; } // Calculate Heikin Ashi values for the last 3 candles for(int i = 2; i >= 0; i--) { haClose[i] = (open[i] + high[i] + low[i] + close[i]) / 4; if(i == 2) { haOpen[i] = (open[i] + close[i]) / 2; } else { haOpen[i] = (haOpen[i+1] + haClose[i+1]) / 2; } haHigh[i] = MathMax(high[i], MathMax(haOpen[i], haClose[i])); haLow[i] = MathMin(low[i], MathMin(haOpen[i], haClose[i])); } // Debug print Print("Heikin Ashi values:"); for(int i = 0; i < 3; i++) { Print("Candle ", i, ": Open=", haOpen[i], " High=", haHigh[i], " Low=", haLow[i], " Close=", haClose[i]); } }
// Copy indicator data if(CopyBuffer(handleRSI, 0, 0, 3, rsiBuffer) <= 0 || CopyBuffer(handleSMA, 0, 0, 3, smaBuffer) <= 0 || CopyBuffer(handlePSAR, 0, 0, 3, psarBuffer) <= 0 || CopyBuffer(handleATR, 0, 0, 3, atrBuffer) <= 0) { Print("Failed to copy indicator data"); return; } CalculateHeikinAshi(); if(ArraySize(haOpen) < 3 || ArraySize(haClose) < 3) { Print("Not enough Heikin Ashi data"); return; } PredictPrice(); int trend = GetTrend(); bool longCondition = close > psarBuffer[1] && close > smaBuffer[1] && rsiBuffer[1] < 60 && haClose[1] > haOpen[1] && haClose[2] > haOpen[2] && ExtPredictedClass == PRICE_UP && trend == 1; bool shortCondition = close < psarBuffer[1] && close < smaBuffer[1] && rsiBuffer[1] > 40 && haClose[1] < haOpen[1] && haClose[2] < haOpen[2] && ExtPredictedClass == PRICE_DOWN && trend == -1; // Debug printing Print("--- Debug Info ---"); Print("Close: ", close, " PSAR: ", psarBuffer[1], " SMA: ", smaBuffer[1]); Print("RSI: ", rsiBuffer[1], " HA Close[1]: ", haClose[1], " HA Open[1]: ", haOpen[1]); Print("HA Close[2]: ", haClose[2], " HA Open[2]: ", haOpen[2]); Print("ExtPredictedClass: ", ExtPredictedClass, " Trend: ", trend); Print("Long Condition: ", longCondition, " Short Condition: ", shortCondition); if(longCondition) { Print("Long Condition Met"); if(PositionsTotal() == 0) { double atrStopLoss = SLATRMultiple * atrBuffer[1]; double minStopLoss = GetMinimumStopLoss(); double sl = close - MathMax(atrStopLoss, minStopLoss); double tp = close + TPATRMultiple * atrBuffer[1]; bool result = trade.Buy(0.01, Symbol(), 0, sl, tp); if(result) Print("Buy order placed successfully. SL: ", sl, " TP: ", tp); else Print("Failed to place buy order. Error: ", GetLastError()); } else { Print("Position already open. Skipping buy order."); } } else if(shortCondition) { Print("Short Condition Met"); if(PositionsTotal() == 0) { double atrStopLoss = SLATRMultiple * atrBuffer[1]; double minStopLoss = GetMinimumStopLoss(); double sl = close + MathMax(atrStopLoss, minStopLoss); double tp = close - TPATRMultiple * atrBuffer[1]; bool result = trade.Sell(0.01, Symbol(), 0, sl, tp); if(result) Print("Sell order placed successfully. SL: ", sl, " TP: ", tp); else Print("Failed to place sell order. Error: ", GetLastError()); } else { Print("Position already open. Skipping sell order."); } } ManageTrailingStop(); }
La gestión de riesgos también está integrada en el script. Utiliza el rango verdadero promedio (ATR) para establecer niveles dinámicos de stop-loss y take-profit. Al basar estos niveles en la volatilidad del mercado, el script se adapta a las condiciones cambiantes del mercado, con el objetivo de protegerse contra pérdidas significativas y, al mismo tiempo, fijar ganancias. Además, incluye un mecanismo de trailing stop, que ajusta el stop-loss a medida que la operación se mueve en una dirección favorable, protegiendo aún más las ganancias.
double GetMinimumStopLoss() { double spread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD) * SymbolInfoDouble(Symbol(), SYMBOL_POINT); double minStopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) * SymbolInfoDouble(Symbol(), SYMBOL_POINT); return MathMax(spread * 2, minStopLevel); // Usamos el doble del spread como mínimo } // Agregar esta función para manejar el trailing stop void ManageTrailingStop() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionSelectByTicket(PositionGetTicket(i))) { if(PositionGetString(POSITION_SYMBOL) == Symbol()) { double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); double currentStopLoss = PositionGetDouble(POSITION_SL); double currentTakeProfit = PositionGetDouble(POSITION_TP); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { double newStopLoss = NormalizeDouble(SymbolInfoDouble(Symbol(), SYMBOL_BID) - TrailingStop * SymbolInfoDouble(Symbol(), SYMBOL_POINT), Digits()); if(newStopLoss > currentStopLoss + TrailingStep * SymbolInfoDouble(Symbol(), SYMBOL_POINT)) { if(trade.PositionModify(PositionGetTicket(i), newStopLoss, currentTakeProfit)) { Print("Trailing stop ajustado para posición larga. Nuevo SL: ", newStopLoss); } } } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { double newStopLoss = NormalizeDouble(SymbolInfoDouble(Symbol(), SYMBOL_ASK) + TrailingStop * SymbolInfoDouble(Symbol(), SYMBOL_POINT), Digits()); if(newStopLoss < currentStopLoss - TrailingStep * SymbolInfoDouble(Symbol(), SYMBOL_POINT)) { if(trade.PositionModify(PositionGetTicket(i), newStopLoss, currentTakeProfit)) { Print("Trailing stop ajustado para posición corta. Nuevo SL: ", newStopLoss); } } } } } } }
En resumen, este script sirve como una forma rápida y experimental de probar la viabilidad de una estrategia comercial. No pretende ser la solución perfecta, sino más bien un campo de pruebas para ver si la combinación de análisis técnico y un modelo de aprendizaje profundo puede identificar oportunidades comerciales rentables. Si bien puede que no garantice ganancias, proporciona un enfoque más matizado e informado para las decisiones comerciales, combinando la intuición humana con conocimientos de aprendizaje automático.
Resultados
Los resultados del Asesor Experto (EA) brindan una visión detallada de su desempeño durante el período de prueba retrospectiva. Partiendo de un depósito inicial de 10.000 dólares, la estrategia obtuvo un modesto beneficio neto total de 17 unidades, lo que indica que, si bien generó beneficios, éstos fueron relativamente pequeños en proporción a la inversión inicial. La curva de equilibrio refleja este crecimiento gradual, mostrando una trayectoria ascendente constante, aunque lenta, a lo largo del tiempo.
Una de las métricas destacadas aquí es el factor de beneficio de 4,39. Se trata de una cifra sólida, que sugiere que por cada unidad de riesgo asumida, la estrategia obtuvo 4,39 unidades de recompensa. Esto implica que el EA fue eficaz al maximizar las ganancias en comparación con sus pérdidas. El factor de recuperación de 4,48 respalda aún más esto, mostrando que la estrategia fue capaz de recuperarse de las caídas de manera efectiva, lo que es una señal positiva de su solidez. Sin embargo, vale la pena destacar el ratio de Sharpe, que se sitúa en 5,25. Si bien esta relación es relativamente alta y generalmente indica buenos retornos ajustados al riesgo, la pequeña ganancia general sugiere que la estrategia podría haber estado asumiendo riesgos mínimos, lo que llevó a la ganancia absoluta limitada.
Profundizando en las estadísticas comerciales, el EA ejecutó 16 operaciones en total, divididas equitativamente entre posiciones largas y cortas. La tasa de ganancia para operaciones largas fue mayor, 62,5%, mientras que las operaciones cortas tuvieron una tasa de ganancia del 37,5%. Esta disparidad indica que la estrategia tuvo más éxito a la hora de capturar los movimientos ascendentes del mercado. Curiosamente, las operaciones rentables constituyeron exactamente el 50% de las operaciones totales, lo que resalta que la rentabilidad del EA no se debió a una alta tasa de ganancias, sino más bien al tamaño de sus ganadores versus sus perdedores. Las operaciones con ganancias más grandes y promedio fueron positivas, lo que sugiere que el EA logró asegurar ganancias cuando el mercado se movió favorablemente. La curva de equilibrio refleja estos resultados, mostrando períodos de caída seguidos de ganancias que indican un enfoque cuidadoso pero constante hacia el trading.
Las métricas de reducción muestran que el EA mantuvo un bajo nivel de exposición al riesgo. La caída absoluta del saldo fue de 2 unidades, lo cual es bastante pequeño, y la caída relativa fue de solo 0,02%. Esta reducción tan baja sugiere que la estrategia del EA fue conservadora y priorizó la preservación del capital por sobre la búsqueda agresiva de ganancias. Este enfoque conservador puede ser beneficioso en mercados volátiles, pero también explica el beneficio neto total relativamente modesto.
En resumen, el EA demostró un enfoque cauteloso y sistemático hacia el trading, centrándose en mantener un alto factor de ganancia y minimizar las caídas. Si bien el beneficio total fue relativamente pequeño, el alto factor de beneficio y las bajas reducciones indican una estrategia que se centra más en la gestión de riesgos que en la generación rápida de beneficios. Esto hace que el EA sea adecuado para operadores que priorizan un crecimiento constante y de bajo riesgo por sobre estrategias más agresivas y de alto riesgo. Sin embargo, para aquellos que buscan retornos absolutos más altos, el EA podría necesitar ajustes o mejoras adicionales para aumentar su potencial de ganancias y al mismo tiempo mantener su sólido marco de gestión de riesgos.
Conclusión
La exploración de la integración de modelos de aprendizaje profundo con el análisis técnico tradicional ha mostrado resultados prometedores. A través del script de Python, vimos cómo un enfoque sistemático que utiliza datos históricos, indicadores clave y aprendizaje automático puede ayudar a identificar posibles oportunidades comerciales. Los resultados fueron modestos pero consistentes, demostrando que dicha combinación puede producir un resultado positivo, aunque con algunas limitaciones. De manera similar, el Asesor Experto (EA) de MetaTrader 5 confirmó este enfoque cauteloso. Si bien el desempeño del EA durante las pruebas retrospectivas reveló un crecimiento pequeño pero constante con una reducción mínima, destacó una estrategia centrada en la gestión del riesgo en lugar de maximizar las ganancias. Esta naturaleza conservadora lo hace atractivo para los traders que priorizan un crecimiento constante y de bajo riesgo. Sin embargo, quienes buscan obtener mayores retornos necesitan mayor refinamiento y optimización. Al final, estas herramientas sirven como base, un punto de partida para que los traders combinen la intuición humana con el poder analítico de la IA, ofreciendo un enfoque matizado para navegar por el complejo mundo del trading de divisas.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15868
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso