diff --git a/OB_EURUSD_30M_2026_v107.mq5 b/OB_EURUSD_30M_2026_v107.mq5 new file mode 100644 index 0000000..3e0efdc --- /dev/null +++ b/OB_EURUSD_30M_2026_v107.mq5 @@ -0,0 +1,563 @@ +//+------------------------------------------------------------------+ +//| OB EURUSD 30 MINS 2026 (Conversion Pine Script v6 -> MQL5) | +//| V1.07 - + Interruptores por bloque para calibracion modular | +//+------------------------------------------------------------------+ +#property copyright "Conversion Pine -> MQL5" +#property version "1.07" +#property strict + +#include +CTrade trade; + +input group "Configuracion PineConnector" +input string pc_id = "8769131446485"; // License ID +input string pc_symbol = "EURUSD"; // Simbolo MT5 + +input group "Gestion de Capital" +input double riesgo_per = 2.0; // Riesgo por Operacion (%) +input int max_ops = 2; // Max. Operaciones Abiertas +input double max_daily_loss = 2.1; // Perdida Maxima Diaria (%) + +input group "Filtros Tecnicos (Osciladores)" +input bool Use_Osciladores = true; // ON/OFF bloque completo (RSI+ADX+CHOP) +input int atr_len = 13; // Periodo ATR +input int rsi_len = 7; // Periodo RSI +input double rsi_min = 10.0; // RSI Minimo +input double rsi_max = 36.4; // RSI Maximo +input int adx_len = 11; // Periodo ADX/DMI +input double adx_min = 11.2; // ADX Minimo +input double adx_max = 60.0; // ADX Maximo +input int chop_len = 10; // Periodo CHOP +input double chop_min = 26.7; // CHOP Minimo +input double chop_max = 72.2; // CHOP Maximo + +input group "Confirmacion de Velas" +input bool Use_Wicks = true; // ON/OFF filtro de mechas +input bool Use_Body = true; // ON/OFF filtro de cuerpo solido +input bool Use_VelaSize = true; // ON/OFF filtro de tamano de vela vs ATR +input bool Use_Volumen = true; // ON/OFF filtro de volumen +input double min_wick_top = 0.0; // Min. Mecha Superior (%) +input double max_wick_top = 39.0; // Max. Mecha Superior (%) +input double min_wick_bot = 0.0; // Min. Mecha Inferior (%) +input double max_wick_bot = 50.1; // Max. Mecha Inferior (%) +input double min_body_pct = 38.7; // Min. Cuerpo Solido (%) +input double max_body_pct = 100.0; // Max. Cuerpo Solido (%) +input double min_atr_size = 0.0; // Min. Tamano Vela (Mult. ATR) +input double max_atr_size = 1.8; // Max. Tamano Vela (Mult. ATR) +input int vol_ma_len = 20; // Periodo Media Volumen +input double min_vol_mult = 0.0; // Min. Volumen (Mult. Media) +input double max_vol_mult = 1.5; // Max. Volumen (Mult. Media) + +input group "Filtro Premium/Discount" +input bool use_pd_filter = true; // Activar Filtro Descuento +input int pd_lookback = 4; // Velas atras (Calibrado) +input double pd_threshold = 0.25; // Nivel Descuento (Calibrado) + +input group "OB DE COMPRA (BULLISH)" +input bool Use_StochBuy = true; // ON/OFF confirmacion estocastica de compra +input double st_nivel_buy = 28.8; // Estocastico Compra +input double st_conf_th = 30.0; // Umbral Confirmacion Stoch +input double ob_mult_buy = 0.8; // Fuerza Impulso Compra (ATR) +input double sl_atr_mult = 0.5; // Mult. SL (ATR) +input double tp_ratio = 1.5; // Ratio TP Respaldo +input double ob_buy_off = 0.1; // Offset de Entrada (Mult. ATR) + +input group "OB DE VENTA (BEARISH / TP)" +input double st_nivel_sell = 24.5; // Estocastico Venta +input double ob_mult_sell = 0.5; // Fuerza Impulso Venta (ATR) +input double pct_tp_ob_in = 1.1; // Alcance TP en OB Venta (%) + +input group "Configuracion MT5 (zona horaria / magic)" +input int InpBrokerGMT = 2; // Offset GMT del broker (ajustar: +2/+3) +input long InpMagic = 20260001; // Numero magico +input bool InpKeepBoxes = true; // Conservar cajas OB congeladas (auditoria) +input bool InpDeleteOnExit = false; // Borrar dibujos al detener EA/Tester +input bool InpUseDeadHours = true; // Aplicar filtro de horarios muertos (GMT-5) +input bool InpDebug = false; // Modo diagnostico: log de OB y rechazos + +input group "Criterios Minimos (OnTester / Optimizacion)" +input bool InpUseCustomMax = true; // Activar filtro custom max en optimizacion +input int InpMinTrades = 300; // Minimo de operaciones +input double InpMaxDDpct = 20.0; // DD maximo de equity permitido (%) +input double InpMinPF = 1.5; // Profit Factor minimo +input double InpMinProfit = 10000.0;// Ganancia neta minima +input double InpMinSharpe = 1.0; // Sharpe Ratio minimo +input double InpMinRecovery = 3.0; // Recovery Factor minimo + +double pct_tp_ob; + +struct SOB { double top; double bot; string name; }; +SOB buyOBs[]; +SOB sellOBs[]; + +int hRSI, hADX; +datetime g_lastBarTime = 0; +double g_equity_day0 = 0.0; +int g_last_day = -1; +double g_final_impulso = 0.0; +long g_objCnt = 0; +string PREF = "OBEU_"; + +double H(int s){ return iHigh(_Symbol,_Period,s); } +double L(int s){ return iLow(_Symbol,_Period,s); } +double C(int s){ return iClose(_Symbol,_Period,s);} +double O(int s){ return iOpen(_Symbol,_Period,s); } +long V(int s){ return iTickVolume(_Symbol,_Period,s); } +datetime T(int s){ return iTime(_Symbol,_Period,s); } + +double IndVal(int handle,int buffer,int shift) +{ + double a[]; + if(CopyBuffer(handle,buffer,shift,1,a)==1) return a[0]; + return 0.0; +} + +// PINE ATR (RMA - Wilder) +double PineATR(int bar_index, int length) +{ + double rma = 0.0; + int start_bar = bar_index + 250; + for(int i = start_bar; i >= bar_index; i--) + { + double tr = MathMax(H(i)-L(i), MathMax(MathAbs(H(i)-C(i+1)), MathAbs(L(i)-C(i+1)))); + if(rma == 0.0) rma = tr; + else rma = (rma * (length - 1) + tr) / length; + } + return rma; +} + +// PINE STOCHASTIC (%K Smoothed con SMA directa) +double PineStoch(int bar_index, int length, int smooth) +{ + double sum_stoch = 0.0; + for(int s = 0; s < smooth; s++) + { + int current_b = bar_index + s; + double ll = L(current_b); + double hh = H(current_b); + for(int i = 0; i < length; i++) + { + ll = MathMin(ll, L(current_b + i)); + hh = MathMax(hh, H(current_b + i)); + } + double stoch = 0.0; + if(hh - ll != 0) stoch = 100.0 * (C(current_b) - ll) / (hh - ll); + sum_stoch += stoch; + } + return sum_stoch / smooth; +} + +int OnInit() +{ + pct_tp_ob = pct_tp_ob_in/100.0; + ObjectsDeleteAll(0,PREF); + + hRSI = iRSI(_Symbol,_Period,rsi_len,PRICE_CLOSE); + hADX = iADX(_Symbol,_Period,adx_len); + if(hRSI==INVALID_HANDLE || hADX==INVALID_HANDLE) + { + Print("Error creando handles de indicadores"); + return(INIT_FAILED); + } + + trade.SetExpertMagicNumber(InpMagic); + trade.SetTypeFillingBySymbol(_Symbol); + + g_equity_day0 = AccountInfoDouble(ACCOUNT_EQUITY); + MqlDateTime st; TimeToStruct(TimeCurrent(),st); + g_last_day = st.day; + + BuildTable(); + return(INIT_SUCCEEDED); +} + +void OnDeinit(const int reason) +{ + if(!MQLInfoInteger(MQL_TESTER) && InpDeleteOnExit) + ObjectsDeleteAll(0,PREF); +} + +bool InRange(int m,int a,int b){ return (m>=a && m=0;i--) + { + ulong tk=PositionGetTicket(i); + if(PositionSelectByTicket(tk)) + if(PositionGetString(POSITION_SYMBOL)==_Symbol && + PositionGetInteger(POSITION_MAGIC)==InpMagic) c++; + } + return c; +} + +double CalcLots(double entry,double sl) +{ + double riskAmt = AccountInfoDouble(ACCOUNT_EQUITY)*riesgo_per/100.0; + double slDist = MathAbs(entry-sl); + if(slDist<=0) return 0.0; + double tickVal = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE); + double tickSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE); + if(tickSize<=0 || tickVal<=0) return 0.0; + double lossPerLot = (slDist/tickSize)*tickVal; + if(lossPerLot<=0) return 0.0; + double lots = riskAmt/lossPerLot; + double step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); + double minv = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); + double maxv = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); + if(step>0) lots = MathFloor(lots/step)*step; + if(lotsmaxv) lots=maxv; + return lots; +} + +void AddBuyOB(double top,double bot,datetime lt,datetime rt) +{ + int n=ArraySize(buyOBs); ArrayResize(buyOBs,n+1); + string nm=PREF+"buyOB_"+(string)(g_objCnt++); + buyOBs[n].top=top; buyOBs[n].bot=bot; buyOBs[n].name=nm; + ObjectCreate(0,nm,OBJ_RECTANGLE,0,lt,top,rt,bot); + ObjectSetInteger(0,nm,OBJPROP_COLOR,clrGreen); + ObjectSetInteger(0,nm,OBJPROP_FILL,true); + ObjectSetInteger(0,nm,OBJPROP_BACK,true); + ObjectSetInteger(0,nm,OBJPROP_WIDTH,1); +} + +void AddSellOB(double top,double bot,datetime lt,datetime rt) +{ + int n=ArraySize(sellOBs); ArrayResize(sellOBs,n+1); + string nm=PREF+"sellOB_"+(string)(g_objCnt++); + sellOBs[n].top=top; sellOBs[n].bot=bot; sellOBs[n].name=nm; + ObjectCreate(0,nm,OBJ_RECTANGLE,0,lt,top,rt,bot); + ObjectSetInteger(0,nm,OBJPROP_COLOR,clrRed); + ObjectSetInteger(0,nm,OBJPROP_FILL,true); + ObjectSetInteger(0,nm,OBJPROP_BACK,true); + ObjectSetInteger(0,nm,OBJPROP_WIDTH,1); +} + +void RetireBuyOB(int i,bool triggered) +{ + if(InpKeepBoxes) + { + string nm = buyOBs[i].name; + ObjectSetInteger(0,nm,OBJPROP_TIME,1,T(0)); + ObjectSetInteger(0,nm,OBJPROP_FILL,false); + ObjectSetInteger(0,nm,OBJPROP_COLOR, triggered ? clrAqua : clrGray); + ObjectSetInteger(0,nm,OBJPROP_STYLE, triggered ? STYLE_SOLID : STYLE_DOT); + } + else ObjectDelete(0,buyOBs[i].name); + ArrayRemove(buyOBs,i,1); +} + +void RetireSellOB(int i) +{ + if(InpKeepBoxes) + { + string nm = sellOBs[i].name; + ObjectSetInteger(0,nm,OBJPROP_TIME,1,T(0)); + ObjectSetInteger(0,nm,OBJPROP_FILL,false); + ObjectSetInteger(0,nm,OBJPROP_COLOR,clrGray); + ObjectSetInteger(0,nm,OBJPROP_STYLE,STYLE_DOT); + } + else ObjectDelete(0,sellOBs[i].name); + ArrayRemove(sellOBs,i,1); +} + +void DrawSLTP(double sl_fijo,double tp_fijo) +{ + datetime t0 = T(1); + datetime t1 = t0 + (datetime)(20*PeriodSeconds()); + + string nSL = PREF+"SLline_"+(string)g_objCnt; + ObjectCreate(0,nSL,OBJ_TREND,0,t0,sl_fijo,t1,sl_fijo); + ObjectSetInteger(0,nSL,OBJPROP_COLOR,clrRed); + ObjectSetInteger(0,nSL,OBJPROP_WIDTH,2); + ObjectSetInteger(0,nSL,OBJPROP_STYLE,STYLE_DOT); + ObjectSetInteger(0,nSL,OBJPROP_RAY_RIGHT,false); + + string nTP = PREF+"TPline_"+(string)g_objCnt; + ObjectCreate(0,nTP,OBJ_TREND,0,t0,tp_fijo,t1,tp_fijo); + ObjectSetInteger(0,nTP,OBJPROP_COLOR,clrLime); + ObjectSetInteger(0,nTP,OBJPROP_WIDTH,2); + ObjectSetInteger(0,nTP,OBJPROP_STYLE,STYLE_DOT); + ObjectSetInteger(0,nTP,OBJPROP_RAY_RIGHT,false); + + string nSLt = PREF+"SLtxt_"+(string)g_objCnt; + ObjectCreate(0,nSLt,OBJ_TEXT,0,t1,sl_fijo); + ObjectSetString(0,nSLt,OBJPROP_TEXT,"SL: "+DoubleToString(sl_fijo,_Digits)); + ObjectSetInteger(0,nSLt,OBJPROP_COLOR,clrRed); + ObjectSetInteger(0,nSLt,OBJPROP_ANCHOR,ANCHOR_LEFT); + + string nTPt = PREF+"TPtxt_"+(string)g_objCnt; + ObjectCreate(0,nTPt,OBJ_TEXT,0,t1,tp_fijo); + ObjectSetString(0,nTPt,OBJPROP_TEXT,"TP: "+DoubleToString(tp_fijo,_Digits)); + ObjectSetInteger(0,nTPt,OBJPROP_COLOR,clrLime); + ObjectSetInteger(0,nTPt,OBJPROP_ANCHOR,ANCHOR_LEFT); + + g_objCnt++; +} + +void UpdateBoxesRight() +{ + datetime rt = T(0); + for(int i=0;i0)?tam_vela:1; + + double wick_top_pct = (H(S)-MathMax(O(S),C(S)))/denom*100.0; + double wick_bot_pct = (MathMin(O(S),C(S))-L(S))/denom*100.0; + bool wicks_ok = !Use_Wicks || + ((wick_top_pct>=min_wick_top && wick_top_pct<=max_wick_top) && + (wick_bot_pct>=min_wick_bot && wick_bot_pct<=max_wick_bot)); + + double body_pct = cuerpo_abs/denom*100.0; + bool cuerpo_solid_ok = !Use_Body || + (body_pct>=min_body_pct && body_pct<=max_body_pct); + bool vela_size_ok = !Use_VelaSize || + ((cuerpo_abs>=atr_v*min_atr_size) && (cuerpo_abs<=atr_v*max_atr_size)); + + double volsum=0.0; + for(int i=0;i=current_vol_ma*min_vol_mult) && + ((double)V(S)<=current_vol_ma*max_vol_mult)); + + double atr_sum=0.0; + for(int i=0;i0)?hl_diff:1))/MathLog10(chop_len); + bool chop_ok = (chop_val>=chop_min && chop_val<=chop_max); + + double rsi_v = IndVal(hRSI,0,S); + double rsi_v1 = IndVal(hRSI,0,S+1); + bool rsi_ok = ((rsi_v>=rsi_min && rsi_v<=rsi_max) || (rsi_v1>=rsi_min && rsi_v1<=rsi_max)); + + double adx_v = IndVal(hADX,0,S); + bool adx_ok = (adx_v>=adx_min && adx_v<=adx_max); + + bool filtros_ok = !Use_Osciladores || (chop_ok && adx_ok && rsi_ok); + + bool is_dead_buy = InpUseDeadHours && DeadBuy(T(S)); + bool is_dead_sell = InpUseDeadHours && DeadSell(T(S)); + + double inicio_impulso = L(S); + for(int i=0;i H(S+1)) g_final_impulso = H(S); + double nivel_descuento = inicio_impulso + (g_final_impulso - inicio_impulso)*pd_threshold; + bool en_descuento = (!use_pd_filter) || (L(S) <= nivel_descuento); + + double k_val = PineStoch(S, 14, 3); + bool st_confirm_buy = !Use_StochBuy || (k_val<=st_nivel_buy && k_val=st_nivel_sell); + + double equity = AccountInfoDouble(ACCOUNT_EQUITY); + MqlDateTime nowst; TimeToStruct(TimeCurrent(),nowst); + if(nowst.day != g_last_day){ g_equity_day0 = equity; g_last_day = nowst.day; } + bool stop_por_drawdown = ((g_equity_day0 - equity)/g_equity_day0)*100.0 >= max_daily_loss; + + datetime lt = T(S+1); + datetime rt = T(S); + + if(C(S) > H(S+1) && (C(S)-O(S)) > (atr_v*ob_mult_buy) && !is_dead_buy) + { + AddBuyOB(H(S+1), L(S+1), lt, rt); + if(InpDebug) + PrintFormat("[OB-BUY ] %s top=%s bot=%s atr=%s", + TimeToString(T(S),TIME_DATE|TIME_MINUTES), + DoubleToString(H(S+1),_Digits), DoubleToString(L(S+1),_Digits), + DoubleToString(atr_v,_Digits)); + } + + if(C(S) < L(S+1) && (O(S)-C(S)) > (atr_v*ob_mult_sell) && st_confirm_sell && !is_dead_sell) + { + AddSellOB(H(S+1), L(S+1), lt, rt); + if(InpDebug) + PrintFormat("[OB-SELL] %s top=%s bot=%s", + TimeToString(T(S),TIME_DATE|TIME_MINUTES), + DoubleToString(H(S+1),_Digits), DoubleToString(L(S+1),_Digits)); + } + + UpdateBoxesRight(); + + double target_tp_ob = 0.0; bool has_tp=false; + double min_dist = 1e10; + for(int j=0;j C(S) && (nivel_tp - C(S)) < min_dist) + { + min_dist = nivel_tp - C(S); + target_tp_ob = nivel_tp; has_tp=true; + } + } + + int actuales_ops = CountPositions(); + + for(int i=ArraySize(buyOBs)-1; i>=0; i--) + { + double b_top=buyOBs[i].top, b_bot=buyOBs[i].bot; + + bool touched = (L(S) <= b_top && L(S) >= (b_bot-(atr_v*ob_buy_off))); + + bool entrar = touched && cuerpo_solid_ok && vela_size_ok && vol_ok && + wicks_ok && st_confirm_buy && filtros_ok && + actuales_ops0?cuerpo_abs/atr_v:0), + vol_ok, (double)V(S), current_vol_ma, + wicks_ok, wick_top_pct, wick_bot_pct, + st_confirm_buy, k_val, + chop_ok, chop_val, adx_ok, adx_v, rsi_ok, + en_descuento, (actuales_ops0) trade.Buy(lots,_Symbol,0.0,sl_fijo,tp_fijo,"Long_"+(string)T(S)); + + RetireBuyOB(i,true); + break; + } + else if(C(S) < (b_bot - (atr_v*0.5))) + { + RetireBuyOB(i,false); + } + } + + for(int j=ArraySize(sellOBs)-1; j>=0; j--) + if(C(S) > sellOBs[j].top) RetireSellOB(j); + + BuildTable(); +} + +//==================================================================== +// ONTESTER - CRITERIOS MINIMOS PARA OPTIMIZACION (CUSTOM MAX) +// - Si NO cumple cualquier minimo -> devuelve 0 (descarta la corrida) +// - Si cumple todos -> maximiza el FACTOR DE RECUPERACION: +// recuperacion = ganancia_neta / DD_equity_en_dinero +// El DD de 20% se evalua sobre el DD RELATIVO de EQUITY (%). +//==================================================================== +double OnTester() +{ + // Metricas estandar del Probador + int trades = (int)TesterStatistics(STAT_TRADES); + double profit = TesterStatistics(STAT_PROFIT); // ganancia neta + double pf = TesterStatistics(STAT_PROFIT_FACTOR); // profit factor + double dd_pct = TesterStatistics(STAT_EQUITYDD_PERCENT); // DD relativo de equity (%) + double dd_money = TesterStatistics(STAT_EQUITY_DD); // DD de equity en dinero + double sharpe = TesterStatistics(STAT_SHARPE_RATIO); // Sharpe Ratio + double recovery = TesterStatistics(STAT_RECOVERY_FACTOR); // Recovery Factor + + if(InpDebug) + PrintFormat("[OnTester] trades=%d profit=%.2f PF=%.2f DDeq=%.2f%% DD$=%.2f Sharpe=%.2f Recovery=%.2f", + trades, profit, pf, dd_pct, dd_money, sharpe, recovery); + + // Si el filtro custom max esta desactivado, optimiza por recuperacion sin filtrar + if(!InpUseCustomMax) + return( dd_money>0.0 ? profit/dd_money : profit ); + + // --- FILTRO DURO: cualquier incumplimiento descarta la corrida --- + if(trades < InpMinTrades) return 0.0; + if(dd_pct > InpMaxDDpct) return 0.0; // DD de equity por encima del tope + if(pf < InpMinPF) return 0.0; + if(profit < InpMinProfit) return 0.0; + if(sharpe < InpMinSharpe) return 0.0; + if(recovery < InpMinRecovery) return 0.0; + + // --- METRICA A MAXIMIZAR: factor de recuperacion (ganancia / DD$) --- + // Mayor es mejor: mas ganancia por cada unidad de drawdown sufrido. + double recuperacion = (dd_money > 0.0) ? (profit / dd_money) : profit; + return recuperacion; +} +//+------------------------------------------------------------------+