//+------------------------------------------------------------------+ //| TradingLabS1.mqh | //| Copyright 2025, Leo. | //| https://www.mql5.com/es/users/nique_372/news | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Leo." #property link "https://www.mql5.com/es/users/nique_372/news" #property strict //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\..\\DefGlobal.mqh" #include #include #include #include //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /* Fuente: https://www.youtube.com/watch?v=Up-Vs2afNGs&t=278s | @InstitucionalTradingLab */ IBlocksGen* g_block; // Bloque fvg o tick CSwingBar* swing_externo; //15M CSessionManager> sesion; TSN::CRiskManagemet* risk; CLiquidity sweep; #define LIQ_TIPO_SWING 0 //--- Estados de la estrategia (en vez de flags) enum ENUM_S1_STATE { S1_STATE_BUSCAR_SWEEP, // esperando un sweep de liquidez (swing externo) //--- S1_STATE_BUSCAR_PDA_VENTA, // sweep al alza: se espera un bloque bajista mitigado para vender S1_STATE_BUSCAR_PDA_COMPRA, // sweep a la baja: se espera un bloque alcista mitigado para comprar }; //--- enum ENUM_BLOQUE_TO_USE { USE_OB, // Order block USE_FVG // Fvg }; //+------------------------------------------------------------------+ //| Estrategia que implementa el uso de la libreria ICTLIbrary | //+------------------------------------------------------------------+ class CEstrategia : public TSN::CStrategyBase { private: ENUM_S1_STATE m_state; // Estado actual de la estrategia int m_curr_idx; // Indice actual de la sesion ulong m_last_ticket; // Ultimo ticket abierto bool m_confirm; // Confirmar //--- void OpenOrder(ENUM_POSITION_TYPE type); void Clean(); //--- bool CheckSwingAlignment(); public: CEstrategia(ulong magic_number_, string symbol_, ENUM_TIMEFRAMES timeframe_ = PERIOD_CURRENT, long chart_id_ = 0, int subwindow_ = 0, ulong max_deviation_ = NO_MAX_DEVIATION_DEFINED); //--- void Set(bool usar_confirmacion_Extra); //--- void OnNewBar(const datetime curr_time) override final; void OnTick(const datetime curr_time) override final { } void OnInterupcion() override final; //--- inline ulong GetLastTicket() const { return m_last_ticket; } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CEstrategia::CEstrategia(ulong magic_number_, string symbol_, ENUM_TIMEFRAMES timeframe_ = PERIOD_CURRENT, long chart_id_ = 0, int subwindow_ = 0, ulong max_deviation_ = NO_MAX_DEVIATION_DEFINED) : CStrategyBase(magic_number_, symbol_, timeframe_, chart_id_, subwindow_, max_deviation_, "TradingLabS1"), m_last_ticket(INVALID_TICKET), m_state(S1_STATE_BUSCAR_SWEEP), m_curr_idx(INVALID_SESION) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CEstrategia::Set(bool usar_confirmacion_Extra) { m_confirm = usar_confirmacion_Extra; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CEstrategia::Clean(void) { m_state = S1_STATE_BUSCAR_SWEEP; m_curr_idx = INVALID_SESION; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CEstrategia::OnNewBar(const datetime curr_time) { if(risk.GetPositionsTotal() > 0) return; //--- Obtenemos el indice de la seison actual m_curr_idx = sesion.GetSesionActualIdx(); //--- Logica (un paso por vela, igual que EasySb) if(m_curr_idx != INVALID_SESION) { switch(m_state) { //--- Esperando un sweep de la liquidez, la direccion de la entrada es la contraria al sweep case S1_STATE_BUSCAR_SWEEP: { if(m_confirm && CheckSwingAlignment()) // Solo si hay confiramcion previa corremos el chek break; if(sweep.NuevaManipulacionAlAlza()) { LogInfo("Nuevo sweep arriba", FUNCION_ACTUAL); m_state = S1_STATE_BUSCAR_PDA_VENTA; } else if(sweep.NuevaManipulacionAlaBaja()) { LogInfo("Nuevo sweep abajo", FUNCION_ACTUAL); m_state = S1_STATE_BUSCAR_PDA_COMPRA; } break; } //--- Entrada de venta: se mitigo un bloque bajista case S1_STATE_BUSCAR_PDA_VENTA: { if(g_block.SizeBajistasMitigados() > 0) OpenOrder(POSITION_TYPE_SELL); break; } //--- Entrada de compra: se mitigo un bloque alcista case S1_STATE_BUSCAR_PDA_COMPRA: { if(g_block.SizeAlcistasMitigados() > 0) OpenOrder(POSITION_TYPE_BUY); break; } default: break; } } } //+------------------------------------------------------------------+ //| Open Order Default | //+------------------------------------------------------------------+ void CEstrategia::OpenOrder(ENUM_POSITION_TYPE type) { SymbolInfoTick(m_symbol, m_tick); if(type == POSITION_TYPE_BUY) { double entry = m_tick.ask; double sl = GetSL(entry, type, m_tick.time); double tp = GetTP(entry, type, m_tick.time); risk.SetStopLoss(m_symbol, entry - sl); double l = m_LOT_SIZE > 0.00 ? m_LOT_SIZE : risk.GetLote(ORDER_TYPE_BUY, entry, m_max_deviation, 0); if(m_trade.Buy(l, m_symbol, entry, sl, tp, "Ea buy")) m_last_ticket = m_trade.ResultOrder(); } else if(type == POSITION_TYPE_SELL) { double entry = m_tick.bid; double sl = GetSL(entry, type, m_tick.time); double tp = GetTP(entry, type, m_tick.time); risk.SetStopLoss(m_symbol, sl - entry); double l = m_LOT_SIZE > 0.00 ? m_LOT_SIZE : risk.GetLote(ORDER_TYPE_SELL, entry, m_max_deviation, 0); if(m_trade.Sell(l, m_symbol, entry, sl, tp, "Ea sell")) m_last_ticket = m_trade.ResultOrder(); } //--- Clean(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CEstrategia::CheckSwingAlignment() { // Get recent price data for external timeframe (M15) double externo_close_0 = iClose(m_symbol, PERIOD_M15, 0); double externo_close_1 = iClose(m_symbol, PERIOD_M15, 1); double externo_close_2 = iClose(m_symbol, PERIOD_M15, 2); // Get recent price data for mid timeframe (H1) double mid_close_0 = iClose(m_symbol, PERIOD_H1, 0); double mid_close_1 = iClose(m_symbol, PERIOD_H1, 1); double mid_close_2 = iClose(m_symbol, PERIOD_H1, 2); // Determine trend direction for external timeframe (M15) bool externo_trend_bullish = (externo_close_0 > externo_close_1) && (externo_close_1 > externo_close_2); bool externo_trend_bearish = (externo_close_0 < externo_close_1) && (externo_close_1 < externo_close_2); // Determine trend direction for mid timeframe (H1) bool mid_trend_bullish = (mid_close_0 > mid_close_1) && (mid_close_1 > mid_close_2); bool mid_trend_bearish = (mid_close_0 < mid_close_1) && (mid_close_1 < mid_close_2); // Only allow trades when both timeframes show clear directional alignment bool aligned_bullish = externo_trend_bullish && mid_trend_bullish; bool aligned_bearish = externo_trend_bearish && mid_trend_bearish; return (aligned_bullish || aligned_bearish); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CEstrategia::OnInterupcion() { g_block.SetParo(); sweep.SetParo(); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Liq_AddSwing() { const int idx_a = swing_externo.HayUnNuevoSwing(SWING_ALCISTA); const int idx_b = swing_externo.HayUnNuevoSwing(SWING_BAJISTA); //--- Nuevo, usamos directamente la funcion sobrecargada if(idx_a >= 0) { sweep.AddLiquidityLevel(swing_externo.GetSwingsAlcistas(idx_a), LIQ_TIPO_SWING, LIQUIDITY_BULL); } else if(idx_b >= 0) { sweep.AddLiquidityLevel(swing_externo.GetSwingsBasjitas(idx_b), LIQ_TIPO_SWING, LIQUIDITY_BEAR); } } //+------------------------------------------------------------------+