ICTLibraryExamples/TradingLab/TradingLabS1/TradingLabS1.mqh
268 lines
9.7 KiB
MQL5
Nique_372
2025-11-26 07:19:36 -05:00
//+------------------------------------------------------------------+
//| 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
Nique_372
2026-11-01 22:12:14 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Nique_372
2026-02-01 12:28:19 -05:00
#include "..\\..\\DefGlobal.mqh"
Nique_372
2026-01-02 10:21:37 -05:00
#include <TSN\\ICTLibrary\\Liquidity.mqh>
#include <TSN\\MQLArticles\\Strategy\\Main.mqh>
#include <TSN\\MQLArticles\\PosMgmt\\Trailing Stop.mqh>
Nique_372
2026-11-01 22:12:14 -05:00
#include <TSN\\MQLArticles\\RM\\RiskManagement.mqh>
Nique_372
2025-11-26 07:19:36 -05:00
Nique_372
2026-11-01 22:12:14 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Nique_372
2025-11-26 07:19:36 -05:00
/*
Fuente: https://www.youtube.com/watch?v=Up-Vs2afNGs&t=278s | @InstitucionalTradingLab
*/
IBlocksGen* g_block; // Bloque fvg o tick
CSwingBar* swing_externo; //15M
Nique_372
2025-12-27 19:14:26 -05:00
CSessionManager<CTradingSession, ICTContainer<CTradingSession>> sesion;
Nique_372
2026-10-23 21:51:38 -05:00
TSN::CRiskManagemet* risk;
Nique_372
2025-11-26 07:19:36 -05:00
CLiquidity sweep;
#define LIQ_TIPO_SWING 0
Nique_372
2026-11-06 15:36:06 -05:00
//--- 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
};
Nique_372
2025-11-26 07:19:36 -05:00
//---
enum ENUM_BLOQUE_TO_USE
{
USE_OB, // Order block
USE_FVG // Fvg
};
//+------------------------------------------------------------------+
//| Estrategia que implementa el uso de la libreria ICTLIbrary |
//+------------------------------------------------------------------+
Nique_372
2026-10-23 21:51:38 -05:00
class CEstrategia : public TSN::CStrategyBase
Nique_372
2025-11-26 07:19:36 -05:00
{
private:
Nique_372
2026-11-06 15:36:06 -05:00
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
Nique_372
2025-11-26 07:19:36 -05:00
//---
void OpenOrder(ENUM_POSITION_TYPE type);
void Clean();
//---
Nique_372
2026-11-06 15:36:06 -05:00
bool CheckSwingAlignment();
Nique_372
2025-11-26 07:19:36 -05:00
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);
Nique_372
2026-11-06 15:36:06 -05:00
//---
Nique_372
2025-11-26 07:19:36 -05:00
void Set(bool usar_confirmacion_Extra);
Nique_372
2026-11-06 15:36:06 -05:00
//---
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; }
Nique_372
2025-11-26 07:19:36 -05:00
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
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)
Nique_372
2026-11-06 15:36:06 -05:00
: 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)
Nique_372
2025-11-26 07:19:36 -05:00
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CEstrategia::Set(bool usar_confirmacion_Extra)
{
Nique_372
2026-11-06 15:36:06 -05:00
m_confirm = usar_confirmacion_Extra;
Nique_372
2025-11-26 07:19:36 -05:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CEstrategia::Clean(void)
{
Nique_372
2026-11-06 15:36:06 -05:00
m_state = S1_STATE_BUSCAR_SWEEP;
m_curr_idx = INVALID_SESION;
Nique_372
2025-11-26 07:19:36 -05:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Nique_372
2026-02-14 17:50:28 -05:00
void CEstrategia::OnNewBar(const datetime curr_time)
Nique_372
2025-11-26 07:19:36 -05:00
{
if(risk.GetPositionsTotal() > 0)
return;
//--- Obtenemos el indice de la seison actual
Nique_372
2026-11-06 15:36:06 -05:00
m_curr_idx = sesion.GetSesionActualIdx();
Nique_372
2025-11-26 07:19:36 -05:00
Nique_372
2026-11-06 15:36:06 -05:00
//--- Logica (un paso por vela, igual que EasySb)
if(m_curr_idx != INVALID_SESION)
Nique_372
2025-11-26 07:19:36 -05:00
{
Nique_372
2026-11-06 15:36:06 -05:00
switch(m_state)
Nique_372
2025-11-26 07:19:36 -05:00
{
Nique_372
2026-11-06 15:36:06 -05:00
//--- Esperando un sweep de la liquidez, la direccion de la entrada es la contraria al sweep
case S1_STATE_BUSCAR_SWEEP:
Nique_372
2025-11-26 07:19:36 -05:00
{
Nique_372
2026-11-06 15:36:06 -05:00
if(m_confirm && CheckSwingAlignment()) // Solo si hay confiramcion previa corremos el chek
break;
if(sweep.NuevaManipulacionAlAlza())
Nique_372
2025-11-26 07:19:36 -05:00
{
Nique_372
2026-11-06 15:36:06 -05:00
LogInfo("Nuevo sweep arriba", FUNCION_ACTUAL);
m_state = S1_STATE_BUSCAR_PDA_VENTA;
Nique_372
2025-11-26 07:19:36 -05:00
}
Nique_372
2026-11-06 15:36:06 -05:00
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:
Nique_372
2025-11-26 07:19:36 -05:00
{
Nique_372
2026-11-06 15:36:06 -05:00
if(g_block.SizeBajistasMitigados() > 0)
OpenOrder(POSITION_TYPE_SELL);
break;
Nique_372
2025-11-26 07:19:36 -05:00
}
Nique_372
2026-11-06 15:36:06 -05:00
//--- 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;
}
Nique_372
2025-11-26 07:19:36 -05:00
}
}
//+------------------------------------------------------------------+
//| 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);
Nique_372
2026-11-01 22:12:14 -05:00
risk.SetStopLoss(m_symbol, entry - sl);
Nique_372
2025-11-26 07:19:36 -05:00
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"))
Nique_372
2026-11-06 15:36:06 -05:00
m_last_ticket = m_trade.ResultOrder();
Nique_372
2025-11-26 07:19:36 -05:00
}
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);
Nique_372
2026-11-01 22:12:14 -05:00
risk.SetStopLoss(m_symbol, sl - entry);
Nique_372
2025-11-26 07:19:36 -05:00
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"))
Nique_372
2026-11-06 15:36:06 -05:00
m_last_ticket = m_trade.ResultOrder();
Nique_372
2025-11-26 07:19:36 -05:00
}
Nique_372
2026-11-06 15:36:06 -05:00
//---
Nique_372
2025-11-26 07:19:36 -05:00
Clean();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Nique_372
2026-11-06 15:36:06 -05:00
bool CEstrategia::CheckSwingAlignment()
Nique_372
2025-11-26 07:19:36 -05:00
{
// Get recent price data for external timeframe (M15)
Nique_372
2026-11-06 15:36:06 -05:00
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);
Nique_372
2025-11-26 07:19:36 -05:00
// Get recent price data for mid timeframe (H1)
Nique_372
2026-11-06 15:36:06 -05:00
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);
Nique_372
2025-11-26 07:19:36 -05:00
// 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);
}
Nique_372
2026-11-06 15:36:06 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CEstrategia::OnInterupcion()
{
g_block.SetParo();
sweep.SetParo();
}
//+------------------------------------------------------------------+
Nique_372
2025-11-26 07:19:36 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
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);
}
}
//+------------------------------------------------------------------+