//+------------------------------------------------------------------+ //| Ea.mq5 | //| Copyright 2025, Niquel Mendoza. | //| https://www.mql5.com/es/users/nique_372 | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Niquel Mendoza." #property link "https://www.mql5.com/es/users/nique_372" #property version "1.00" #property strict //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include "Main.mqh" //+------------------------------------------------------------------+ //| Input | //+------------------------------------------------------------------+ sinput group "----------| General |----------" input ulong InpMagic = 35465; // Magic Number input bool InpModifiedChart = true; // Modificar el grafico? sinput group "" sinput group "-------| Strategy |-------" input ENUM_VERBOSE_LOG_LEVEL InpStrategyLogLevel = VERBOSE_LOG_LEVEL_CAUTION; // Strategy log level sinput group "-----| TP/SL |-----" input ENUM_TYPE_TP_SL_FIXED InpStrategyTypeTpSl = TP_SL_ATR; // Tipo de TP/SL sinput group "-- TP SL by Point" input long InpStrategySlPoint = 250; // Stop Loss en puntos input long InpStrategyTpPoint = 500; // Take Profit en puntos sinput group "-- TP SL by ATR" input double InpStrategyAtrMultiplierTp = 2.0; // Multiplicador del ATR para el TP input double InpStrategyAtrMultiplierSl = 2.0; // Multiplicador del ATR para el SL sinput group "-----| Ordenes |-----" input ulong InpStrategyMaxDeviationOrders = 100; //Maxima desviacion/slípagge de las ordenes en puntos sinput group "" sinput group "-------| Account Status |-------" input ENUM_VERBOSE_LOG_LEVEL InpAccountStatusLogLevel = VERBOSE_LOG_LEVEL_ERROR_ONLY; //(Account Status|Ticket Mangement) log level: sinput group "" sinput group "-------| Risk Management |-------" input ENUM_LOTE_TYPE InpRmLoteType = Dinamico; //Lote Type: input double InpRmLote = 0.1; //Lot size (only for fixed lot) input ENUM_MODE_RISK_MANAGEMENT InpRmRiskMode = risk_mode_personal_account; //type of g_risk management mode input ENUM_GET_LOT InpRmGetMode = GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION; //How to get the lot input double InpRmPropFirmBalance = 0; //If g_risk mode is Prop Firm FTMO, then put your ftmo account balance input ENUM_VERBOSE_LOG_LEVEL InpRmLogLevel = VERBOSE_LOG_LEVEL_ERROR_ONLY; //Risk Management log level: sinput group "- ML/Maximum loss/Maximum loss -" input double InpRmPercentageOrMoneyMl = 0; //percentage or money (0 => not used ML) input ENUM_RISK_CALCULATION_MODE InpRmModeCalculationMl = percentage; //Mode calculation Max Loss input ENUM_APPLIED_PERCENTAGES InpRmAppliedPercentagesMl = Balance; //ML percentage applies to: sinput group "- MWL/Maximum weekly loss/Maximum weekly loss -" input double InpRmPercentageOrMoneyMwl = 0; //percentage or money (0 => not used MWL) input ENUM_RISK_CALCULATION_MODE InpRmModeCalculationMwl = percentage; //Mode calculation Max weekly Loss input ENUM_APPLIED_PERCENTAGES InpRmAppliedPercentagesMwl = Balance;//MWL percentage applies to: sinput group "- MDL/Maximum daily loss/Maximum daily loss -" input double InpRmPercentageOrMoneyMdl = 3.0; //percentage or money (0 => not used MDL) input ENUM_RISK_CALCULATION_MODE InpRmModeCalculationMdl = percentage; //Mode calculation Max daily loss input ENUM_APPLIED_PERCENTAGES InpRmAppliedPercentagesMdl = Balance;//MDL percentage applies to: sinput group "- GMLPO/Gross maximum loss per CanTradetion/Percentage to g_risk per CanTradetion -" input ENUM_OF_DYNAMIC_MODES_OF_GMLPO InpRmModeGmlpo = NO_DYNAMIC_GMLPO; //Select GMLPO mode: input double InpRmPercentageOrMoneyGmlpo = 2.0; //percentage or money (0 => not used GMLPO) input ENUM_RISK_CALCULATION_MODE InpRmModeCalculationGmlpo = percentage; //Mode calculation Max Loss per CanTradetion input ENUM_APPLIED_PERCENTAGES InpRmAppliedPercentagesGmlpo = Balance;//GMPLO percentage applies to: sinput group "-- Optional GMLPO settings, Dynamic GMLPO" sinput group "--- Full customizable dynamic GMLPO" input string InpRmNote1 = "subtracted from your total balance to establish a threshold."; //This parameter determines a specific percentage that will be input string InpRmStrPercentagesToBeReviewed = "15,30,50"; //percentages separated by commas. input string InpRmNote2 = "a new g_risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold input string InpRmStrPercentagesToApply = "10,20,25"; //percentages separated by commas. input string InpRmNote3 = "0 in both parameters => do not use dynamic g_risk in gmlpo"; //Note: sinput group "--- Fixed dynamic GMLPO with parameters" sinput group "- 1 -" input string InpRmNote11 = "subtracted from your total balance to establish a threshold."; //This parameter determines a specific percentage that will be input double InpRmBalancePercentageToActivateTheRisk1 = 2.0; //percentage 1 that will be exceeded to modify the g_risk separated by commas input string InpRmNote21 = "a new g_risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold input double InpRmPercentageToBeModified1 = 1.0;//new percentage 1 to which the gmlpo is modified sinput group "- 2 -" input double InpRmBalancePercentageToActivateTheRisk2 = 5.0;//percentage 2 that will be exceeded to modify the g_risk separated by commas input double InpRmPercentageToBeModified2 = 0.7;//new percentage 2 to which the gmlpo is modified sinput group "- 3 -" input double InpRmBalancePercentageToActivateTheRisk3 = 7.0;//percentage 3 that will be exceeded to modify the g_risk separated by commas input double InpRmPercentageToBeModified3 = 0.5;//new percentage 3 to which the gmlpo is modified sinput group "- 4 -" input double InpRmBalancePercentageToActivateTheRisk4 = 9.0;//percentage 4 that will be exceeded to modify the g_risk separated by commas input double InpRmPercentageToBeModified4 = 0.33;//new percentage 4 1 to which the gmlpo is modified sinput group "-- MDP/Maximum daily profit/Maximum daily profit --" input bool InpRmMdpIsStrict = true; //MDP is strict? input double InpRmPercentageOrMoneyMdp = 11.0; //percentage or money (0 => not used MDP) input ENUM_RISK_CALCULATION_MODE InpRmModeCalculationMdp = percentage; //Mode calculation Max Daily Profit input ENUM_APPLIED_PERCENTAGES InpRmAppliedPercentagesMdp = Balance;//MDP percentage applies to: //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ CEstrategia g_strategy(InpMagic, _Symbol, _Period, 0, 0, InpStrategyMaxDeviationOrders); //--- int8_t g_idx_controler_w1 = INVALID_INDEX; int8_t g_idx_controler_curr = INVALID_INDEX; int8_t g_idx_bar_controlerd1 = INVALID_INDEX; int8_t g_idx_bar_mn1 = INVALID_INDEX; //--- bool g_new_day = false; bool g_can_trade = true; //--- const CLossProfitManager* g_loss_profit_manager; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Registramos timeframes g_new_bar_manager.GetPosExecute(PERIOD_D1, g_idx_bar_controlerd1); g_new_bar_manager.GetPosExecute(_Period, g_idx_controler_curr); g_new_bar_manager.GetPosExecute(PERIOD_W1, g_idx_controler_w1); g_new_bar_manager.GetPosExecute(PERIOD_MN1, g_idx_bar_mn1); //--- Modifamos el grafico si se permite if(InpModifiedChart) { long chart_id = ChartID(); // Obtén el ID del gráfico actual // Ocultar la cuadrícula ChartSetInteger(chart_id, CHART_SHOW_GRID, false); ChartSetInteger(chart_id, CHART_COLOR_BACKGROUND, C'255,255,255'); // Fondo blanco ChartSetInteger(chart_id, CHART_COLOR_FOREGROUND, clrBlack); // Texto y escalas negras ChartSetInteger(chart_id, CHART_COLOR_CHART_UP, C'38,166,154'); // Velas alcistas color verde azulado ChartSetInteger(chart_id, CHART_COLOR_CHART_DOWN, C'239,83,80'); // Velas bajistas color rojo ChartSetInteger(chart_id, CHART_COLOR_CANDLE_BULL, C'38,166,154'); // Cuerpo de las velas alcistas ChartSetInteger(chart_id, CHART_COLOR_CANDLE_BEAR, C'239,83,80'); // Cuerpo de las velas bajistas ChartSetInteger(chart_id, CHART_COLOR_CHART_LINE, clrDarkSeaGreen); // Línea de tendencia ChartSetInteger(chart_id, CHART_COLOR_VOLUME, C'38,166,154'); // Color de los volúmenes ChartSetInteger(chart_id, CHART_COLOR_BID, C'38,166,154'); // Línea de oferta ChartSetInteger(chart_id, CHART_COLOR_ASK, C'239,83,80'); // Línea de demanda ChartSetInteger(chart_id, CHART_COLOR_LAST, C'156,186,240'); // Último precio ChartSetInteger(chart_id, CHART_COLOR_STOP_LEVEL, C'239,83,80'); // Líneas de stop ChartSetInteger(chart_id, CHART_SHOW_VOLUMES, true); // Actualizar el gráfico ChartRedraw(chart_id); } //--- Conceptos // Order block g_fvg.Create(_Symbol, _Period); g_fvg.SetGrapich(clrGreen, clrRed, clrGreen, clrRed, STYLE_SOLID, true, 1, true, 15, 1, STYLE_DASHDOT); g_fvg.Set(250, defined_pattern, fvg_1_vela, CreateDiffInstance(MODE_DIFF_BY_FIXED_POITNS, _Symbol, NULL, 1, 0.00)); //--- Estrategia // Atr Ultra CAtrUltraOptimized* atr_ultra = new CAtrUltraOptimized(); atr_ultra.SetVariables(PERIOD_CURRENT, _Symbol, 0, 14); atr_ultra.SetInternalPointer(); // Strategy g_strategy.AddLogFlags(InpStrategyLogLevel); g_strategy.SetAtrTP_SL(atr_ultra, InpStrategyAtrMultiplierTp, InpStrategyAtrMultiplierSl); g_strategy.SetOperateMode(TR_BUY_SELL, InpStrategyTypeTpSl); g_strategy.SetTP_SL(InpStrategySlPoint, InpStrategyTpPoint); account_status.AddItemFast(&g_strategy); if(InpRmLoteType == Fijo) g_strategy.FixedLotSize(InpRmLote); //--- Gestion de riesgo CRiskPointer* manager = new CRiskPointer(InpMagic, InpRmGetMode); manager.SetPropirm(InpRmPropFirmBalance); g_risk = manager.GetRiskPointer(InpRmRiskMode); g_risk.AddLogFlags(InpRmLogLevel); g_risk.SetLote(CreateLotePtr(_Symbol)); // We set the parameters string to_apply = InpRmStrPercentagesToApply, to_modfied = InpRmStrPercentagesToBeReviewed; if(InpRmModeGmlpo == DYNAMIC_GMLPO_FIXED_PARAMETERS) SetDynamicUsingFixedParameters(InpRmBalancePercentageToActivateTheRisk1, InpRmBalancePercentageToActivateTheRisk2, InpRmBalancePercentageToActivateTheRisk3 , InpRmBalancePercentageToActivateTheRisk4, InpRmPercentageToBeModified1, InpRmPercentageToBeModified2, InpRmPercentageToBeModified3, InpRmPercentageToBeModified4 , to_modfied, to_apply); g_risk.AddLoss(InpRmPercentageOrMoneyMdl, InpRmAppliedPercentagesMdl, InpRmModeCalculationMdl, LP_MDL, true); g_risk.AddLoss(InpRmPercentageOrMoneyGmlpo, InpRmAppliedPercentagesGmlpo, InpRmModeCalculationGmlpo, LP_GMLPO, true, (InpRmModeGmlpo != NO_DYNAMIC_GMLPO), to_modfied, to_apply); g_risk.AddLoss(InpRmPercentageOrMoneyMl, InpRmAppliedPercentagesMl, InpRmModeCalculationMl, LP_ML, true); g_risk.AddLoss(InpRmPercentageOrMoneyMwl, InpRmAppliedPercentagesMwl, InpRmModeCalculationMwl, LP_MWL, true); g_risk.AddProfit(InpRmPercentageOrMoneyMdp, InpRmAppliedPercentagesMdp, InpRmModeCalculationMdp, LP_MDP, InpRmMdpIsStrict); g_risk.EndAddProfitLoss(); // Execute this every time we finish setting the maximum losses and profits g_loss_profit_manager = g_risk.GetLossProfitManager(); // We finish by adding it account_status.AddItemFast(g_risk); // We delete the temporary pointer delete manager; //--- We initialize the account account_status.AddLogFlagTicket(InpAccountStatusLogLevel); account_status.AddLogFlags(InpAccountStatusLogLevel); account_status.OnInitEvent(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- ICTGen_OnDeinitEvent(); // IMPORTANTE: Siempre llamar a esta fucnion en OnDeinit } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- const datetime time_curr = TimeCurrent(); g_new_day = false; g_new_bar_manager.Execute(time_curr); //--- Bloque 1 | Control Diario/Semanal/Mensual if(CNewBarManager_IsNewBarM1(g_new_bar_manager)) { if(CNewBarManager_IsNewBar(g_new_bar_manager, g_idx_bar_controlerd1)) { account_status.OnNewDay(time_curr); //--- if(CNewBarManager_IsNewBar(g_new_bar_manager, g_idx_controler_w1)) //Limpiar para no acomular mucha memoria { account_status.OnNewWeek(time_curr); } //--- Nuevo mes, eliminacion de liquidez if(CNewBarManager_IsNewBar(g_new_bar_manager, g_idx_bar_mn1)) account_status.OnNewMonth(time_curr); //--- g_can_trade = true; g_new_day = true; } } //--- Bloque 2 | Control de MDL\MDP.. etc if(account_status_positions_open) { CAccountStatus_OnTickEvent if(g_can_trade) { if(g_loss_profit_manager.MaxLossIsSuperated()) { const ENUM_TYPE_LOSS_PROFIT type = g_loss_profit_manager.GetLastLossSuperatedType(); if(type == LP_ML) { if(InpRmRiskMode == risk_mode_propfirm_dynamic_daiy_loss) { Print("The expert advisor lost the funding test"); } else { Print("Maximum loss exceeded now"); } //--- Remover(); } else if(type == LP_MDL) { Print("Maximum daily loss exceeded now"); } //--- g_strategy.OnInterupcion(); g_risk.CloseAllPositions(); g_can_trade = false; } else if(g_loss_profit_manager.MaxProfitIsSuperated()) { if(g_loss_profit_manager.GetLastProfitSuperatedType() != LP_MDP) return; //--- g_strategy.OnInterupcion(); g_risk.CloseAllPositions(); Print("Excellent Maximum daily profit achieved"); g_can_trade = false; } } } //--- Bloque 3 | Ejecucion if(!g_can_trade) // Si se superaron los limites de MDL, o MDP enotnces no CanTrader return; //--- /* Bien, llegamos a esta parte del código donde debemos ejecutar los conceptos ICT. Este bloque, como tal, no es “fijo”; existen muchas combinaciones posibles. Esto se debe a que intervienen varios factores: 1. ¿El timeframe de todos los conceptos y el que utiliza la estrategia es el mismo? 2. ¿Existe algún concepto en M1 (que, en lugar de usar OnNewBar, utilice OnNewBarM1), por ejemplo sesiones o High/Low Zones? En base a estos dos criterios podemos optimizar la ejecución. Por ejemplo, en el caso de utilizar sesiones, y cuando los conceptos no varían de timeframe (o en caso de hacerlo, el período actual es múltiplo del timeframe de los conceptos), podríamos hacer lo siguiente: if(CNewBarManager_IsNewBarM1(g_new_bar_manager)) { //--- // Esta función siempre se ejecuta en cada nueva barra M1 ICTGen_FuncionOnBarM1(g_new_day, time_curr); // Aquí irían las sesiones u otros objetos en M1 //--- if(CNewBarManager_IsNewBar(g_new_bar_manager, g_idx_controler_curr)) { //--- // Aquí se ejecutan los conceptos, // o bien se delega dentro de la clase, dependiendo de la estrategia //--- g_strategy.OnNewBar(time_curr); } } Por otro lado, si tenemos conceptos en varios timeframes, o los timeframes no son fijos ni múltiplos entre sí, entonces deberíamos ejecutar los conceptos en cada vela M1, y también llamar a OnNewBar de la estrategia en M1. En ese caso, el control de la apertura de velas deberá gestionarse internamente. if(CNewBarManager_IsNewBarM1(g_new_bar_manager)) { //--- // Siempre se ejecuta en cada nueva barra M1 ICTGen_FuncionOnBarM1(g_new_day, time_curr); // Aquí las sesiones u objetos M1 //--- // Aquí los conceptos, o dentro de la clase de la estrategia //--- g_strategy.OnNewBar(time_curr); } Para nuestro caso es especial, ya que: - El punto 1 es falso (todo trabaja en el timeframe actual). - El punto 2 también es falso (no utilizamos sesiones). Por lo tanto, podemos optimizar ejecutando todo únicamente en cada nueva barra del timeframe actual, siempre que también exista una nueva barra M1. if(CNewBarManager_IsNewBarM1(g_new_bar_manager) && CNewBarManager_IsNewBar(g_new_bar_manager, g_idx_controler_curr)) { //--- // Siempre ejecutamos esta función en cada nueva barra M1 ICTGen_FuncionOnBarM1(g_new_day, time_curr); //--- // Conceptos //--- g_strategy.OnNewBar(time_curr); } Siendo esto lo que haremos a continuación. */ //--- if(CNewBarManager_IsNewBarM1(g_new_bar_manager) && CNewBarManager_IsNewBar(g_new_bar_manager, g_idx_controler_curr)) { //--- // Siempre se ejecuta en cada nueva barra M1 ICTGen_FuncionOnBarM1(g_new_day, time_curr); //--- g_fvg.OnNewBar(); //--- g_strategy.OnNewBar(time_curr); } //--- // También es importante mencionar que, siempre que trabajemos con g_new_bar_manager // dentro de OnTick, debemos comprobar la nueva vela M1. // En cambio, dentro de CEstrategia::OnNewBar, si esta función se ejecuta en cada vela M1, // dicha comprobación ya no es necesaria, ya que sería una forma de “simplificar” // las primeras verificaciones. } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction & trans, const MqlTradeRequest & request, const MqlTradeResult & result) { //--- account_status.OnTradeTransactionEvent(trans); } //+------------------------------------------------------------------+