112 lines
4.5 KiB
Text
112 lines
4.5 KiB
Text
|
//+------------------------------------------------------------------+
|
||
|
//| SMC_FVG_Liquidity_EA.mq5 |
|
||
|
//+------------------------------------------------------------------+
|
||
|
#property strict
|
||
|
input double RiskPct = 0.5; // % riesgo por trade
|
||
|
input ENUM_TIMEFRAMES HTF = PERIOD_H1; // Marco para bias
|
||
|
input ENUM_TIMEFRAMES LTF = PERIOD_M5; // Marco para FVG/entrada
|
||
|
input int FVGLookback = 100; // Velas a escanear LTF
|
||
|
input int StopBufferPoints = 30; // Colchón extra para SL
|
||
|
input bool AllowLong = true;
|
||
|
input bool AllowShort = true;
|
||
|
|
||
|
// --- utilidades
|
||
|
double GetAccountRiskValue(){ return(AccountInfoDouble(ACCOUNT_BALANCE) * RiskPct/100.0); }
|
||
|
double PipToPoints(double pips){ return(pips * (_Point==0.00001 || _Point==0.001 ? 10 : 1)); }
|
||
|
|
||
|
// --- bias HTF simple (puedes mejorar con BOS/CHoCH)
|
||
|
int BiasHTF() {
|
||
|
// 1 = alcista, -1 = bajista, 0 = neutro
|
||
|
MqlRates r[]; if(!CopyRates(_Symbol, HTF, 0, 200, r)) return 0;
|
||
|
if(r[1].close > r[1].open && r[2].close > r[2].open) return 1;
|
||
|
if(r[1].close < r[1].open && r[2].close < r[2].open) return -1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// --- detección básica de FVG (tres velas): gap entre vela1.high y vela3.low (ventas) o vela1.low y vela3.high (compras)
|
||
|
bool FindFVG(int direction, double &fvgTop, double &fvgBottom){
|
||
|
MqlRates c[]; if(!CopyRates(_Symbol, LTF, 0, FVGLookback, c)) return false;
|
||
|
ArraySetAsSeries(c,true);
|
||
|
for(int i=2; i<FVGLookback; i++){
|
||
|
double prevHigh = c[i].high, prevLow = c[i].low;
|
||
|
double midHigh = c[i-1].high, midLow = c[i-1].low;
|
||
|
double nextHigh = c[i-2].high, nextLow = c[i-2].low;
|
||
|
|
||
|
if(direction<0){ // short: hueco bajista (prevHigh < nextLow)
|
||
|
if(prevHigh < nextLow){
|
||
|
fvgTop = nextLow; fvgBottom = prevHigh; return true;
|
||
|
}
|
||
|
} else if(direction>0){ // long: hueco alcista (prevLow > nextHigh)
|
||
|
if(prevLow > nextHigh){
|
||
|
fvgTop = prevLow; fvgBottom = nextHigh; return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// --- barrida simple: rompe el máximo/mínimo previo reciente y regresa
|
||
|
bool LiquiditySweep(int direction){
|
||
|
MqlRates c[]; if(!CopyRates(_Symbol, LTF, 0, 50, c)) return false;
|
||
|
ArraySetAsSeries(c,true);
|
||
|
double recentHigh = c[10].high, recentLow = c[10].low;
|
||
|
for(int i=1;i<=10;i++){ recentHigh = MathMax(recentHigh, c[i].high); recentLow = MathMin(recentLow, c[i].low); }
|
||
|
if(direction<0){ // short: barrió arriba y vuelve debajo
|
||
|
return (iHigh(_Symbol,LTF,1) > recentHigh && Close[0] < recentHigh);
|
||
|
} else if(direction>0){ // long: barrió abajo y vuelve encima
|
||
|
return (iLow(_Symbol,LTF,1) < recentLow && Close[0] > recentLow);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// --- tamaño de lote por SL en puntos
|
||
|
double CalcLotBySL(double slPrice){
|
||
|
double risk = GetAccountRiskValue();
|
||
|
double slPoints = MathAbs((slPrice - SymbolInfoDouble(_Symbol,SYMBOL_BID))/_Point);
|
||
|
double tickValue = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE);
|
||
|
double tickSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
|
||
|
if(slPoints<=0 || tickValue<=0 || tickSize<=0) return 0.0;
|
||
|
double valuePerPointPerLot = tickValue / (tickSize/_Point);
|
||
|
double lot = risk / (slPoints * valuePerPointPerLot);
|
||
|
double minLot = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
|
||
|
double step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
|
||
|
// normaliza
|
||
|
lot = MathMax(minLot, MathFloor(lot/step)*step);
|
||
|
return lot;
|
||
|
}
|
||
|
|
||
|
// --- ejecución
|
||
|
void TryEntry(){
|
||
|
int bias = BiasHTF(); if(bias==0) return;
|
||
|
|
||
|
double fvgTop, fvgBottom;
|
||
|
if(!FindFVG(bias, fvgTop, fvgBottom)) return;
|
||
|
if(!LiquiditySweep(bias)) return;
|
||
|
|
||
|
// definimos precio de entrada en el borde del FVG
|
||
|
double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
|
||
|
double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
||
|
|
||
|
if(bias<0 && AllowShort){
|
||
|
double entry = fvgBottom; // vender en rebote al borde inferior del FVG bajista
|
||
|
double sl = fvgTop + StopBufferPoints*_Point;
|
||
|
double lot = CalcLotBySL(sl);
|
||
|
if(lot>0 && bid>=entry){ // condición simple, podrías usar órdenes pendientes
|
||
|
trade.Sell(lot,_Symbol,bid,sl,0,"SMC short");
|
||
|
}
|
||
|
}
|
||
|
if(bias>0 && AllowLong){
|
||
|
double entry = fvgTop; // comprar en rebote al borde superior del FVG alcista
|
||
|
double sl = fvgBottom - StopBufferPoints*_Point;
|
||
|
double lot = CalcLotBySL(sl);
|
||
|
if(lot>0 && ask<=entry){
|
||
|
trade.Buy(lot,_Symbol,ask,sl,0,"SMC long");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#include <Trade/Trade.mqh>
|
||
|
CTrade trade;
|
||
|
|
||
|
int OnInit(){ return(INIT_SUCCEEDED); }
|
||
|
void OnTick(){ TryEntry(); }
|