Add Order Blocks EA v1.07
This commit is contained in:
committed by
GitHub
parent
3e6000efef
commit
0cfe101e74
@@ -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 <Trade\Trade.mqh>
|
||||
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<b); }
|
||||
|
||||
bool DeadBuy(datetime tbar)
|
||||
{
|
||||
datetime gmt5 = tbar - (datetime)((InpBrokerGMT+5)*3600);
|
||||
MqlDateTime s; TimeToStruct(gmt5,s);
|
||||
int m = s.hour*60 + s.min;
|
||||
return( InRange(m,45,90) || InRange(m,1035,1140) );
|
||||
}
|
||||
|
||||
bool DeadSell(datetime tbar)
|
||||
{
|
||||
datetime gmt5 = tbar - (datetime)((InpBrokerGMT+5)*3600);
|
||||
MqlDateTime s; TimeToStruct(gmt5,s);
|
||||
int m = s.hour*60 + s.min;
|
||||
return( InRange(m,135,210) || InRange(m,945,1080) );
|
||||
}
|
||||
|
||||
int CountPositions()
|
||||
{
|
||||
int c=0;
|
||||
for(int i=PositionsTotal()-1;i>=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(lots<minv) lots=minv;
|
||||
if(lots>maxv) 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;i<ArraySize(buyOBs);i++) ObjectSetInteger(0,buyOBs[i].name,OBJPROP_TIME,1,rt);
|
||||
for(int j=0;j<ArraySize(sellOBs);j++) ObjectSetInteger(0,sellOBs[j].name,OBJPROP_TIME,1,rt);
|
||||
}
|
||||
|
||||
void Lbl(string id,int row,string txt,color col)
|
||||
{
|
||||
string nm=PREF+"tbl_"+id;
|
||||
if(ObjectFind(0,nm)<0) ObjectCreate(0,nm,OBJ_LABEL,0,0,0);
|
||||
ObjectSetInteger(0,nm,OBJPROP_CORNER,CORNER_RIGHT_UPPER);
|
||||
ObjectSetInteger(0,nm,OBJPROP_XDISTANCE,10);
|
||||
ObjectSetInteger(0,nm,OBJPROP_YDISTANCE,12+row*18);
|
||||
ObjectSetInteger(0,nm,OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER);
|
||||
ObjectSetString(0,nm,OBJPROP_TEXT,txt);
|
||||
ObjectSetInteger(0,nm,OBJPROP_COLOR,col);
|
||||
ObjectSetInteger(0,nm,OBJPROP_FONTSIZE,9);
|
||||
}
|
||||
|
||||
void BuildTable()
|
||||
{
|
||||
string bg=PREF+"tbl_bg";
|
||||
if(ObjectFind(0,bg)<0) ObjectCreate(0,bg,OBJ_RECTANGLE_LABEL,0,0,0);
|
||||
ObjectSetInteger(0,bg,OBJPROP_CORNER,CORNER_RIGHT_UPPER);
|
||||
ObjectSetInteger(0,bg,OBJPROP_XDISTANCE,200);
|
||||
ObjectSetInteger(0,bg,OBJPROP_YDISTANCE,5);
|
||||
ObjectSetInteger(0,bg,OBJPROP_XSIZE,195);
|
||||
ObjectSetInteger(0,bg,OBJPROP_YSIZE,80);
|
||||
ObjectSetInteger(0,bg,OBJPROP_BGCOLOR,clrBlack);
|
||||
ObjectSetInteger(0,bg,OBJPROP_BORDER_TYPE,BORDER_FLAT);
|
||||
ObjectSetInteger(0,bg,OBJPROP_COLOR,clrBlack);
|
||||
|
||||
Lbl("dl",0,"DAILY LOSS CAP: "+DoubleToString(max_daily_loss,1)+"%",clrRed);
|
||||
Lbl("rk",1,"RISK: "+DoubleToString(riesgo_per,1)+"%",clrOrange);
|
||||
Lbl("wr",2,"WIN RATE: 62.75%",clrLime);
|
||||
Lbl("ds",3,"DISCOUNT: "+DoubleToString(pd_threshold*100,1)+"%",clrYellow);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
datetime t0 = iTime(_Symbol,_Period,0);
|
||||
if(t0==g_lastBarTime) return;
|
||||
g_lastBarTime = t0;
|
||||
OnNewBar();
|
||||
}
|
||||
|
||||
void OnNewBar()
|
||||
{
|
||||
int needBars = MathMax(chop_len, MathMax(vol_ma_len, pd_lookback)) + 252;
|
||||
if(Bars(_Symbol,_Period) < needBars) return;
|
||||
|
||||
int S = 1;
|
||||
|
||||
double atr_v = PineATR(S, atr_len);
|
||||
double cuerpo_abs = MathAbs(C(S)-O(S));
|
||||
double tam_vela = H(S)-L(S);
|
||||
double denom = (tam_vela>0)?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<vol_ma_len;i++) volsum += (double)V(S+i);
|
||||
double current_vol_ma = volsum/vol_ma_len;
|
||||
bool vol_ok = !Use_Volumen ||
|
||||
(((double)V(S)>=current_vol_ma*min_vol_mult) &&
|
||||
((double)V(S)<=current_vol_ma*max_vol_mult));
|
||||
|
||||
double atr_sum=0.0;
|
||||
for(int i=0;i<chop_len;i++)
|
||||
{
|
||||
int sh=S+i;
|
||||
double tr = MathMax(H(sh)-L(sh), MathMax(MathAbs(H(sh)-C(sh+1)), MathAbs(L(sh)-C(sh+1))));
|
||||
atr_sum += tr;
|
||||
}
|
||||
double hh=H(S), ll=L(S);
|
||||
for(int i=0;i<chop_len;i++){ hh=MathMax(hh,H(S+i)); ll=MathMin(ll,L(S+i)); }
|
||||
double hl_diff = hh-ll;
|
||||
double chop_val = 100.0*MathLog10(atr_sum/((hl_diff>0)?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<pd_lookback;i++) inicio_impulso = MathMin(inicio_impulso,L(S+i));
|
||||
if(C(S) > 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_conf_th);
|
||||
bool st_confirm_sell = (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<ArraySize(sellOBs);j++)
|
||||
{
|
||||
double s_top=sellOBs[j].top, s_bot=sellOBs[j].bot;
|
||||
double nivel_tp = s_bot + (s_top - s_bot)*pct_tp_ob;
|
||||
if(nivel_tp > 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_ops<max_ops && en_descuento && !stop_por_drawdown;
|
||||
|
||||
if(InpDebug && touched && !entrar)
|
||||
PrintFormat("[RECHAZO] %s OBbot=%s | cuerpo=%d vela=%d(body%%=%.1f sz/atr=%.2f) vol=%d(v=%.0f ma=%.0f) wicks=%d(t%%=%.1f b%%=%.1f) stoch=%d(k=%.1f) chop=%d(%.1f) adx=%d(%.1f) rsi=%d desc=%d ops=%d dd=%d",
|
||||
TimeToString(T(S),TIME_DATE|TIME_MINUTES), DoubleToString(b_bot,_Digits),
|
||||
cuerpo_solid_ok, vela_size_ok, body_pct, (atr_v>0?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_ops<max_ops), !stop_por_drawdown);
|
||||
|
||||
if(entrar)
|
||||
{
|
||||
double sl_fijo = b_bot - (atr_v*sl_atr_mult);
|
||||
double tp_fijo = has_tp ? target_tp_ob : C(S) + (C(S)-sl_fijo)*tp_ratio;
|
||||
|
||||
DrawSLTP(sl_fijo,tp_fijo);
|
||||
|
||||
if(InpDebug)
|
||||
PrintFormat("[ENTRADA] %s entry~%s sl=%s tp=%s",
|
||||
TimeToString(T(S),TIME_DATE|TIME_MINUTES), DoubleToString(C(S),_Digits),
|
||||
DoubleToString(sl_fijo,_Digits), DoubleToString(tp_fijo,_Digits));
|
||||
|
||||
double lots = CalcLots(C(S), sl_fijo);
|
||||
if(lots>0) 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;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user