//╔════════════════════════════════════════════════════════════════════════╗ //║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║ //║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║ //║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║ //║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║ //║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║ //║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║ //╚═══════ Algorithms that observe ════ Signals that speak ════════════════╝ //+------------------------------------------------------------------+ //| Archivo: Donchian_Channels_Trend.mq5 | //| Origen: Replicación del "Donchian Channels Trend" de TradingView | //| Autor Original: holdon_to_profits | //| Descripción: Indicador de canales con memoria de estado (FSM). | //| Detecta la tendencia basándose en rupturas de la | //| estructura previa y cambia de color dinámicamente. | //+------------------------------------------------------------------+ #property copyright "Exobeacon Labs" #property link "https://www.exobeacon.com/" #property version "1.0" #property description "Donchian Channels Trend (holdon_to_profits) v1.0" #property description "Trend-following breakout indicator." #property description "Dynamic coloring based on trend state memory." #property description "Identifies directional bias and structural levels." #property description " " #property description "〰〰〰〰〰((👽))〰〰〰〰〰" #property description "🛸 mql5.com/en/users/ulisescalb" #property description "🛸 github.com/Exobeacon-Labs" #property strict #property indicator_chart_window #property indicator_buffers 7 #property indicator_plots 3 // --- Configuración Plot 1: Upper Band --- #property indicator_label1 "Upper Band" #property indicator_type1 DRAW_COLOR_LINE #property indicator_color1 clrLime, clrRed #property indicator_style1 STYLE_SOLID #property indicator_width1 2 // --- Configuración Plot 2: Lower Band --- #property indicator_label2 "Lower Band" #property indicator_type2 DRAW_COLOR_LINE #property indicator_color2 clrLime, clrRed #property indicator_style2 STYLE_SOLID #property indicator_width2 2 // --- Configuración Plot 3: Basis Line --- #property indicator_label3 "Basis Line" #property indicator_type3 DRAW_COLOR_LINE #property indicator_color3 clrLime, clrRed #property indicator_style3 STYLE_DASHDOT #property indicator_width3 1 //+------------------------------------------------------------------+ //| ENTRADAS DEL USUARIO (Inputs) | //+------------------------------------------------------------------+ input group "====== Donchian Settings ======" input int InpLength = 20; // Lookback Period (Length) input group "====== Alert Settings ======" input bool InpEnableAlerts = false; // Enable All Alerts input bool InpAlertPopup = true; // Popup Alerts input bool InpAlertSound = true; // Sound Alerts input bool InpAlertPush = false; // Push Notifications input bool InpAlertEmail = false; // Email Alerts //+------------------------------------------------------------------+ //| BÚFERES GLOBALES (Buffers) | //+------------------------------------------------------------------+ double BufferUpper[]; double BufferUpperColor[]; double BufferLower[]; double BufferLowerColor[]; double BufferBasis[]; double BufferBasisColor[]; double BufferTrend[]; // Búfer oculto para memoria de tendencia (1 = Bullish, -1 = Bearish) //+------------------------------------------------------------------+ //| VARIABLES GLOBALES AUXILIARES | //+------------------------------------------------------------------+ datetime g_lastAlertTime = 0; // Control de tiempo para evitar spam de alertas //+------------------------------------------------------------------+ //| INIT FUNCTION | //+------------------------------------------------------------------+ int OnInit() { // 1. Validación estricta de parámetros if(InpLength < 1) { Print("Error: Lookback Period must be greater than 0."); return INIT_PARAMETERS_INCORRECT; } // 2. Mapeo de búferes de indicador SetIndexBuffer(0, BufferUpper, INDICATOR_DATA); SetIndexBuffer(1, BufferUpperColor, INDICATOR_COLOR_INDEX); SetIndexBuffer(2, BufferLower, INDICATOR_DATA); SetIndexBuffer(3, BufferLowerColor, INDICATOR_COLOR_INDEX); SetIndexBuffer(4, BufferBasis, INDICATOR_DATA); SetIndexBuffer(5, BufferBasisColor, INDICATOR_COLOR_INDEX); SetIndexBuffer(6, BufferTrend, INDICATOR_CALCULATIONS); // 3. Inicialización de valores vacíos y arrays PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); ArrayInitialize(BufferUpper, EMPTY_VALUE); ArrayInitialize(BufferLower, EMPTY_VALUE); ArrayInitialize(BufferBasis, EMPTY_VALUE); ArrayInitialize(BufferTrend, 1.0); // Asume tendencia alcista inicialmente // 4. Configuración visual para la ventana del indicador IndicatorSetInteger(INDICATOR_DIGITS, _Digits); string short_name = StringFormat("Donchian Trend (%d)", InpLength); IndicatorSetString(INDICATOR_SHORTNAME, short_name); // Evitar renderizar basura visual en las primeras velas PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpLength); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, InpLength); PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, InpLength); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| DEINIT FUNCTION | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Limpieza de objetos gráficos y comentarios si existieran Comment(""); } //+------------------------------------------------------------------+ //| CALCULATION FUNCTION | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { // Evitar cálculos si no hay suficientes barras if(rates_total < InpLength) return 0; // Determinar el índice de inicio optimizado int limit = prev_calculated == 0 ? InpLength - 1 : prev_calculated - 1; if (limit < InpLength - 1) limit = InpLength - 1; // Iteración principal (dirección estándar MQL5: de antiguo a reciente) for(int i = limit; i < rates_total; i++) { // 1. Encontrar Máximos y Mínimos en la ventana N double highest = high[i]; double lowest = low[i]; for(int j = 0; j < InpLength; j++) { if(high[i - j] > highest) highest = high[i - j]; if(low[i - j] < lowest) lowest = low[i - j]; } // 2. Asignación de datos matemáticos a los búferes BufferUpper[i] = highest; BufferLower[i] = lowest; BufferBasis[i] = (highest + lowest) / 2.0; // 3. Máquina de Estados (Trend Memory FSM) int current_trend = 1; // Valor por defecto if (i > InpLength - 1) { // Heredar el estado de la barra anterior (Memoria) current_trend = (int)BufferTrend[i-1]; // Regla 1: Ruptura alcista contra el borde dinámico de la iteración previa if(BufferUpper[i-1] != EMPTY_VALUE && high[i] >= BufferUpper[i-1]) { current_trend = 1; } // Regla 2: Ruptura bajista contra el borde dinámico de la iteración previa else if(BufferLower[i-1] != EMPTY_VALUE && low[i] <= BufferLower[i-1]) { current_trend = -1; } } // Guardar estado BufferTrend[i] = current_trend; // 4. Transformación Gráfica (Color Routing) // color_idx 0 = clrLime, 1 = clrRed int color_idx = (current_trend == 1) ? 0 : 1; BufferUpperColor[i] = color_idx; BufferLowerColor[i] = color_idx; BufferBasisColor[i] = color_idx; // 5. Sistema de Alertas (Evaluado solo en el cierre de la vela para evitar repintado) if(InpEnableAlerts && i == rates_total - 2 && i > limit) { if(BufferTrend[i] != BufferTrend[i-1]) { TriggerAlerts(current_trend, time[i]); } } } // Retornar número de barras calculadas para la siguiente iteración return rates_total; } //+------------------------------------------------------------------+ //| FUNCIONES AUXILIARES | //+------------------------------------------------------------------+ // Disparador multi-canal de alertas void TriggerAlerts(int trend_state, datetime signal_time) { // Filtrado de seguridad: solo una alerta por vela if(signal_time <= g_lastAlertTime) return; g_lastAlertTime = signal_time; // Preparación del mensaje (ASCII únicamente) string trend_name = (trend_state == 1) ? "BULLISH" : "BEARISH"; string msg = StringFormat("[Donchian Trend] %s %s - Trend shifted to %s", _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period), trend_name); // Ejecución de rutas de salida if(InpAlertPopup) Alert(msg); if(InpAlertSound) PlaySound("alert.wav"); if(InpAlertPush) SendNotification(msg); if(InpAlertEmail) SendMail("Donchian Trend Breakout", msg); // Registro en Journal Print(msg); } //+------------------------------------------------------------------+ //| REFERENCIA ORIGINAL PINE SCRIPT (DOCUMENTACIÓN INTERNA) | //+------------------------------------------------------------------+ /* //@version=5 indicator("Donchian Channels Trend", overlay=true) // --- Inputs --- length = input.int(20, title="Length") // --- Core Calculations --- upper = ta.highest(high, length) lower = ta.lowest(low, length) basis = math.avg(upper, lower) // --- State Machine --- var int trend = 1 if high >= upper[1] trend := 1 else if low <= lower[1] trend := -1 // --- Coloring Output --- col = trend == 1 ? color.lime : color.red plot(upper, color=col, linewidth=2, title="Upper Band") plot(lower, color=col, linewidth=2, title="Lower Band") plot(basis, color=col, style=plot.style_dashdot, title="Basis Line") */