//+------------------------------------------------------------------+ //| Trading en Conjunto - Xcripe 2.5 FULL / Sin Licencia | //| Versión LOCAL - Sin sistema de licencias | //+------------------------------------------------------------------+ /* ═══════════════════════════════════════════════════════════════════ 🔥 XCRIPE 2.5 FULL / Sin Licencia - BASKET MODE + EMA TREND FILTER ═══════════════════════════════════════════════════════════════════ 📋 CAMBIOS REALIZADOS (2.5 FULL - BASKET MODE): ✅ NUEVO: SISTEMA DE CESTA (BASKET) 1. Basket_Enable - Activa/desactiva modo cesta por dirección 2. Basket_MinLevels - Mínimo niveles para activar cesta (2-4) 3. Basket_Buy_TargetMoney - Target $ para cerrar cesta BUY 4. Basket_Buy_StopLossMoney - SL $ para cerrar cesta BUY 5. Basket_Sell_TargetMoney - Target $ para cerrar cesta SELL 6. Basket_Sell_StopLossMoney - SL $ para cerrar cesta SELL 7. Al activarse cesta: elimina TPs individuales, toma control PnL 8. Si se abren más niveles: se incorporan automáticamente a la cesta 9. SL individuales se mantienen como protección ante spikes 10. Cesta BUY y cesta SELL son completamente independientes 11. Al cerrar cesta: resetea armed flags para nueva entrada limpia 12. Panel actualizado con estado de cada cesta en tiempo real ✅ NUEVO: EMA TREND FILTER 1. EMA_Filter_Enable - Toggle global del filtro 2. EMA_Period - Período de la EMA (default 200) 3. EMA_Timeframe - Timeframe configurable por usuario 4. EMA_Method - EMA/SMA/SMMA/LWMA 5. EMA_Filter_Buys / EMA_Filter_Sells - Control por dirección 6. Usa vela cerrada confirmada (índice 1) — sin ruido 7. Botón toggle ON/OFF en panel en tiempo real 8. Contadores de bloqueos BUY y SELL en panel 9. Log detallado por qué bloqueó cada señal ✅ HEREDADO: - Daily PnL REAL (Realizado + Flotante) - Circuit Breaker anti-spike - Anti-liquidez extrema - Rate Limiter anti-429 - Time Filter ventanas bloqueadas - News Filter Forex Factory - ATR Filter con toggle runtime - Trailing Stop por nivel 🌐 WEBREQUEST WHITELIST (MT5 → Tools → Options → EA): ✅ https://nfs.faireconomy.media ✅ https://api.telegram.org (si activado) ═══════════════════════════════════════════════════════════════════ */ #property strict #property version "2.50" #property copyright "Trading en Conjunto" #property description "Xcripe 2.5 FULL / Sin Licencia - RSI Burst + Basket Mode + EMA Trend Filter + Daily PnL + Circuit Breaker" #include CTrade trade; #define LEVELS 4 //+------------------------------------------------------------------+ //| ENUMS - ALARMAS | //+------------------------------------------------------------------+ enum ENUM_RSI_ALARM_MODE { ALARM_CROSS_BELOW = 0, ALARM_CROSS_ABOVE = 1, ALARM_BELOW_EQ = 2, ALARM_ABOVE_EQ = 3 }; //+------------------------------------------------------------------+ //| ENUMS - PRESETS DE CUENTA | //+------------------------------------------------------------------+ enum ENUM_ACCOUNT_PRESET { PRESET_CUSTOM = 0, PRESET_PROP_AGGRESSIVE = 1, PRESET_PROP_CONSERVATIVE = 2, PRESET_REAL_AGGRESSIVE = 3, PRESET_REAL_CONSERVATIVE = 4 }; //+------------------------------------------------------------------+ //| INPUTS - PRESET CONFIGURACIÓN | //+------------------------------------------------------------------+ input group "════════ 📋 PRESET DE CONFIGURACIÓN ════════" input ENUM_ACCOUNT_PRESET AccountPreset = PRESET_CUSTOM; input bool ApplyPresetOnInit = true; //+------------------------------------------------------------------+ //| INPUTS - IDENTIFICACIÓN DE ÓRDENES | //+------------------------------------------------------------------+ input group "════════ 🧾 IDENTIFICACIÓN DE ÓRDENES ════════" input string InpTradeComment = ""; input bool InpUsePerLevelMagic = true; input long InpMagicBase = 202501; input bool InpMagicBaseFromLogin = true; input int InpMagicLoginModulo = 10000; //+------------------------------------------------------------------+ //| INPUTS - CONFIGURACIÓN GENERAL | //+------------------------------------------------------------------+ input group "════════ 📊 CONFIGURACIÓN GENERAL ════════" input string InpSymbol = "XAUUSD"; input ENUM_TIMEFRAMES TF_Signal = PERIOD_M1; input ulong MagicNumber = 777777; //+------------------------------------------------------------------+ //| INPUTS - RSI GENERAL | //+------------------------------------------------------------------+ input group "════════ 📈 RSI - CONFIGURACIÓN GENERAL ════════" input int RSIPeriod = 14; input bool ActivarVentas = true; input bool UseProgressiveRisk = false; input double ProgressiveMultiplier = 1.2; //+------------------------------------------------------------------+ //| INPUTS - BASKET MODE | //+------------------------------------------------------------------+ input group "════════ 📊 EMA TREND FILTER ════════" input bool EMA_Filter_Enable = true; // Activar Filtro Tendencia EMA input int EMA_Period = 200; // Período de la EMA input ENUM_TIMEFRAMES EMA_Timeframe = PERIOD_H1; // Timeframe de la EMA input ENUM_MA_METHOD EMA_Method = MODE_EMA; // Método MA (EMA/SMA/SMMA/LWMA) input int EMA_AppliedPrice = PRICE_CLOSE; // Precio aplicado (0=Close,1=Open,2=High,3=Low) input bool EMA_Filter_Buys = true; // Filtrar COMPRAS por tendencia input bool EMA_Filter_Sells = true; // Filtrar VENTAS por tendencia input bool EMA_ShowInPanel = true; // Mostrar en Panel input color EMA_BullishColor = clrLime; // Color tendencia alcista input color EMA_BearishColor = clrRed; // Color tendencia bajista input group "════════ 🧺 BASKET MODE - CESTA ════════" input bool Basket_Enable = true; // Activar Modo Cesta input int Basket_MinLevels = 2; // Niveles mínimos para activar cesta (2-4) input double Basket_Buy_TargetMoney = 150.0; // Target $ cesta COMPRAS (0=desactivado) input double Basket_Buy_StopLossMoney = 200.0; // Stop Loss $ cesta COMPRAS (0=desactivado) input double Basket_Sell_TargetMoney = 150.0; // Target $ cesta VENTAS (0=desactivado) input double Basket_Sell_StopLossMoney = 200.0; // Stop Loss $ cesta VENTAS (0=desactivado) input bool Basket_ShowInPanel = true; // Mostrar cesta en panel input bool Basket_TelegramAlert = true; // Alerta Telegram al cerrar cesta //+------------------------------------------------------------------+ //| INPUTS - BUY NIVEL 1 | //+------------------------------------------------------------------+ input group "════════ 🔵 COMPRAS NIVEL 1 ════════" input bool Buy1_Enable = true; input double Buy1_RSI = 15.0; input int Buy1_Orders = 5; input double Buy1_Risk = 0.5; input int Buy1_TP_Points = 500; input int Buy1_SL_Points = 1500; input bool Buy1_TS_Enable = true; input int Buy1_TS_Start = 300; input int Buy1_TS_Step = 50; input int Buy1_TS_Dist = 150; //+------------------------------------------------------------------+ //| INPUTS - BUY NIVEL 2 | //+------------------------------------------------------------------+ input group "════════ 🔵 COMPRAS NIVEL 2 ════════" input bool Buy2_Enable = true; input double Buy2_RSI = 10.0; input int Buy2_Orders = 10; input double Buy2_Risk = 1.0; input int Buy2_TP_Points = 500; input int Buy2_SL_Points = 1500; input bool Buy2_TS_Enable = true; input int Buy2_TS_Start = 300; input int Buy2_TS_Step = 50; input int Buy2_TS_Dist = 150; //+------------------------------------------------------------------+ //| INPUTS - BUY NIVEL 3 | //+------------------------------------------------------------------+ input group "════════ 🔵 COMPRAS NIVEL 3 ════════" input bool Buy3_Enable = true; input double Buy3_RSI = 5.0; input int Buy3_Orders = 15; input double Buy3_Risk = 1.5; input int Buy3_TP_Points = 500; input int Buy3_SL_Points = 1500; input bool Buy3_TS_Enable = true; input int Buy3_TS_Start = 300; input int Buy3_TS_Step = 50; input int Buy3_TS_Dist = 150; //+------------------------------------------------------------------+ //| INPUTS - BUY NIVEL 4 | //+------------------------------------------------------------------+ input group "════════ 🔵 COMPRAS NIVEL 4 ════════" input bool Buy4_Enable = true; input double Buy4_RSI = 2.0; input int Buy4_Orders = 20; input double Buy4_Risk = 2.0; input int Buy4_TP_Points = 500; input int Buy4_SL_Points = 1500; input bool Buy4_TS_Enable = true; input int Buy4_TS_Start = 300; input int Buy4_TS_Step = 50; input int Buy4_TS_Dist = 150; //+------------------------------------------------------------------+ //| INPUTS - SELL NIVEL 1 | //+------------------------------------------------------------------+ input group "════════ 🔴 VENTAS NIVEL 1 ════════" input bool Sell1_Enable = true; input double Sell1_RSI = 85.0; input int Sell1_Orders = 5; input double Sell1_Risk = 0.5; input int Sell1_TP_Points = 500; input int Sell1_SL_Points = 1500; input bool Sell1_TS_Enable = true; input int Sell1_TS_Start = 300; input int Sell1_TS_Step = 50; input int Sell1_TS_Dist = 150; //+------------------------------------------------------------------+ //| INPUTS - SELL NIVEL 2 | //+------------------------------------------------------------------+ input group "════════ 🔴 VENTAS NIVEL 2 ════════" input bool Sell2_Enable = true; input double Sell2_RSI = 90.0; input int Sell2_Orders = 10; input double Sell2_Risk = 1.0; input int Sell2_TP_Points = 500; input int Sell2_SL_Points = 1500; input bool Sell2_TS_Enable = true; input int Sell2_TS_Start = 300; input int Sell2_TS_Step = 50; input int Sell2_TS_Dist = 150; //+------------------------------------------------------------------+ //| INPUTS - SELL NIVEL 3 | //+------------------------------------------------------------------+ input group "════════ 🔴 VENTAS NIVEL 3 ════════" input bool Sell3_Enable = true; input double Sell3_RSI = 95.0; input int Sell3_Orders = 15; input double Sell3_Risk = 1.5; input int Sell3_TP_Points = 500; input int Sell3_SL_Points = 1500; input bool Sell3_TS_Enable = true; input int Sell3_TS_Start = 300; input int Sell3_TS_Step = 50; input int Sell3_TS_Dist = 150; //+------------------------------------------------------------------+ //| INPUTS - SELL NIVEL 4 | //+------------------------------------------------------------------+ input group "════════ 🔴 VENTAS NIVEL 4 ════════" input bool Sell4_Enable = true; input double Sell4_RSI = 98.0; input int Sell4_Orders = 20; input double Sell4_Risk = 2.0; input int Sell4_TP_Points = 500; input int Sell4_SL_Points = 1500; input bool Sell4_TS_Enable = true; input int Sell4_TS_Start = 300; input int Sell4_TS_Step = 50; input int Sell4_TS_Dist = 150; //+------------------------------------------------------------------+ //| INPUTS - GESTIÓN DE RIESGO | //+------------------------------------------------------------------+ input group "════════ 💰 GESTIÓN DE RIESGO ════════" input bool UseRiskCalculation = true; input double FixedLotSize = 0.01; //+------------------------------------------------------------------+ //| INPUTS - FILTRO ATR | //+------------------------------------------------------------------+ input group "════════ 📉 FILTRO ATR (VOLATILIDAD) ════════" input bool EnableATRFilter = true; input int ATRPeriod = 14; input ENUM_TIMEFRAMES ATR_Timeframe = PERIOD_M15; input double ATR_MinMultiplier = 0.5; input double ATR_MaxMultiplier = 3.0; input int ATR_SmoothPeriod = 20; input bool ATR_ShowInPanel = true; input bool ATR_BlockOnExtreme = true; input int ATR_TelegramAlertEvery = 10; input color ATR_NormalColor = clrLime; input color ATR_LowColor = clrOrange; input color ATR_HighColor = clrRed; input int MaxSpreadPoints = 50; //+------------------------------------------------------------------+ //| INPUTS - FILTRO DE HORARIO | //+------------------------------------------------------------------+ input group "════════ ⏰ FILTRO DE HORARIO ════════" input bool EnableTimeFilter = true; input bool TimeFilterUseServerTime = true; input string BlockWindows = "23:00-01:30; 12:25-13:10"; input bool BlockBuysInTimeFilter = true; input bool BlockSellsInTimeFilter = true; input bool BlockAlarmsInTimeFilter = false; input bool TimeFilterShowInPanel = true; input int TimeFilter_TelegramAlertEvery = 10; //+------------------------------------------------------------------+ //| INPUTS - NEWS FILTER FOREX FACTORY | //+------------------------------------------------------------------+ input group "════════ 📰 NEWS FILTER FOREX FACTORY ════════" input bool EnableNewsFilter = true; input string ForexFactoryURL = "https://nfs.faireconomy.media/ff_calendar_thisweek.json"; input int NewsMinutesBefore = 30; input int NewsMinutesAfter = 30; input bool NewsFilterHighImpact = true; input bool NewsFilterMediumImpact = false; input string NewsFilterCurrencies = "USD"; input bool NewsFailSafeBlockIfNoData = false; input int NewsUpdateIntervalMinutes = 60; //+------------------------------------------------------------------+ //| INPUTS - CONTROL DRAWDOWN Y DAILY LIMITS | //+------------------------------------------------------------------+ input group "════════ 🛡️ CONTROL DRAWDOWN Y DAILY LIMITS ════════" input bool EnableDailyLimits = true; input double DailyLossLimitPercent = 5.0; input double DailyLossLimitMoney = 0.0; input bool CloseAllOnDailyLoss = true; input bool CountFloatingLoss = true; input bool ShowDDInPanel = true; input double DailyTargetMoney = 300.0; input bool CloseAllOnDailyTarget = true; input bool LockTradingAfterTarget = true; input string AsiaSession = "00:00-06:59"; input string LondonSession = "07:00-12:59"; input string NYSession = "13:00-20:59"; //+------------------------------------------------------------------+ //| INPUTS - CIRCUIT BREAKER | //+------------------------------------------------------------------+ input group "════════ ⚡ CIRCUIT BREAKER - ANTI SPIKE ════════" input bool EnableCircuitBreaker = true; input ENUM_TIMEFRAMES SpikeTF = PERIOD_M1; input double SpikeRangeATRMult = 2.5; input int SpikeRangePointsMin = 1500; input int BreakerCooldownMinutes = 30; input bool BreakerCloseAllOnSpike = true; input bool BreakerBlockOnSlippage = true; input int BreakerSlippageCooldown = 15; input int MaxTickGapPoints = 800; input int MaxSpreadSpikePoints = 120; input int GapCooldownMinutes = 45; //+------------------------------------------------------------------+ //| INPUTS - INTERFAZ GRÁFICA | //+------------------------------------------------------------------+ input group "════════ 🎨 INTERFAZ GRÁFICA ════════" input bool ShowPanel = true; input int PanelX = 20; input int PanelY = 30; input color PanelBgColor = C'15,15,35'; input color PanelTextColor = clrWhite; input color PanelHeaderColor = C'102,126,234'; //+------------------------------------------------------------------+ //| INPUTS - ALARMAS RSI | //+------------------------------------------------------------------+ input group "════════ 🚨 SISTEMA DE ALARMAS (RSI) ════════" input bool EnableSoundAlerts = true; input bool EnablePopupAlerts = true; input bool EnableTelegramAlerts = false; input string TelegramBotToken = ""; input string TelegramChatId = ""; input string TelegramPrefix = "🚨 XCRIPE ALERTA"; input int AlarmCooldownSeconds = 300; input bool Alarm1_Enabled = false; input ENUM_TIMEFRAMES Alarm1_TF = PERIOD_M1; input ENUM_RSI_ALARM_MODE Alarm1_Mode = ALARM_CROSS_BELOW; input double Alarm1_Level = 20.0; input bool Alarm1_Telegram = true; input string Alarm1_Sound = "alert.wav"; input string Alarm1_Message = ""; input bool Alarm2_Enabled = false; input ENUM_TIMEFRAMES Alarm2_TF = PERIOD_M1; input ENUM_RSI_ALARM_MODE Alarm2_Mode = ALARM_CROSS_BELOW; input double Alarm2_Level = 10.0; input bool Alarm2_Telegram = true; input string Alarm2_Sound = "alert2.wav"; input string Alarm2_Message = ""; input bool Alarm3_Enabled = false; input ENUM_TIMEFRAMES Alarm3_TF = PERIOD_M5; input ENUM_RSI_ALARM_MODE Alarm3_Mode = ALARM_CROSS_ABOVE; input double Alarm3_Level = 90.0; input bool Alarm3_Telegram = true; input string Alarm3_Sound = "alert3.wav"; input string Alarm3_Message = ""; //+------------------------------------------------------------------+ //| VERSIÓN LOCAL - SIN SISTEMA DE LICENCIAS | //+------------------------------------------------------------------+ string g_license_status = "✅ Versión Local"; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - INDICADORES | //+------------------------------------------------------------------+ int rsiHandleTrade = INVALID_HANDLE; int rsiHandleA1 = INVALID_HANDLE; int rsiHandleA2 = INVALID_HANDLE; int rsiHandleA3 = INVALID_HANDLE; bool ownA1=false, ownA2=false, ownA3=false; int atrHandle = INVALID_HANDLE; double currentATR = 0.0; double avgATR = 0.0; double atrRatio = 0.0; string atrStatus = "-"; bool atrAllowTrade = true; int atrBlockedSignals = 0; datetime lastATRTelegramAlert = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - ATR RUNTIME TOGGLE | //+------------------------------------------------------------------+ bool g_ATR_Enabled_Runtime = true; ENUM_TIMEFRAMES g_ATR_TF_Runtime = PERIOD_M15; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - TIME FILTER | //+------------------------------------------------------------------+ bool g_time_blocked = false; string g_time_block_reason = ""; int g_time_block_count = 0; datetime g_last_time_warning = 0; datetime g_last_time_telegram = 0; #define MAX_TIME_WINDOWS 10 int g_time_windows_count = 0; int g_time_starts_min[MAX_TIME_WINDOWS]; int g_time_ends_min[MAX_TIME_WINDOWS]; bool g_time_parse_error_logged = false; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - ARM/DISARM RSI TOUCH | //+------------------------------------------------------------------+ bool g_buyArmed[4] = {true,true,true,true}; bool g_sellArmed[4] = {true,true,true,true}; bool g_alarm1Armed = true; bool g_alarm2Armed = true; bool g_alarm3Armed = true; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - CONTROL DE NIVELES (BOTONES) | //+------------------------------------------------------------------+ bool g_buyLevel1Enabled = true; bool g_buyLevel2Enabled = true; bool g_buyLevel3Enabled = true; bool g_buyLevel4Enabled = true; bool g_sellLevel1Enabled = true; bool g_sellLevel2Enabled = true; bool g_sellLevel3Enabled = true; bool g_sellLevel4Enabled = true; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - BAR TIME CONTROL | //+------------------------------------------------------------------+ datetime lastSignalTimeRSI_Buy1 = 0; datetime lastSignalTimeRSI_Buy2 = 0; datetime lastSignalTimeRSI_Buy3 = 0; datetime lastSignalTimeRSI_Buy4 = 0; datetime lastSignalTimeRSI_Sell1 = 0; datetime lastSignalTimeRSI_Sell2 = 0; datetime lastSignalTimeRSI_Sell3 = 0; datetime lastSignalTimeRSI_Sell4 = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - ESTADÍSTICAS | //+------------------------------------------------------------------+ int signalCounter = 0; double currentRSI = 0; string lastSignalType = "-"; datetime lastSignalTime = 0; datetime g_lastAlarm1 = 0; datetime g_lastAlarm2 = 0; datetime g_lastAlarm3 = 0; string g_lastAlarmText = "-"; datetime g_lastAlarmTime = 0; int totalTrades = 0; int winTrades = 0; int lossTrades = 0; double totalProfit = 0; double initialBalance = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - PANEL Y TELEGRAM | //+------------------------------------------------------------------+ string panelPrefix = "XcripeV25_Panel_"; datetime g_last_telegram_error = 0; int g_telegram_error_count = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - CACHED STATS | //+------------------------------------------------------------------+ datetime g_last_stats_calc = 0; int g_cached_total_trades = 0; int g_cached_win_trades = 0; int g_cached_loss_trades = 0; double g_cached_total_profit = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - SYMBOL INFO | //+------------------------------------------------------------------+ double g_point_cached=0.0; int g_digits_cached=0; double g_tick_value_cached=0.0; double g_tick_size_cached=0.0; double g_lot_step_cached=0.01; double g_lot_min_cached=0.01; double g_lot_max_cached=100.0; int g_alarm_cooldown_sec = 300; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - CONFIG RSI POR NIVEL | //+------------------------------------------------------------------+ bool g_buy_en[LEVELS]; double g_buy_rsi[LEVELS]; int g_buy_orders[LEVELS]; double g_buy_risk[LEVELS]; int g_buy_tp[LEVELS]; int g_buy_sl[LEVELS]; bool g_buy_ts_en[LEVELS]; int g_buy_ts_start[LEVELS]; int g_buy_ts_step[LEVELS]; int g_buy_ts_dist[LEVELS]; bool g_sell_en[LEVELS]; double g_sell_rsi[LEVELS]; int g_sell_orders[LEVELS]; double g_sell_risk[LEVELS]; int g_sell_tp[LEVELS]; int g_sell_sl[LEVELS]; bool g_sell_ts_en[LEVELS]; int g_sell_ts_start[LEVELS]; int g_sell_ts_step[LEVELS]; int g_sell_ts_dist[LEVELS]; bool g_ActivarVentas; bool g_UseRiskCalculation; double g_FixedLotSize; bool g_EnableDailyDrawdownControl; double g_MaxDailyDrawdownPercent; bool g_EnableNewsFilter; bool g_EnableATRFilter; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - EMA TREND FILTER | //+------------------------------------------------------------------+ int g_ema_handle = INVALID_HANDLE; double g_ema_value = 0.0; double g_ema_close = 0.0; bool g_ema_bullish = false; bool g_ema_bearish = false; string g_ema_trend_status = "-"; bool g_ema_filter_runtime = true; datetime g_ema_last_update = 0; int g_ema_blocked_buys = 0; int g_ema_blocked_sells = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - BASKET MODE | //+------------------------------------------------------------------+ // Estado cesta BUY bool g_basket_buy_active = false; // cesta activa int g_basket_buy_levels = 0; // cuántos niveles hay en la cesta double g_basket_buy_pnl = 0.0; // PnL flotante actual datetime g_basket_buy_open_time = 0; // cuándo se formó la cesta int g_basket_buy_total_pos = 0; // total posiciones en la cesta // Estado cesta SELL bool g_basket_sell_active = false; int g_basket_sell_levels = 0; double g_basket_sell_pnl = 0.0; datetime g_basket_sell_open_time = 0; int g_basket_sell_total_pos = 0; // Resultado de último cierre de cesta (para panel/telegram) string g_basket_buy_last_result = "-"; string g_basket_sell_last_result = "-"; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - DAILY LIMITS | //+------------------------------------------------------------------+ double g_day_start_equity = 0.0; double g_day_start_balance = 0.0; datetime g_day_anchor_time = 0; datetime g_day_start_time = 0; double g_cached_realized_pnl = 0.0; datetime g_last_realized_calc = 0; bool g_daily_loss_hit = false; bool g_daily_target_hit = false; datetime g_trade_lock_until = 0; string g_lock_reason = ""; double g_current_dd_percent = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - ANTI-LIQUIDEZ | //+------------------------------------------------------------------+ double g_last_bid = 0.0; double g_last_ask = 0.0; datetime g_last_tick_time = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - RATE LIMITER | //+------------------------------------------------------------------+ datetime g_ff_next_fetch_allowed = 0; int g_ff_backoff_seconds = 0; string g_ff_cached_json = ""; datetime g_ff_cache_time = 0; datetime g_tg_next_allowed_time = 0; int g_tg_backoff_seconds = 0; int g_tg_cooldown_minutes = 2; datetime g_last_error_log_time = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - CIRCUIT BREAKER | //+------------------------------------------------------------------+ datetime g_breaker_until = 0; string g_breaker_reason = ""; int g_breaker_spike_count = 0; int g_breaker_slippage_count = 0; datetime g_last_any_signal = 0; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - CACHE | //+------------------------------------------------------------------+ struct IndicatorCache { double value; double value_prev; datetime last_update; bool valid; }; IndicatorCache g_rsi_trade_cache; IndicatorCache g_atr_cache; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES - NEWS FF | //+------------------------------------------------------------------+ struct FFNewsEvent { datetime time; string title; string currency; string impact; }; FFNewsEvent g_ff_news[]; int g_ff_news_count = 0; datetime g_ff_last_update = 0; bool g_ff_is_news_time = false; string g_ff_next_title = ""; int g_ff_minutes_to_news = 999; int g_ff_blocked_count = 0; //+------------------------------------------------------------------+ //| FIN PARTE 1/4 - CONTINUAR CON PARTE 2/4 | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| XCRIPE 2.5 FULL - PARTE 2/4: HELPERS, CORE LOGIC | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| HELPERS - IDENTIFICACIÓN ÚNICA | //+------------------------------------------------------------------+ bool IsBacktesting() { return (MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION)); } string BrokerSafe(string s){ StringReplace(s,"\r"," "); StringReplace(s,"\n"," "); StringTrimLeft(s); StringTrimRight(s); return s; } long EffectiveMagicBase() { long base = InpMagicBase; if(InpMagicBaseFromLogin) { long login = AccountInfoInteger(ACCOUNT_LOGIN); long mod = (InpMagicLoginModulo <= 0 ? 10000 : InpMagicLoginModulo); base = base + ((login % mod) * 1000); } return base; } string BuildOrderComment(bool isBuy, int levelIndex) { return InpTradeComment; } long MagicForLevel(bool isBuy, int levelIndex) { if(!InpUsePerLevelMagic) return (long)MagicNumber; long base = EffectiveMagicBase(); long sideOffset = isBuy ? 0 : 100; return (base + sideOffset + (levelIndex+1)); } bool DecodeLevelFromMagic(long magic, bool &isBuy, int &lvl) { if(!InpUsePerLevelMagic) { if(magic != (long)MagicNumber) return false; isBuy = true; lvl = 0; return true; } long base = EffectiveMagicBase(); if(magic < base) return false; long diff = magic - base; if(diff >= 1 && diff <= 4) { isBuy=true; lvl=(int)diff-1; return true; } if(diff >= 101 && diff <= 104){ isBuy=false; lvl=(int)(diff-101); return true; } return false; } bool IsOurMagic(long magic) { if(!InpUsePerLevelMagic) return (magic == (long)MagicNumber); bool isBuy; int lvl; return DecodeLevelFromMagic(magic, isBuy, lvl); } //+------------------------------------------------------------------+ //| HELPERS - JSON/UTILS | //+------------------------------------------------------------------+ string JSON_Escape(string s){ StringReplace(s,"\\","\\\\"); StringReplace(s,"\"","\\\""); StringReplace(s,"\r"," "); StringReplace(s,"\n"," "); return s; } int VolumeDigitsFromStep(double step) { int digits=0; double s=step; while(digits<8 && MathAbs(s-MathRound(s))>1e-8){ s*=10.0; digits++; } return digits; } bool GetRSI2(int handle, double &rsiCur, double &rsiPrev) { if(handle==INVALID_HANDLE) return false; double rsiBuff[]; ArrayResize(rsiBuff,3); ArraySetAsSeries(rsiBuff,true); if(CopyBuffer(handle,0,0,3,rsiBuff)<3) return false; rsiCur=rsiBuff[0]; rsiPrev=rsiBuff[1]; return true; } double PriceFromPoints(double entryPrice, int points, bool isBuy, bool isSL) { if(points<=0) return 0.0; double distance=points*g_point_cached; if(isBuy) return NormalizeDouble(isSL?entryPrice-distance:entryPrice+distance, g_digits_cached); else return NormalizeDouble(isSL?entryPrice+distance:entryPrice-distance, g_digits_cached); } string MaskSensitiveData(string data, int showChars=4) { int len=StringLen(data); if(len<=showChars) return "****"; return StringSubstr(data,0,showChars)+"..."+StringSubstr(data,len-showChars,showChars); } //+------------------------------------------------------------------+ //| RATE LIMITER | //+------------------------------------------------------------------+ bool RateLimit_Allow(string key, int minSeconds, datetime &nextAllowed) { datetime now=TimeCurrent(); if(now=60){ Print("⏳ Rate Limit [",key,"] - Espera ",(int)(nextAllowed-now),"s"); lw=now; } return false; } nextAllowed=now+minSeconds; return true; } void RateLimit_On429(string key, datetime &nextAllowed, int &backoffSeconds) { datetime now=TimeCurrent(); if(backoffSeconds==0) backoffSeconds=30; else backoffSeconds=MathMin(backoffSeconds*2,900); nextAllowed=now+backoffSeconds; Print("🚨 HTTP 429 [",key,"] - Backoff: ",backoffSeconds,"s hasta ",TimeToString(nextAllowed,TIME_MINUTES)); } bool ParseRetryAfter(string headers, int &seconds) { int pos=StringFind(headers,"Retry-After:"); if(pos<0) return false; int start=pos+12; int end=StringFind(headers,"\r\n",start); if(end<0) end=StringLen(headers); string value=StringSubstr(headers,start,end-start); StringTrimLeft(value); StringTrimRight(value); seconds=(int)StringToInteger(value); return (seconds>0); } //+------------------------------------------------------------------+ //| TELEGRAM | //+------------------------------------------------------------------+ bool Telegram_ValidateConfig() { if(!EnableTelegramAlerts) return false; if(TelegramBotToken=="") return false; if(TelegramChatId=="") return false; return true; } bool Telegram_SendMessage(const string text) { if(!EnableTelegramAlerts) return false; if(!Telegram_ValidateConfig()) return false; datetime now=TimeCurrent(); if(!RateLimit_Allow("TELEGRAM",g_tg_cooldown_minutes*60,g_tg_next_allowed_time)) return false; if(g_telegram_error_count>5&&(now-g_last_telegram_error)<300) return false; if(g_telegram_error_count>5) g_telegram_error_count=0; string url="https://api.telegram.org/bot"+TelegramBotToken+"/sendMessage"; string fullMessage=TelegramPrefix; if(TelegramPrefix!="") fullMessage+="\n\n"; fullMessage+=text; string body=StringFormat("{\"chat_id\":\"%s\",\"text\":\"%s\",\"parse_mode\":\"HTML\"}",JSON_Escape(TelegramChatId),JSON_Escape(fullMessage)); char data[]; StringToCharArray(body,data,0,WHOLE_ARRAY,CP_UTF8); char result[]; string rh=""; int code=WebRequest("POST",url,"Content-Type: application/json; charset=utf-8\r\n",8000,data,result,rh); if(code==-1){ g_telegram_error_count++; g_last_telegram_error=now; return false; } if(code==429){ int rs=0; if(ParseRetryAfter(rh,rs)&&rs>0){ g_tg_next_allowed_time=now+rs; g_tg_backoff_seconds=rs; } else RateLimit_On429("TELEGRAM",g_tg_next_allowed_time,g_tg_backoff_seconds); g_telegram_error_count++; g_last_telegram_error=now; return false; } if(code==200){ g_telegram_error_count=0; g_tg_backoff_seconds=0; return true; } g_telegram_error_count++; g_last_telegram_error=now; return false; } void Telegram_SendTestMessage() { if(!EnableTelegramAlerts||!Telegram_ValidateConfig()) return; string testMsg="TEST CONEXIÓN ✅\n\n📊 Símbolo: "+InpSymbol+"\n🧺 Basket Mode: "+(Basket_Enable?"ACTIVADO":"DESACTIVADO")+"\n⏰ "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES); Telegram_SendMessage(testMsg); } //+------------------------------------------------------------------+ //| DECLARACIONES FORWARD | //+------------------------------------------------------------------+ datetime NowServer(); bool ParseTimeString(string timeStr, int &hourOut, int &minOut); int MinutesOfDay(datetime t); //+------------------------------------------------------------------+ //| DAILY LIMITS | //+------------------------------------------------------------------+ void ResetDailyAnchor() { datetime now=TimeCurrent(); MqlDateTime dt; TimeToStruct(now,dt); dt.hour=0; dt.min=0; dt.sec=0; g_day_anchor_time=StructToTime(dt); g_day_start_balance=AccountInfoDouble(ACCOUNT_BALANCE); g_day_start_equity=AccountInfoDouble(ACCOUNT_EQUITY); g_day_start_time=g_day_anchor_time; g_daily_loss_hit=false; g_daily_target_hit=false; g_trade_lock_until=0; g_lock_reason=""; g_current_dd_percent=0; Print("📅 RESET DIARIO | Balance: $",DoubleToString(g_day_start_balance,2)); } void UpdateDailyResetIfDayChanged() { datetime now=TimeCurrent(); MqlDateTime dt; TimeToStruct(now,dt); MqlDateTime lastDt; TimeToStruct(g_day_anchor_time,lastDt); if(dt.day!=lastDt.day||dt.mon!=lastDt.mon||dt.year!=lastDt.year) ResetDailyAnchor(); } double GetRealizedPnLToday() { datetime now=TimeCurrent(); if((now-g_last_realized_calc)<30&&g_last_realized_calc>0) return g_cached_realized_pnl; MqlDateTime dt; TimeToStruct(now,dt); dt.hour=0; dt.min=0; dt.sec=0; datetime dayStart=StructToTime(dt); if(!HistorySelect(dayStart,now)) return g_cached_realized_pnl; double realized=0.0; int dealsTotal=HistoryDealsTotal(); for(int i=0;i=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(PositionGetString(POSITION_SYMBOL)!=InpSymbol) continue; long magic=(long)PositionGetInteger(POSITION_MAGIC); if(!IsOurMagic(magic)) continue; floating+=PositionGetDouble(POSITION_PROFIT); } return floating; } double GetDailyPnL() { return GetRealizedPnLToday()+GetFloatingPnLNow(); } bool ParseSessionStart(const string session,int &h,int &m) { int dash=StringFind(session,"-"); if(dash<0) return false; string start=StringSubstr(session,0,dash); StringTrimLeft(start); StringTrimRight(start); return ParseTimeString(start,h,m); } datetime NextAsiaStartStrict(datetime nowServer) { int sh=0,sm=0; if(!ParseSessionStart(AsiaSession,sh,sm)){ sh=0; sm=0; } MqlDateTime dt; TimeToStruct(nowServer,dt); dt.hour=sh; dt.min=sm; dt.sec=0; datetime todayAsia=StructToTime(dt); if(nowServer=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket<=0) continue; if(PositionGetString(POSITION_SYMBOL)!=symbol) continue; long mg=PositionGetInteger(POSITION_MAGIC); if(!IsOurMagic(mg)) continue; if(trade.PositionClose(ticket)){ closed++; Print(" ✅ Cerrada #",ticket); } else Print(" ❌ Error cerrando #",ticket," | ",trade.ResultRetcodeDescription()); } Print("✅ Total cerradas: ",closed); } bool CheckDailyLoss() { if(!EnableDailyLimits) return true; if(g_daily_loss_hit) return false; double pnl=GetDailyPnL(); double base=g_day_start_balance; if(base<=0.0) base=AccountInfoDouble(ACCOUNT_BALANCE); double lossLimitMoney=(DailyLossLimitMoney>0.0)?DailyLossLimitMoney:base*(DailyLossLimitPercent/100.0); g_current_dd_percent=(base>0.0)?(MathMax(0.0,-pnl)/base*100.0):0.0; if(pnl<=-lossLimitMoney) { g_daily_loss_hit=true; g_lock_reason="🛑 DAILY LOSS"; Print("🛑 DAILY LOSS HIT | PnL: $",DoubleToString(pnl,2)," | Límite: $",DoubleToString(lossLimitMoney,2)); if(CloseAllOnDailyLoss) CloseAllPositionsForSymbol(InpSymbol); if(EnableTelegramAlerts){ string msg="🛑 LÍMITE DE PÉRDIDA DIARIA\n💰 PnL: $"+DoubleToString(pnl,2)+"\n🚫 Límite: $"+DoubleToString(lossLimitMoney,2); Telegram_SendMessage(msg); } return false; } return true; } bool CheckDailyTarget() { if(!EnableDailyLimits) return true; if(DailyTargetMoney<=0) return true; if(g_daily_target_hit) return false; double pnl=GetDailyPnL(); if(pnl>=DailyTargetMoney) { g_daily_target_hit=true; Print("🎯 DAILY TARGET HIT | PnL: $",DoubleToString(pnl,2)); if(CloseAllOnDailyTarget) CloseAllPositionsForSymbol(InpSymbol); if(LockTradingAfterTarget){ g_trade_lock_until=NextAsiaStartStrict(TimeCurrent()); g_lock_reason="🎯 TARGET"; } if(EnableTelegramAlerts){ string msg="🎯 TARGET DIARIO ALCANZADO\n💰 PnL: $"+DoubleToString(pnl,2); Telegram_SendMessage(msg); } return false; } return true; } bool IsTradeLocked() { if(g_trade_lock_until<=0) return false; return (TimeCurrent()MaxSpreadPoints){ static datetime lw=0; if((TimeCurrent()-lw)>=60){ Print("🚫 SPREAD ALTO: ",DoubleToString(spreadPts,1)," pts"); lw=TimeCurrent(); } return false; } return true; } bool CheckSpikeBreaker() { if(!EnableCircuitBreaker) return true; datetime now=TimeCurrent(); if(now0)?(currentATR/g_point_cached):0; bool spike=false; if(atrPts>0&&range>=(atrPts*SpikeRangeATRMult)) spike=true; if(range>=SpikeRangePointsMin) spike=true; if(spike){ g_breaker_spike_count++; TriggerBreaker("⚡ SPIKE",BreakerCooldownMinutes); if(BreakerCloseAllOnSpike) CloseAllPositionsForSymbol(InpSymbol); return false; } return true; } bool CheckSlippageBreaker(uint retcode) { if(!EnableCircuitBreaker||!BreakerBlockOnSlippage) return true; if(retcode==TRADE_RETCODE_REQUOTE||retcode==TRADE_RETCODE_REJECT){ g_breaker_slippage_count++; TriggerBreaker("💥 SLIPPAGE",BreakerSlippageCooldown); return false; } return true; } bool CheckLiquidityGap() { double bid=SymbolInfoDouble(InpSymbol,SYMBOL_BID); double ask=SymbolInfoDouble(InpSymbol,SYMBOL_ASK); datetime now=TimeCurrent(); if(bid<=0||ask<=0) return true; double spr=(ask-bid)/g_point_cached; if(MaxSpreadSpikePoints>0&&spr>=MaxSpreadSpikePoints){ g_breaker_slippage_count++; TriggerBreaker("🌪️ SPREAD EXPLOSION",GapCooldownMinutes); if(BreakerCloseAllOnSpike) CloseAllPositionsForSymbol(InpSymbol); return false; } if(g_last_bid>0.0&&g_last_tick_time>0) { double jumpPts=MathAbs(bid-g_last_bid)/g_point_cached; if((now-g_last_tick_time)<=3&&MaxTickGapPoints>0&&jumpPts>=MaxTickGapPoints){ g_breaker_spike_count++; TriggerBreaker("💥 GAP / LIQUIDEZ",GapCooldownMinutes); if(BreakerCloseAllOnSpike) CloseAllPositionsForSymbol(InpSymbol); return false; } } g_last_bid=bid; g_last_ask=ask; g_last_tick_time=now; return true; } //+------------------------------------------------------------------+ //| EMA TREND FILTER — FUNCIONES CORE | //+------------------------------------------------------------------+ void SetupEMAHandle() { if(g_ema_handle != INVALID_HANDLE) { IndicatorRelease(g_ema_handle); g_ema_handle = INVALID_HANDLE; } g_ema_handle = iMA(InpSymbol, EMA_Timeframe, EMA_Period, 0, EMA_Method, EMA_AppliedPrice); if(g_ema_handle == INVALID_HANDLE) Print("❌ EMA Filter: Error creando handle | TF=", EnumToString(EMA_Timeframe), " | Period=", EMA_Period); else Print("✅ EMA Filter: Handle OK | TF=", EnumToString(EMA_Timeframe), " | Period=", EMA_Period, " | Method=", EnumToString(EMA_Method)); } void EvaluateEMATrend() { if(!EMA_Filter_Enable || !g_ema_filter_runtime) { g_ema_trend_status = "Desactivado"; g_ema_bullish = false; g_ema_bearish = false; return; } if(g_ema_handle == INVALID_HANDLE) { g_ema_trend_status = "❌ Sin handle"; g_ema_bullish = false; g_ema_bearish = false; return; } // Cache 5 segundos para no recalcular en cada tick datetime now = TimeCurrent(); if((now - g_ema_last_update) < 5 && g_ema_last_update > 0) return; g_ema_last_update = now; // Índice 1 = vela cerrada confirmada (más estable que vela en curso) double emaBuf[]; ArraySetAsSeries(emaBuf, true); if(CopyBuffer(g_ema_handle, 0, 1, 1, emaBuf) < 1) { g_ema_trend_status = "⏳ Cargando EMA..."; return; } g_ema_value = emaBuf[0]; // Precio de cierre de la misma vela confirmada double closeBuf[]; ArraySetAsSeries(closeBuf, true); if(CopyClose(InpSymbol, EMA_Timeframe, 1, 1, closeBuf) < 1) { g_ema_trend_status = "⏳ Cargando precio..."; return; } g_ema_close = closeBuf[0]; if(g_ema_value <= 0.0) { g_ema_trend_status = "❌ EMA=0"; return; } g_ema_bullish = (g_ema_close > g_ema_value); g_ema_bearish = (g_ema_close < g_ema_value); if(g_ema_bullish) g_ema_trend_status = StringFormat("🟢 ALCISTA (%."+IntegerToString(g_digits_cached)+"f)", g_ema_value); else if(g_ema_bearish) g_ema_trend_status = StringFormat("🔴 BAJISTA (%."+IntegerToString(g_digits_cached)+"f)", g_ema_value); else g_ema_trend_status = "⚪ NEUTRAL"; } bool EMA_AllowTrade(bool isBuy) { if(!EMA_Filter_Enable || !g_ema_filter_runtime) return true; if(g_ema_value <= 0.0) return true; // Sin datos → no bloquear if(isBuy && EMA_Filter_Buys) return g_ema_bullish; if(!isBuy && EMA_Filter_Sells) return g_ema_bearish; return true; } //+------------------------------------------------------------------+ //| ATR HELPERS | //+------------------------------------------------------------------+ bool GetATRValues(double &atrCurrent, double &atrAverage) { if(atrHandle==INVALID_HANDLE) return false; double atrBuffer[]; ArraySetAsSeries(atrBuffer,true); int needed=ATR_SmoothPeriod+1; if(needed<2) needed=2; if(CopyBuffer(atrHandle,0,0,needed,atrBuffer)ATR_MaxMultiplier){ atrAllowTrade=!ATR_BlockOnExtreme; atrStatus="🔴 ALTO ("+DoubleToString(atrRatio,2)+"x)"; } else{ atrAllowTrade=true; atrStatus="🟢 NORMAL ("+DoubleToString(atrRatio,2)+"x)"; } } color GetATRColor() { if(!g_ATR_Enabled_Runtime) return clrGray; if(atrRatioATR_MaxMultiplier) return ATR_HighColor; return ATR_NormalColor; } double GetATRBlockRate() { int totalSignals=signalCounter+atrBlockedSignals; if(totalSignals<=0) return 0.0; return (atrBlockedSignals*100.0/totalSignals); } string TimeframeToString(ENUM_TIMEFRAMES tf) { switch(tf){ case PERIOD_M1: return "M1"; case PERIOD_M5: return "M5"; case PERIOD_M15: return "M15"; case PERIOD_M30: return "M30"; case PERIOD_H1: return "H1"; case PERIOD_H4: return "H4"; case PERIOD_D1: return "D1"; default: return EnumToString(tf); } } void RecreateATRHandle() { if(atrHandle!=INVALID_HANDLE){ IndicatorRelease(atrHandle); atrHandle=INVALID_HANDLE; } g_atr_cache.valid=false; g_atr_cache.value=0.0; g_atr_cache.last_update=0; atrHandle=iATR(InpSymbol,g_ATR_TF_Runtime,ATRPeriod); if(atrHandle==INVALID_HANDLE){ atrAllowTrade=false; atrStatus="❌ ATR Handle"; return; } EvaluateATRStatus(); } //+------------------------------------------------------------------+ //| TIME FILTER | //+------------------------------------------------------------------+ datetime NowServer() { if(TimeFilterUseServerTime){ datetime st=TimeTradeServer(); if(st>0) return st; } return TimeCurrent(); } bool ParseTimeString(string timeStr, int &hourOut, int &minOut) { StringTrimLeft(timeStr); StringTrimRight(timeStr); int colonPos=StringFind(timeStr,":"); if(colonPos<0) return false; hourOut=(int)StringToInteger(StringSubstr(timeStr,0,colonPos)); minOut=(int)StringToInteger(StringSubstr(timeStr,colonPos+1)); if(hourOut<0||hourOut>23) return false; if(minOut<0||minOut>59) return false; return true; } int MinutesOfDay(datetime t){ MqlDateTime dt; TimeToStruct(t,dt); return dt.hour*60+dt.min; } bool ParseBlockWindows(string windowsStr,int &count,int &startsMin[],int &endsMin[]) { count=0; if(windowsStr=="") return true; string windows[]; int numWindows=StringSplit(windowsStr,';',windows); for(int i=0;i=startMin&&curMin<=endMin){ g_time_block_reason=StringFormat("🚫 Bloqueado (%02d:%02d)",dt.hour,dt.min); return true; } } else{ if(curMin>=startMin||curMin<=endMin){ g_time_block_reason=StringFormat("🚫 Bloqueado (%02d:%02d) cruza medianoche",dt.hour,dt.min); return true; } } } MqlDateTime dtNow; TimeToStruct(tServer,dtNow); g_time_block_reason=StringFormat("✅ Permitido (%02d:%02d)",dtNow.hour,dtNow.min); return false; } void EvaluateTimeFilter() { if(!EnableTimeFilter){ g_time_blocked=false; g_time_block_reason="Desactivado"; return; } g_time_blocked=IsInBlockedWindow(NowServer()); } void InitTimeFilter() { g_time_parse_error_logged=false; if(!ParseBlockWindows(BlockWindows,g_time_windows_count,g_time_starts_min,g_time_ends_min)){ g_time_windows_count=0; } if(g_time_windows_count>0){ Print("✅ TimeFilter: ",g_time_windows_count," ventanas configuradas"); } EvaluateTimeFilter(); } //+------------------------------------------------------------------+ //| NEWS FILTER - FOREX FACTORY | //+------------------------------------------------------------------+ bool CurrencyAllowed(string curList,string cur) { string s=curList; StringReplace(s," ",""); string arr[]; int n=StringSplit(s,',',arr); if(n<=0) return (s==cur); for(int i=0;i0?pPlus:(pMinus>0?pMinus:-1)); int offsetSec=0; if(p>0) { string base=StringSubstr(s,0,p); string off=StringSubstr(s,p); StringTrimLeft(off); StringTrimRight(off); int sign=(StringGetCharacter(off,0)=='-')?-1:1; int hh=(int)StringToInteger(StringSubstr(off,1,2)); int mm=(int)StringToInteger(StringSubstr(off,4,2)); offsetSec=sign*(hh*3600+mm*60); datetime tLocal=StringToTime(base); datetime tUTC=tLocal-offsetSec; int serverOffset=(int)(TimeCurrent()-TimeGMT()); return tUTC+serverOffset; } return StringToTime(s); } bool ParseForexFactoryJSON(string json) { ArrayResize(g_ff_news,0); g_ff_news_count=0; int pos=0,guard=0; while(true) { pos=StringFind(json,"\"title\":",pos); if(pos<0) break; FFNewsEvent news; int titleStart=StringFind(json,"\"title\":\"",pos); if(titleStart<0){ pos+=7; continue; } titleStart+=9; int titleEnd=StringFind(json,"\"",titleStart); if(titleEnd<0){ pos+=7; continue; } news.title=StringSubstr(json,titleStart,titleEnd-titleStart); int currStart=StringFind(json,"\"currency\":\"",pos); if(currStart<0) currStart=StringFind(json,"\"country\":\"",pos); if(currStart<0){ pos=titleEnd; continue; } string currKey=(StringFind(json,"\"currency\":\"",pos)>=0)?"\"currency\":\"":"\"country\":\""; currStart=StringFind(json,currKey,pos)+StringLen(currKey); int currEnd=StringFind(json,"\"",currStart); if(currEnd<0){ pos=titleEnd; continue; } news.currency=StringSubstr(json,currStart,currEnd-currStart); int impStart=StringFind(json,"\"impact\":\"",pos); if(impStart<0){ pos=titleEnd; continue; } impStart+=10; int impEnd=StringFind(json,"\"",impStart); if(impEnd<0){ pos=titleEnd; continue; } news.impact=StringSubstr(json,impStart,impEnd-impStart); int dateStart=StringFind(json,"\"date\":\"",pos); if(dateStart<0){ pos=titleEnd; continue; } dateStart+=8; int dateEnd=StringFind(json,"\"",dateStart); if(dateEnd<0){ pos=titleEnd; continue; } news.time=ParseFFDateTime(StringSubstr(json,dateStart,dateEnd-dateStart)); bool validCurrency=CurrencyAllowed(NewsFilterCurrencies,news.currency); bool validImpact=(StringFind(news.impact,"High")>=0&&NewsFilterHighImpact)||(StringFind(news.impact,"Medium")>=0&&NewsFilterMediumImpact); bool validTime=(news.time>=(TimeCurrent()-(NewsMinutesAfter*60))); if(validCurrency&&validImpact&&validTime){ int sz=g_ff_news_count; ArrayResize(g_ff_news,sz+1); g_ff_news[sz]=news; g_ff_news_count++; } pos=titleEnd+1; guard++; if(guard>3000) break; } g_ff_last_update=TimeCurrent(); Print("✅ FF parseados | Relevantes: ",g_ff_news_count); return true; } bool LoadNewsFromForexFactory() { char data[],result[]; string headers; string rh="Accept: application/json\r\n"; ResetLastError(); int res=WebRequest("GET",ForexFactoryURL,rh,5000,data,result,headers); if(res==-1){ int err=GetLastError(); if(err==4014) Print("❌ ERROR 4014: Agregar https://nfs.faireconomy.media en WebRequest"); return false; } if(res!=200){ Print("❌ HTTP error: ",res); return false; } string json=CharArrayToString(result,0,WHOLE_ARRAY,CP_UTF8); if(StringLen(json)<10) return false; return ParseForexFactoryJSON(json); } void FindNextNews_FF() { datetime now=TimeCurrent(); datetime closest=D'2099.12.31'; g_ff_next_title=""; g_ff_minutes_to_news=999; for(int i=0;inow&&g_ff_news[i].time=(g_ff_news[i].time-secBefore)&&now<=(g_ff_news[i].time+secAfter)) { g_ff_is_news_time=true; g_ff_next_title=g_ff_news[i].title; g_ff_minutes_to_news=(int)((g_ff_news[i].time-now)/60); return true; } } return false; } void UpdateNewsFilter_FF() { if(!g_EnableNewsFilter) return; if((TimeCurrent()-g_ff_last_update)>=3600||g_ff_news_count==0){ LoadNewsFromForexFactory(); FindNextNews_FF(); } } //+------------------------------------------------------------------+ //| FIN PARTE 2/4 - CONTINUAR CON PARTE 3/4 | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| XCRIPE 2.5 FULL - PARTE 3/4: BASKET SYSTEM + TRADING LOGIC | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| HELPERS UNIVERSALES DE POSICIÓN | //+------------------------------------------------------------------+ bool ResolveSideAndLevel_FromPosition(const long magic,const ENUM_POSITION_TYPE posType,bool &isBuy,int &lvl) { if(!IsOurMagic(magic)) return false; isBuy=(posType==POSITION_TYPE_BUY); if(InpUsePerLevelMagic){ bool tmp=false; int tl=0; if(!DecodeLevelFromMagic(magic,tmp,tl)) return false; lvl=tl; } else lvl=0; if(lvl<0) lvl=0; if(lvl>=LEVELS) lvl=LEVELS-1; return true; } bool ResolveSideAndLevel_FromDeal(const long magic,const long dealType,bool &isBuy,int &lvl) { if(!IsOurMagic(magic)) return false; isBuy=(dealType==DEAL_TYPE_BUY); if(InpUsePerLevelMagic){ bool tmp=false; int tl=0; if(!DecodeLevelFromMagic(magic,tmp,tl)) return false; lvl=tl; } else lvl=0; if(lvl<0) lvl=0; if(lvl>=LEVELS) lvl=LEVELS-1; return true; } //+------------------------------------------------------------------+ //| ✅ BASKET SYSTEM - NÚCLEO | //+------------------------------------------------------------------+ //--- Cuenta cuántos niveles distintos hay abiertos en una dirección int CountActiveBasketLevels(bool isBuy) { bool levelsFound[LEVELS]; for(int i=0;i=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(PositionGetString(POSITION_SYMBOL)!=InpSymbol) continue; long mg=(long)PositionGetInteger(POSITION_MAGIC); ENUM_POSITION_TYPE pt=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(isBuy&&pt!=POSITION_TYPE_BUY) continue; if(!isBuy&&pt!=POSITION_TYPE_SELL) continue; bool posIsBuy=false; int lvl=0; if(!ResolveSideAndLevel_FromPosition(mg,pt,posIsBuy,lvl)) continue; levelsFound[lvl]=true; } int count=0; for(int i=0;i=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(PositionGetString(POSITION_SYMBOL)!=InpSymbol) continue; long mg=(long)PositionGetInteger(POSITION_MAGIC); ENUM_POSITION_TYPE pt=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(isBuy&&pt!=POSITION_TYPE_BUY) continue; if(!isBuy&&pt!=POSITION_TYPE_SELL) continue; bool posIsBuy=false; int lvl=0; if(!ResolveSideAndLevel_FromPosition(mg,pt,posIsBuy,lvl)) continue; pnl+=PositionGetDouble(POSITION_PROFIT); } return pnl; } //--- Cuenta total de posiciones abiertas en una dirección int CountBasketPositions(bool isBuy) { int count=0; for(int i=PositionsTotal()-1;i>=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(PositionGetString(POSITION_SYMBOL)!=InpSymbol) continue; long mg=(long)PositionGetInteger(POSITION_MAGIC); ENUM_POSITION_TYPE pt=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(isBuy&&pt!=POSITION_TYPE_BUY) continue; if(!isBuy&&pt!=POSITION_TYPE_SELL) continue; if(!IsOurMagic(mg)) continue; count++; } return count; } //--- Elimina TP de todas las posiciones de la cesta (al activarse) void RemoveTPFromBasket(bool isBuy) { int removed=0; for(int i=PositionsTotal()-1;i>=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(PositionGetString(POSITION_SYMBOL)!=InpSymbol) continue; long mg=(long)PositionGetInteger(POSITION_MAGIC); ENUM_POSITION_TYPE pt=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(isBuy&&pt!=POSITION_TYPE_BUY) continue; if(!isBuy&&pt!=POSITION_TYPE_SELL) continue; if(!IsOurMagic(mg)) continue; double currentTP=PositionGetDouble(POSITION_TP); double currentSL=PositionGetDouble(POSITION_SL); // Solo remover si hay TP activo if(currentTP>0.0) { MqlTradeRequest req={}; MqlTradeResult res={}; req.action=TRADE_ACTION_SLTP; req.position=ticket; req.symbol=PositionGetString(POSITION_SYMBOL); req.sl=currentSL; req.tp=0.0; // ← eliminar TP req.magic=mg; req.deviation=50; ResetLastError(); if(OrderSend(req,res)) removed++; } } if(removed>0) Print("🧺 Basket ",isBuy?"BUY":"SELL"," | Eliminados ",removed," TPs individuales → cesta toma control"); } //--- Cierra toda la cesta de una dirección void CloseBasket(bool isBuy, string reason) { Print("🧺 CERRANDO CESTA ",isBuy?"BUY":"SELL"," | Razón: ",reason); int closed=0; double totalPnL=0.0; for(int i=PositionsTotal()-1;i>=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(PositionGetString(POSITION_SYMBOL)!=InpSymbol) continue; long mg=(long)PositionGetInteger(POSITION_MAGIC); ENUM_POSITION_TYPE pt=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(isBuy&&pt!=POSITION_TYPE_BUY) continue; if(!isBuy&&pt!=POSITION_TYPE_SELL) continue; if(!IsOurMagic(mg)) continue; double posPnL=PositionGetDouble(POSITION_PROFIT); if(trade.PositionClose(ticket)){ closed++; totalPnL+=posPnL; Print(" ✅ Cerrada #",ticket," PnL: $",DoubleToString(posPnL,2)); } else Print(" ❌ Error #",ticket," | ",trade.ResultRetcodeDescription()); } Print("✅ Cesta ",isBuy?"BUY":"SELL"," cerrada | Posiciones: ",closed," | PnL total: $",DoubleToString(totalPnL,2)); // Guardar resultado para panel string resultText=reason+" | $"+DoubleToString(totalPnL,2)+" | "+IntegerToString(closed)+" pos | "+TimeToString(TimeCurrent(),TIME_MINUTES); if(isBuy) g_basket_buy_last_result=resultText; else g_basket_sell_last_result=resultText; // Telegram if(EnableTelegramAlerts&&Basket_TelegramAlert) { string msg="🧺 CESTA "+(isBuy?"COMPRAS":"VENTAS")+" CERRADA\n\n"; msg+="📊 "+reason+"\n"; msg+="💰 PnL total: $"+DoubleToString(totalPnL,2)+"\n"; msg+="📦 Posiciones cerradas: "+IntegerToString(closed)+"\n"; msg+="⏰ "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES); Telegram_SendMessage(msg); } // Reset estado de cesta if(isBuy) { g_basket_buy_active=false; g_basket_buy_levels=0; g_basket_buy_pnl=0.0; g_basket_buy_open_time=0; g_basket_buy_total_pos=0; // Reset armed para permitir nuevas entradas for(int i=0;i4) minLvl=4; // ──────── CESTA BUY ──────── { int activeLevels=CountActiveBasketLevels(true); int activePos=CountBasketPositions(true); double floatPnL=(activePos>0)?GetBasketFloatingPnL(true):0.0; if(!g_basket_buy_active) { // ¿Se activa la cesta? if(activeLevels>=minLvl&&activePos>0) { g_basket_buy_active=true; g_basket_buy_levels=activeLevels; g_basket_buy_open_time=TimeCurrent(); g_basket_buy_total_pos=activePos; g_basket_buy_pnl=floatPnL; // Eliminar TPs individuales → cesta toma control RemoveTPFromBasket(true); Print("🧺 CESTA BUY ACTIVADA | Niveles: ",activeLevels," | Posiciones: ",activePos); Print(" Target: $",DoubleToString(Basket_Buy_TargetMoney,2)," | SL: $",DoubleToString(Basket_Buy_StopLossMoney,2)); if(EnableTelegramAlerts&&Basket_TelegramAlert) { string msg="🧺 CESTA COMPRAS ACTIVADA\n\n"; msg+="📊 Niveles activos: "+IntegerToString(activeLevels)+"\n"; msg+="📦 Posiciones: "+IntegerToString(activePos)+"\n"; msg+="🎯 Target: $"+DoubleToString(Basket_Buy_TargetMoney,2)+"\n"; msg+="🛑 SL cesta: $"+DoubleToString(Basket_Buy_StopLossMoney,2)+"\n"; msg+="⏰ "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES); Telegram_SendMessage(msg); } } } else // cesta ya activa { if(activePos<=0) { // Se cerraron todas (por SL individuales, spike, etc.) g_basket_buy_active=false; g_basket_buy_levels=0; g_basket_buy_pnl=0.0; g_basket_buy_open_time=0; g_basket_buy_total_pos=0; for(int i=0;ig_basket_buy_levels) RemoveTPFromBasket(true); // ¿Llegó al target? if(Basket_Buy_TargetMoney>0.0&&floatPnL>=Basket_Buy_TargetMoney) { CloseBasket(true,"🎯 TARGET $"+DoubleToString(Basket_Buy_TargetMoney,2)); } // ¿Llegó al stop loss de cesta? else if(Basket_Buy_StopLossMoney>0.0&&floatPnL<=-Basket_Buy_StopLossMoney) { CloseBasket(true,"🛑 STOP LOSS $"+DoubleToString(Basket_Buy_StopLossMoney,2)); } } } } // ──────── CESTA SELL ──────── { int activeLevels=CountActiveBasketLevels(false); int activePos=CountBasketPositions(false); double floatPnL=(activePos>0)?GetBasketFloatingPnL(false):0.0; if(!g_basket_sell_active) { if(activeLevels>=minLvl&&activePos>0) { g_basket_sell_active=true; g_basket_sell_levels=activeLevels; g_basket_sell_open_time=TimeCurrent(); g_basket_sell_total_pos=activePos; g_basket_sell_pnl=floatPnL; RemoveTPFromBasket(false); Print("🧺 CESTA SELL ACTIVADA | Niveles: ",activeLevels," | Posiciones: ",activePos); Print(" Target: $",DoubleToString(Basket_Sell_TargetMoney,2)," | SL: $",DoubleToString(Basket_Sell_StopLossMoney,2)); if(EnableTelegramAlerts&&Basket_TelegramAlert) { string msg="🧺 CESTA VENTAS ACTIVADA\n\n"; msg+="📊 Niveles activos: "+IntegerToString(activeLevels)+"\n"; msg+="📦 Posiciones: "+IntegerToString(activePos)+"\n"; msg+="🎯 Target: $"+DoubleToString(Basket_Sell_TargetMoney,2)+"\n"; msg+="🛑 SL cesta: $"+DoubleToString(Basket_Sell_StopLossMoney,2)+"\n"; msg+="⏰ "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES); Telegram_SendMessage(msg); } } } else { if(activePos<=0) { g_basket_sell_active=false; g_basket_sell_levels=0; g_basket_sell_pnl=0.0; g_basket_sell_open_time=0; g_basket_sell_total_pos=0; for(int i=0;ig_basket_sell_levels) RemoveTPFromBasket(false); if(Basket_Sell_TargetMoney>0.0&&floatPnL>=Basket_Sell_TargetMoney) { CloseBasket(false,"🎯 TARGET $"+DoubleToString(Basket_Sell_TargetMoney,2)); } else if(Basket_Sell_StopLossMoney>0.0&&floatPnL<=-Basket_Sell_StopLossMoney) { CloseBasket(false,"🛑 STOP LOSS $"+DoubleToString(Basket_Sell_StopLossMoney,2)); } } } } } //+------------------------------------------------------------------+ //| ALARMAS RSI | //+------------------------------------------------------------------+ void CheckRSIAlarms() { if(BlockAlarmsInTimeFilter&&EnableTimeFilter&&g_time_blocked) return; datetime now=TimeCurrent(); // ALARMA 1 if(Alarm1_Enabled&&rsiHandleA1!=INVALID_HANDLE) { double rsiCur=0,rsiPrev=0; if(GetRSI2(rsiHandleA1,rsiCur,rsiPrev)) { bool triggered=false; string condText=""; if(Alarm1_Mode==ALARM_CROSS_BELOW){ if(g_alarm1Armed&&rsiCur<=Alarm1_Level){ triggered=true; condText="<="+DoubleToString(Alarm1_Level,1); } if(rsiCur>Alarm1_Level) g_alarm1Armed=true; } else if(Alarm1_Mode==ALARM_CROSS_ABOVE){ if(g_alarm1Armed&&rsiCur>=Alarm1_Level){ triggered=true; condText=">="+DoubleToString(Alarm1_Level,1); } if(rsiCurAlarm1_Level) g_alarm1Armed=true; } else if(Alarm1_Mode==ALARM_ABOVE_EQ){ if(g_alarm1Armed&&rsiCur>=Alarm1_Level){ triggered=true; condText=">="+DoubleToString(Alarm1_Level,1); } if(rsiCur=g_alarm_cooldown_sec) { g_alarm1Armed=false; g_lastAlarm1=now; g_lastAlarmText="Alarma 1: RSI "+condText; g_lastAlarmTime=now; string msg="ALARMA 1 | RSI "+condText+" | Actual: "+DoubleToString(rsiCur,2)+" | "+TimeToString(now,TIME_DATE|TIME_MINUTES); if(Alarm1_Message!="") msg+="\n💬 "+Alarm1_Message; if(EnableSoundAlerts&&Alarm1_Sound!="") PlaySound(Alarm1_Sound); if(EnablePopupAlerts) Alert("🚨 "+msg); if(EnableTelegramAlerts&&Alarm1_Telegram) Telegram_SendMessage(msg); } } } // ALARMA 2 if(Alarm2_Enabled&&rsiHandleA2!=INVALID_HANDLE) { double rsiCur=0,rsiPrev=0; if(GetRSI2(rsiHandleA2,rsiCur,rsiPrev)) { bool triggered=false; string condText=""; if(Alarm2_Mode==ALARM_CROSS_BELOW){ if(g_alarm2Armed&&rsiCur<=Alarm2_Level){ triggered=true; condText="<="+DoubleToString(Alarm2_Level,1); } if(rsiCur>Alarm2_Level) g_alarm2Armed=true; } else if(Alarm2_Mode==ALARM_CROSS_ABOVE){ if(g_alarm2Armed&&rsiCur>=Alarm2_Level){ triggered=true; condText=">="+DoubleToString(Alarm2_Level,1); } if(rsiCurAlarm2_Level) g_alarm2Armed=true; } else if(Alarm2_Mode==ALARM_ABOVE_EQ){ if(g_alarm2Armed&&rsiCur>=Alarm2_Level){ triggered=true; condText=">="+DoubleToString(Alarm2_Level,1); } if(rsiCur=g_alarm_cooldown_sec) { g_alarm2Armed=false; g_lastAlarm2=now; g_lastAlarmText="Alarma 2: RSI "+condText; g_lastAlarmTime=now; string msg="ALARMA 2 | RSI "+condText+" | Actual: "+DoubleToString(rsiCur,2)+" | "+TimeToString(now,TIME_DATE|TIME_MINUTES); if(Alarm2_Message!="") msg+="\n💬 "+Alarm2_Message; if(EnableSoundAlerts&&Alarm2_Sound!="") PlaySound(Alarm2_Sound); if(EnablePopupAlerts) Alert("🚨 "+msg); if(EnableTelegramAlerts&&Alarm2_Telegram) Telegram_SendMessage(msg); } } } // ALARMA 3 if(Alarm3_Enabled&&rsiHandleA3!=INVALID_HANDLE) { double rsiCur=0,rsiPrev=0; if(GetRSI2(rsiHandleA3,rsiCur,rsiPrev)) { bool triggered=false; string condText=""; if(Alarm3_Mode==ALARM_CROSS_BELOW){ if(g_alarm3Armed&&rsiCur<=Alarm3_Level){ triggered=true; condText="<="+DoubleToString(Alarm3_Level,1); } if(rsiCur>Alarm3_Level) g_alarm3Armed=true; } else if(Alarm3_Mode==ALARM_CROSS_ABOVE){ if(g_alarm3Armed&&rsiCur>=Alarm3_Level){ triggered=true; condText=">="+DoubleToString(Alarm3_Level,1); } if(rsiCurAlarm3_Level) g_alarm3Armed=true; } else if(Alarm3_Mode==ALARM_ABOVE_EQ){ if(g_alarm3Armed&&rsiCur>=Alarm3_Level){ triggered=true; condText=">="+DoubleToString(Alarm3_Level,1); } if(rsiCur=g_alarm_cooldown_sec) { g_alarm3Armed=false; g_lastAlarm3=now; g_lastAlarmText="Alarma 3: RSI "+condText; g_lastAlarmTime=now; string msg="ALARMA 3 | RSI "+condText+" | Actual: "+DoubleToString(rsiCur,2)+" | "+TimeToString(now,TIME_DATE|TIME_MINUTES); if(Alarm3_Message!="") msg+="\n💬 "+Alarm3_Message; if(EnableSoundAlerts&&Alarm3_Sound!="") PlaySound(Alarm3_Sound); if(EnablePopupAlerts) Alert("🚨 "+msg); if(EnableTelegramAlerts&&Alarm3_Telegram) Telegram_SendMessage(msg); } } } } //+------------------------------------------------------------------+ //| STATS | //+------------------------------------------------------------------+ void UpdateStats() { datetime now=TimeCurrent(); if((now-g_last_stats_calc)<60) return; if(!HistorySelect(0,now)) return; g_cached_total_trades=0; g_cached_win_trades=0; g_cached_loss_trades=0; g_cached_total_profit=0; int dealsTotal=HistoryDealsTotal(); for(int i=0;i0) g_cached_win_trades++; else if(netProfit<0) g_cached_loss_trades++; } g_last_stats_calc=now; totalTrades=g_cached_total_trades; winTrades=g_cached_win_trades; lossTrades=g_cached_loss_trades; totalProfit=g_cached_total_profit; } //+------------------------------------------------------------------+ //| LOT CALCULATION | //+------------------------------------------------------------------+ double CalculateLot(double riskPercent,int numOrders,int slPoints) { if(!g_UseRiskCalculation) return g_FixedLotSize; if(numOrders<=0||riskPercent<=0.0||slPoints<=0) return g_FixedLotSize; double balance=AccountInfoDouble(ACCOUNT_BALANCE); double riskPerOrder=balance*(riskPercent/100.0)/(double)numOrders; if(g_tick_value_cached<=0.0||g_tick_size_cached<=0.0) return g_FixedLotSize; double lossPerLotAtSL=(slPoints*g_point_cached/g_tick_size_cached)*g_tick_value_cached; if(lossPerLotAtSL<=0.0) return g_FixedLotSize; double lot=riskPerOrder/lossPerLotAtSL; if(lotg_lot_max_cached) lot=g_lot_max_cached; lot=NormalizeDouble(MathFloor(lot/g_lot_step_cached)*g_lot_step_cached,VolumeDigitsFromStep(g_lot_step_cached)); if(lot<=0) lot=g_FixedLotSize; return lot; } //+------------------------------------------------------------------+ //| STOP VALIDATION | //+------------------------------------------------------------------+ bool ValidateStopDistance(double price,double sl,bool isBuy) { long stopsLevel=SymbolInfoInteger(InpSymbol,SYMBOL_TRADE_STOPS_LEVEL); long freezeLevel=SymbolInfoInteger(InpSymbol,SYMBOL_TRADE_FREEZE_LEVEL); if(stopsLevel==0&&freezeLevel==0) return true; double minDist=MathMax(stopsLevel,freezeLevel)*g_point_cached; double currentDist=isBuy?(price-sl):(sl-price); if(currentDist0&&sl<=0.0){ Print("❌ BUY FAIL: SL inválido"); return; } if(slPts>0&&!ValidateStopDistance(ask,sl,true)){ Print("❌ BUY FAIL: SL no cumple distancia mínima"); return; } trade.SetExpertMagicNumber(MagicForLevel(true,levelIndex)); trade.SetDeviationInPoints(50); int successCount=0,consecutiveFails=0; for(int i=0;i=3) break; } } Print("✅ ",successCount,"/",numOrders," BUY | ",label," | SL=",slPts,"pts | TP=",(basketActive?0:tpPts),"pts",(basketActive?" [CESTA]":"")); lastSignalType=label; lastSignalTime=TimeCurrent(); } void OpenBurstSells(int levelIndex,int numOrders,double riskPercent,string label) { if(numOrders<=0||riskPercent<=0.0) return; double bid=SymbolInfoDouble(InpSymbol,SYMBOL_BID); if(bid<=0.0){ Print("❌ SELL FAIL: BID inválido"); return; } int tpPts=g_sell_tp[levelIndex]; int slPts=g_sell_sl[levelIndex]; double lot=CalculateLot(riskPercent,numOrders,slPts); double sl=PriceFromPoints(bid,slPts,false,true); double tp=PriceFromPoints(bid,tpPts,false,false); // ✅ Si la cesta ya está activa para SELL → no poner TP bool basketActive=g_basket_sell_active; if(basketActive) tp=0.0; if(slPts>0&&sl<=0.0){ Print("❌ SELL FAIL: SL inválido"); return; } if(slPts>0&&!ValidateStopDistance(bid,sl,false)){ Print("❌ SELL FAIL: SL no cumple distancia mínima"); return; } trade.SetExpertMagicNumber(MagicForLevel(false,levelIndex)); trade.SetDeviationInPoints(50); int successCount=0,consecutiveFails=0; for(int i=0;i=3) break; } } Print("✅ ",successCount,"/",numOrders," SELL | ",label," | SL=",slPts,"pts | TP=",(basketActive?0:tpPts),"pts",(basketActive?" [CESTA]":"")); lastSignalType=label; lastSignalTime=TimeCurrent(); } //+------------------------------------------------------------------+ //| TRAILING STOP | //+------------------------------------------------------------------+ bool ModifyPositionSLTP(ulong ticket,double newSL,double newTP) { if(!PositionSelectByTicket(ticket)) return false; MqlTradeRequest request={}; MqlTradeResult result={}; request.action=TRADE_ACTION_SLTP; request.position=ticket; request.symbol=PositionGetString(POSITION_SYMBOL); request.sl=NormalizeDouble(newSL,g_digits_cached); request.tp=NormalizeDouble(newTP,g_digits_cached); request.magic=PositionGetInteger(POSITION_MAGIC); request.deviation=50; ResetLastError(); if(!OrderSend(request,result)) return false; if(result.retcode!=TRADE_RETCODE_DONE&&result.retcode!=TRADE_RETCODE_PLACED&&result.retcode!=TRADE_RETCODE_DONE_PARTIAL) return false; return true; } void ManageTrailingStop() { // ✅ El trailing solo aplica a posiciones que NO están en cesta activa // (la cesta gestiona el cierre total, el trailing puede mover SL) for(int i=PositionsTotal()-1;i>=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket<=0) continue; if(PositionGetString(POSITION_SYMBOL)!=InpSymbol) continue; long mg=(long)PositionGetInteger(POSITION_MAGIC); ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); bool isBuy=false; int lvl=0; if(!ResolveSideAndLevel_FromPosition(mg,type,isBuy,lvl)) continue; bool tsEnabled=isBuy?g_buy_ts_en[lvl]:g_sell_ts_en[lvl]; if(!tsEnabled) continue; int tsStart=isBuy?g_buy_ts_start[lvl]:g_sell_ts_start[lvl]; int tsStep=isBuy?g_buy_ts_step[lvl]:g_sell_ts_step[lvl]; int tsDist=isBuy?g_buy_ts_dist[lvl]:g_sell_ts_dist[lvl]; double openPrice=PositionGetDouble(POSITION_PRICE_OPEN); double currentSL=PositionGetDouble(POSITION_SL); double currentTP=PositionGetDouble(POSITION_TP); if(type==POSITION_TYPE_BUY) { double currentPrice=SymbolInfoDouble(InpSymbol,SYMBOL_BID); if(currentPrice<=0) continue; double profitPoints=(currentPrice-openPrice)/g_point_cached; if(profitPoints>=tsStart) { double newSL=NormalizeDouble(currentPrice-tsDist*g_point_cached,g_digits_cached); if(newSL>currentSL+tsStep*g_point_cached||currentSL==0) { if(newSL<=openPrice) newSL=openPrice+10*g_point_cached; if(!ValidateStopDistance(currentPrice,newSL,true)) continue; ModifyPositionSLTP(ticket,newSL,currentTP); } } } else if(type==POSITION_TYPE_SELL) { double currentPrice=SymbolInfoDouble(InpSymbol,SYMBOL_ASK); if(currentPrice<=0) continue; double profitPoints=(openPrice-currentPrice)/g_point_cached; if(profitPoints>=tsStart) { double newSL=NormalizeDouble(currentPrice+tsDist*g_point_cached,g_digits_cached); if(currentSL==0||newSL=openPrice) newSL=openPrice-10*g_point_cached; if(!ValidateStopDistance(currentPrice,newSL,false)) continue; ModifyPositionSLTP(ticket,newSL,currentTP); } } } } } //+------------------------------------------------------------------+ //| RSI CACHE | //+------------------------------------------------------------------+ bool GetRSI2_Cached(int handle,double &rsiCur,double &rsiPrev,IndicatorCache &cache,int cacheSeconds=5) { datetime now=TimeCurrent(); if(cache.valid&&(now-cache.last_update)=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(PositionGetString(POSITION_SYMBOL)!=InpSymbol) continue; long pmg=PositionGetInteger(POSITION_MAGIC); if(pmg!=mg) continue; ENUM_POSITION_TYPE pt=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(isBuy&&pt!=POSITION_TYPE_BUY) continue; if(!isBuy&&pt!=POSITION_TYPE_SELL) continue; return true; } return false; } //+------------------------------------------------------------------+ //| EJECUTORES DE SEÑAL | //+------------------------------------------------------------------+ void ExecuteBuySignal(int level,datetime barTime,double rsiCur,double rsiPrev) { int idx=level-1; if(level==1&&barTime==lastSignalTimeRSI_Buy1) return; if(level==2&&barTime==lastSignalTimeRSI_Buy2) return; if(level==3&&barTime==lastSignalTimeRSI_Buy3) return; if(level==4&&barTime==lastSignalTimeRSI_Buy4) return; if(level==1) lastSignalTimeRSI_Buy1=barTime; if(level==2) lastSignalTimeRSI_Buy2=barTime; if(level==3) lastSignalTimeRSI_Buy3=barTime; if(level==4) lastSignalTimeRSI_Buy4=barTime; int orders=g_buy_orders[idx]; double risk=g_buy_risk[idx]; if(UseProgressiveRisk) risk=risk*MathPow(ProgressiveMultiplier,level-1); string levelName="BUY L"+IntegerToString(level); signalCounter++; Print("🚨 ",levelName," TOUCH | RSI: ",DoubleToString(rsiCur,2)); OpenBurstBuys(idx,orders,risk,levelName); g_last_any_signal=TimeCurrent(); } void ExecuteSellSignal(int level,datetime barTime,double rsiCur,double rsiPrev) { int idx=level-1; if(level==1&&barTime==lastSignalTimeRSI_Sell1) return; if(level==2&&barTime==lastSignalTimeRSI_Sell2) return; if(level==3&&barTime==lastSignalTimeRSI_Sell3) return; if(level==4&&barTime==lastSignalTimeRSI_Sell4) return; if(level==1) lastSignalTimeRSI_Sell1=barTime; if(level==2) lastSignalTimeRSI_Sell2=barTime; if(level==3) lastSignalTimeRSI_Sell3=barTime; if(level==4) lastSignalTimeRSI_Sell4=barTime; int orders=g_sell_orders[idx]; double risk=g_sell_risk[idx]; if(UseProgressiveRisk) risk=risk*MathPow(ProgressiveMultiplier,level-1); string levelName="SELL L"+IntegerToString(level); signalCounter++; Print("🚨 ",levelName," TOUCH | RSI: ",DoubleToString(rsiCur,2)); OpenBurstSells(idx,orders,risk,levelName); g_last_any_signal=TimeCurrent(); } //+------------------------------------------------------------------+ //| CHECK SIGNAL TRADING | //+------------------------------------------------------------------+ void CheckSignalTrading(datetime currentBarTime) { if((TimeCurrent()-g_last_any_signal)<3) return; if(!IsSpreadOk()) return; if(!CheckLiquidityGap()) return; if(IsTradeLocked()){ static datetime lw=0; if((TimeCurrent()-lw)>=300){ Print("🔒 Trading bloqueado: ",g_lock_reason); lw=TimeCurrent(); } return; } if(!CheckDailyLoss()) return; if(!CheckDailyTarget()) return; if(IsNewsTime_FF()){ g_ff_blocked_count++; return; } if(!CheckSpikeBreaker()) return; if(TimeCurrent()=60){ Print("⚡ Circuit Breaker: ",g_breaker_reason); lw=TimeCurrent(); } return; } if(!IsSpreadOk()) return; bool timeBlocked=false; if(EnableTimeFilter){ EvaluateTimeFilter(); timeBlocked=g_time_blocked; } if(g_ATR_Enabled_Runtime) EvaluateATRStatus(); double rsiCur=0,rsiPrev=0; if(!GetRSI2_Cached(rsiHandleTrade,rsiCur,rsiPrev,g_rsi_trade_cache,1)) return; currentRSI=rsiCur; // ARM/DISARM for(int i=0;ig_buy_rsi[i]) g_buyArmed[i]=true; if(currentRSI0&&g_buy_risk[3]>0&&g_buyArmed[3]&¤tRSI<=g_buy_rsi[3]) buyLevel=4; else if(g_buyLevel3Enabled&&g_buy_en[2]&&g_buy_orders[2]>0&&g_buy_risk[2]>0&&g_buyArmed[2]&¤tRSI<=g_buy_rsi[2]) buyLevel=3; else if(g_buyLevel2Enabled&&g_buy_en[1]&&g_buy_orders[1]>0&&g_buy_risk[1]>0&&g_buyArmed[1]&¤tRSI<=g_buy_rsi[1]) buyLevel=2; else if(g_buyLevel1Enabled&&g_buy_en[0]&&g_buy_orders[0]>0&&g_buy_risk[0]>0&&g_buyArmed[0]&¤tRSI<=g_buy_rsi[0]) buyLevel=1; // DETECTAR SELL int sellLevel=0; if(g_ActivarVentas) { if(g_sellLevel4Enabled&&g_sell_en[3]&&g_sell_orders[3]>0&&g_sell_risk[3]>0&&g_sellArmed[3]&¤tRSI>=g_sell_rsi[3]) sellLevel=4; else if(g_sellLevel3Enabled&&g_sell_en[2]&&g_sell_orders[2]>0&&g_sell_risk[2]>0&&g_sellArmed[2]&¤tRSI>=g_sell_rsi[2]) sellLevel=3; else if(g_sellLevel2Enabled&&g_sell_en[1]&&g_sell_orders[1]>0&&g_sell_risk[1]>0&&g_sellArmed[1]&¤tRSI>=g_sell_rsi[1]) sellLevel=2; else if(g_sellLevel1Enabled&&g_sell_en[0]&&g_sell_orders[0]>0&&g_sell_risk[0]>0&&g_sellArmed[0]&¤tRSI>=g_sell_rsi[0]) sellLevel=1; } // EJECUTAR BUY if(buyLevel>0) { if(timeBlocked&&BlockBuysInTimeFilter){ g_time_block_count++; return; } if(HasOpenPositionsForLevel(true,buyLevel-1)){ Print("⛔ BUY L",buyLevel," bloqueado: nivel ya abierto"); return; } if(g_ATR_Enabled_Runtime&&!atrAllowTrade){ atrBlockedSignals++; return; } // ✅ FILTRO EMA TENDENCIA if(!EMA_AllowTrade(true)) { g_ema_blocked_buys++; static datetime lastEMAWarnBuy=0; if((TimeCurrent()-lastEMAWarnBuy)>=60) { Print("📊 EMA bloqueó BUY L",buyLevel," | ",g_ema_trend_status, " | Cierre: ",DoubleToString(g_ema_close,g_digits_cached), " | EMA",EMA_Period,": ",DoubleToString(g_ema_value,g_digits_cached)); lastEMAWarnBuy=TimeCurrent(); } return; } g_buyArmed[buyLevel-1]=false; ExecuteBuySignal(buyLevel,currentBarTime,currentRSI,rsiPrev); return; } // EJECUTAR SELL if(sellLevel>0) { if(timeBlocked&&BlockSellsInTimeFilter){ g_time_block_count++; return; } if(HasOpenPositionsForLevel(false,sellLevel-1)){ Print("⛔ SELL L",sellLevel," bloqueado: nivel ya abierto"); return; } if(g_ATR_Enabled_Runtime&&!atrAllowTrade){ atrBlockedSignals++; return; } // ✅ FILTRO EMA TENDENCIA if(!EMA_AllowTrade(false)) { g_ema_blocked_sells++; static datetime lastEMAWarnSell=0; if((TimeCurrent()-lastEMAWarnSell)>=60) { Print("📊 EMA bloqueó SELL L",sellLevel," | ",g_ema_trend_status, " | Cierre: ",DoubleToString(g_ema_close,g_digits_cached), " | EMA",EMA_Period,": ",DoubleToString(g_ema_value,g_digits_cached)); lastEMAWarnSell=TimeCurrent(); } return; } g_sellArmed[sellLevel-1]=false; ExecuteSellSignal(sellLevel,currentBarTime,currentRSI,rsiPrev); } } //+------------------------------------------------------------------+ //| FIN PARTE 3/4 - CONTINUAR CON PARTE 4/4 | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| XCRIPE 2.5 FULL - PARTE 4/4: PANEL, PRESETS, INIT, EVENTS | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| PANEL UI | //+------------------------------------------------------------------+ void CreateLabel(string name,int x,int y,string text,int size,color clr,string font="Arial Bold") { if(ObjectFind(0,name)>=0) ObjectDelete(0,name); ObjectCreate(0,name,OBJ_LABEL,0,0,0); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetString(0,name,OBJPROP_FONT,font); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,size); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); } void CreateRect(string name,int x,int y,int w,int h,color clr,bool fill=true) { if(ObjectFind(0,name)>=0) ObjectDelete(0,name); ObjectCreate(0,name,OBJ_RECTANGLE_LABEL,0,0,0); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetInteger(0,name,OBJPROP_BGCOLOR,clr); ObjectSetInteger(0,name,OBJPROP_BORDER_TYPE,BORDER_FLAT); ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetInteger(0,name,OBJPROP_FILL,fill); } void CreateButton(string name,int x,int y,int w,int h,string text,color bgColor,color txtColor) { if(ObjectFind(0,name)>=0) ObjectDelete(0,name); ObjectCreate(0,name,OBJ_BUTTON,0,0,0); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgColor); ObjectSetInteger(0,name,OBJPROP_COLOR,txtColor); ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,C'60,60,80'); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetString(0,name,OBJPROP_FONT,"Arial Bold"); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,name,OBJPROP_STATE,false); } void CreatePanel() { if(!ShowPanel) return; int w=370; // Altura dinámica: si basket activo agrega sección int h=Basket_ShowInPanel ? 680 : 600; CreateRect(panelPrefix+"BG",PanelX,PanelY,w,h,PanelBgColor); CreateRect(panelPrefix+"Header",PanelX,PanelY,w,28,PanelHeaderColor); CreateLabel(panelPrefix+"Title",PanelX+10,PanelY+7,"🔥 XCRIPE 2.5 FULL - BASKET + EMA",9,clrWhite); int yPos=PanelY+35; // ESTADO VERSIÓN + RSI CreateLabel(panelPrefix+"LicLabel",PanelX+10,yPos,"Lic:",7,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"LicStatus",PanelX+40,yPos,g_license_status,7,clrLime,"Arial Bold"); CreateLabel(panelPrefix+"RSILabel",PanelX+190,yPos,"RSI:",7,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"RSIValue",PanelX+220,yPos,"0.00",7,clrYellow,"Arial Bold"); yPos+=18; // SEÑAL CreateLabel(panelPrefix+"SignalLabel",PanelX+10,yPos,"Señal:",7,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"SignalType",PanelX+55,yPos,"-",7,clrCyan,"Arial Bold"); yPos+=15; CreateLabel(panelPrefix+"SignalTime",PanelX+10,yPos,"-",6,C'180,180,180',"Arial"); yPos+=18; // ESTADO GENERAL CreateLabel(panelPrefix+"BlockReason",PanelX+10,yPos,"✅ ACTIVO",8,clrLime,"Arial Bold"); yPos+=20; CreateRect(panelPrefix+"Sep0",PanelX+10,yPos,w-20,1,C'60,60,80'); yPos+=8; // ✅ BASKET BUY if(Basket_ShowInPanel) { CreateLabel(panelPrefix+"BkBuyTitle",PanelX+10,yPos,"🧺 CESTA COMPRAS",7,C'80,200,120',"Arial Bold"); yPos+=15; CreateLabel(panelPrefix+"BkBuyStatusLbl",PanelX+10,yPos,"Estado:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkBuyStatus",PanelX+55,yPos,"⏸ Inactiva",6,clrGray,"Arial Bold"); CreateLabel(panelPrefix+"BkBuyLvlLbl",PanelX+180,yPos,"Niv:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkBuyLvl",PanelX+205,yPos,"0",6,clrWhite,"Arial"); yPos+=14; CreateLabel(panelPrefix+"BkBuyPnLLbl",PanelX+10,yPos,"PnL:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkBuyPnL",PanelX+40,yPos,"$0.00",6,clrWhite,"Arial Bold"); CreateLabel(panelPrefix+"BkBuyPosLbl",PanelX+130,yPos,"Pos:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkBuyPos",PanelX+155,yPos,"0",6,clrWhite,"Arial"); CreateLabel(panelPrefix+"BkBuyTargLbl",PanelX+200,yPos,"Tgt:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkBuyTarg",PanelX+225,yPos,"$"+DoubleToString(Basket_Buy_TargetMoney,0),6,clrLime,"Arial"); yPos+=12; CreateLabel(panelPrefix+"BkBuyLastResult",PanelX+10,yPos,"-",6,C'150,150,150',"Arial"); yPos+=16; // ✅ BASKET SELL CreateLabel(panelPrefix+"BkSellTitle",PanelX+10,yPos,"🧺 CESTA VENTAS",7,C'220,80,80',"Arial Bold"); yPos+=15; CreateLabel(panelPrefix+"BkSellStatusLbl",PanelX+10,yPos,"Estado:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkSellStatus",PanelX+55,yPos,"⏸ Inactiva",6,clrGray,"Arial Bold"); CreateLabel(panelPrefix+"BkSellLvlLbl",PanelX+180,yPos,"Niv:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkSellLvl",PanelX+205,yPos,"0",6,clrWhite,"Arial"); yPos+=14; CreateLabel(panelPrefix+"BkSellPnLLbl",PanelX+10,yPos,"PnL:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkSellPnL",PanelX+40,yPos,"$0.00",6,clrWhite,"Arial Bold"); CreateLabel(panelPrefix+"BkSellPosLbl",PanelX+130,yPos,"Pos:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkSellPos",PanelX+155,yPos,"0",6,clrWhite,"Arial"); CreateLabel(panelPrefix+"BkSellTargLbl",PanelX+200,yPos,"Tgt:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BkSellTarg",PanelX+225,yPos,"$"+DoubleToString(Basket_Sell_TargetMoney,0),6,clrRed,"Arial"); yPos+=12; CreateLabel(panelPrefix+"BkSellLastResult",PanelX+10,yPos,"-",6,C'150,150,150',"Arial"); yPos+=16; CreateRect(panelPrefix+"SepBK",PanelX+10,yPos,w-20,1,C'60,60,80'); yPos+=8; } // ✅ EMA TREND FILTER — Sección panel if(EMA_ShowInPanel) { CreateRect(panelPrefix+"SepEMA",PanelX+10,yPos,w-20,1,C'60,60,80'); yPos+=8; CreateLabel(panelPrefix+"EMATitle",PanelX+10,yPos,"📊 EMA TREND FILTER",7,PanelHeaderColor,"Arial Bold"); CreateButton(panelPrefix+"BtnEMA",PanelX+230,yPos-2,110,18,"EMA: ON",clrLime,clrBlack); yPos+=20; CreateLabel(panelPrefix+"EMAStatusLbl",PanelX+10,yPos,"Tendencia:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"EMAStatus",PanelX+72,yPos,"-",6,clrWhite,"Arial Bold"); yPos+=14; CreateLabel(panelPrefix+"EMAValueLbl",PanelX+10,yPos,"EMA:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"EMAValue",PanelX+40,yPos,"0.00",6,clrWhite,"Arial"); CreateLabel(panelPrefix+"EMABlkLbl",PanelX+160,yPos,"Bloq:",6,C'180,180,180',"Arial"); CreateLabel(panelPrefix+"EMABlkBuy",PanelX+195,yPos,"B:0",6,clrLime,"Arial"); CreateLabel(panelPrefix+"EMABlkSell",PanelX+240,yPos,"S:0",6,clrRed,"Arial"); yPos+=18; } // CONTROL DE NIVELES CreateLabel(panelPrefix+"BuyControlLabel",PanelX+10,yPos,"🔵 COMPRAS:",7,clrLime,"Arial Bold"); yPos+=18; CreateButton(panelPrefix+"BtnBuy1",PanelX+10, yPos,78,22,"L1",clrLime,clrBlack); CreateButton(panelPrefix+"BtnBuy2",PanelX+94, yPos,78,22,"L2",clrLime,clrBlack); CreateButton(panelPrefix+"BtnBuy3",PanelX+178,yPos,78,22,"L3",clrLime,clrBlack); CreateButton(panelPrefix+"BtnBuy4",PanelX+262,yPos,78,22,"L4",clrLime,clrBlack); yPos+=30; CreateLabel(panelPrefix+"SellControlLabel",PanelX+10,yPos,"🔴 VENTAS:",7,clrRed,"Arial Bold"); yPos+=18; CreateButton(panelPrefix+"BtnSell1",PanelX+10, yPos,78,22,"L1",clrRed,clrWhite); CreateButton(panelPrefix+"BtnSell2",PanelX+94, yPos,78,22,"L2",clrRed,clrWhite); CreateButton(panelPrefix+"BtnSell3",PanelX+178,yPos,78,22,"L3",clrRed,clrWhite); CreateButton(panelPrefix+"BtnSell4",PanelX+262,yPos,78,22,"L4",clrRed,clrWhite); yPos+=28; CreateRect(panelPrefix+"Sep1",PanelX+10,yPos,w-20,1,C'60,60,80'); yPos+=8; // DAILY LIMITS if(EnableDailyLimits) { CreateLabel(panelPrefix+"DailyTitle",PanelX+10,yPos,"💰 DAILY LIMITS",7,PanelHeaderColor,"Arial Bold"); yPos+=15; CreateLabel(panelPrefix+"DailyPnL",PanelX+10,yPos,"PnL:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"DailyPnLValue",PanelX+50,yPos,"$0",6,clrWhite,"Arial Bold"); CreateLabel(panelPrefix+"DailyDD",PanelX+185,yPos,"DD:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"DailyDDValue",PanelX+210,yPos,"0%",6,clrWhite,"Arial"); yPos+=14; CreateLabel(panelPrefix+"DailyStatus",PanelX+10,yPos,"Estado:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"DailyStatusValue",PanelX+55,yPos,"✅ OK",6,clrLime,"Arial Bold"); yPos+=18; CreateRect(panelPrefix+"Sep1c",PanelX+10,yPos,w-20,1,C'60,60,80'); yPos+=8; } // CIRCUIT BREAKER if(EnableCircuitBreaker) { CreateLabel(panelPrefix+"BreakerTitle",PanelX+10,yPos,"⚡ CIRCUIT BREAKER",7,PanelHeaderColor,"Arial Bold"); yPos+=15; CreateLabel(panelPrefix+"BreakerStatus",PanelX+10,yPos,"Estado:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BreakerStatusValue",PanelX+55,yPos,"✅ OK",6,clrLime,"Arial Bold"); CreateLabel(panelPrefix+"BreakerSpikes",PanelX+200,yPos,"Spk:",6,C'180,180,180',"Arial"); CreateLabel(panelPrefix+"BreakerSpikesValue",PanelX+225,yPos,"0",6,clrWhite,"Arial"); yPos+=18; CreateRect(panelPrefix+"Sep1d",PanelX+10,yPos,w-20,1,C'60,60,80'); yPos+=8; } // ESTADÍSTICAS CreateLabel(panelPrefix+"StatsTitle",PanelX+10,yPos,"📊 Estadísticas",7,PanelHeaderColor,"Arial Bold"); yPos+=15; CreateLabel(panelPrefix+"TradesLabel",PanelX+10,yPos,"Ops:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"TradesValue",PanelX+40,yPos,"0",6,clrWhite,"Arial"); CreateLabel(panelPrefix+"WinRateLabel",PanelX+90,yPos,"Win:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"WinRateValue",PanelX+120,yPos,"0%",6,clrLime,"Arial"); CreateLabel(panelPrefix+"ProfitLabel",PanelX+185,yPos,"Profit:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"ProfitValue",PanelX+225,yPos,"$0",6,clrLime,"Arial Bold"); yPos+=14; CreateLabel(panelPrefix+"BalanceLabel",PanelX+10,yPos,"Balance:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"BalanceValue",PanelX+60,yPos,"$0",6,clrWhite,"Arial"); yPos+=18; // ATR if(EMA_ShowInPanel) { string btnText=g_ema_filter_runtime?"EMA: ON":"EMA: OFF"; color btnBg=g_ema_filter_runtime?clrLime:clrGray; ObjectSetString(0,panelPrefix+"BtnEMA",OBJPROP_TEXT,btnText); ObjectSetInteger(0,panelPrefix+"BtnEMA",OBJPROP_BGCOLOR,btnBg); } if(ATR_ShowInPanel) { CreateRect(panelPrefix+"Sep3",PanelX+10,yPos,w-20,1,C'60,60,80'); yPos+=8; CreateLabel(panelPrefix+"ATRTitle",PanelX+10,yPos,"📉 ATR",7,PanelHeaderColor,"Arial Bold"); CreateButton(panelPrefix+"BtnATR",PanelX+120,yPos-2,100,18,"ATR: ON",clrLime,clrBlack); CreateButton(panelPrefix+"BtnATRTF",PanelX+230,yPos-2,110,18,"TF: M15",C'60,90,150',clrWhite); yPos+=20; CreateLabel(panelPrefix+"ATRLabel",PanelX+10,yPos,"Estado:",6,PanelTextColor,"Arial"); CreateLabel(panelPrefix+"ATRStatus",PanelX+55,yPos,"-",6,clrWhite,"Arial Bold"); yPos+=14; CreateLabel(panelPrefix+"ATRCurrent",PanelX+10,yPos,"ATR: 0.00",6,PanelTextColor,"Arial"); yPos+=18; } CreateRect(panelPrefix+"Footer",PanelX,PanelY+h-22,w,22,C'25,25,45'); CreateLabel(panelPrefix+"FooterText",PanelX+10,PanelY+h-15,"XCRIPE 2.5 FULL / Sin Licencia",6,C'150,150,200',"Arial"); } void DeletePanel() { if(!ShowPanel) return; for(int i=ObjectsTotal(0,0,-1)-1;i>=0;i--) { string name=ObjectName(0,i,0,-1); if(StringFind(name,panelPrefix)==0) ObjectDelete(0,name); } } void UpdateButtonStates() { if(!ShowPanel) return; ObjectSetInteger(0,panelPrefix+"BtnBuy1",OBJPROP_BGCOLOR,g_buyLevel1Enabled?clrLime:clrGray); ObjectSetInteger(0,panelPrefix+"BtnBuy2",OBJPROP_BGCOLOR,g_buyLevel2Enabled?clrLime:clrGray); ObjectSetInteger(0,panelPrefix+"BtnBuy3",OBJPROP_BGCOLOR,g_buyLevel3Enabled?clrLime:clrGray); ObjectSetInteger(0,panelPrefix+"BtnBuy4",OBJPROP_BGCOLOR,g_buyLevel4Enabled?clrLime:clrGray); ObjectSetInteger(0,panelPrefix+"BtnSell1",OBJPROP_BGCOLOR,g_sellLevel1Enabled?clrRed:clrGray); ObjectSetInteger(0,panelPrefix+"BtnSell2",OBJPROP_BGCOLOR,g_sellLevel2Enabled?clrRed:clrGray); ObjectSetInteger(0,panelPrefix+"BtnSell3",OBJPROP_BGCOLOR,g_sellLevel3Enabled?clrRed:clrGray); ObjectSetInteger(0,panelPrefix+"BtnSell4",OBJPROP_BGCOLOR,g_sellLevel4Enabled?clrRed:clrGray); if(EMA_ShowInPanel) { string btnText=g_ema_filter_runtime?"EMA: ON":"EMA: OFF"; color btnBg=g_ema_filter_runtime?clrLime:clrGray; ObjectSetString(0,panelPrefix+"BtnEMA",OBJPROP_TEXT,btnText); ObjectSetInteger(0,panelPrefix+"BtnEMA",OBJPROP_BGCOLOR,btnBg); } if(ATR_ShowInPanel) { ObjectSetString(0,panelPrefix+"BtnATR",OBJPROP_TEXT,g_ATR_Enabled_Runtime?"ATR: ON":"ATR: OFF"); ObjectSetInteger(0,panelPrefix+"BtnATR",OBJPROP_BGCOLOR,g_ATR_Enabled_Runtime?clrLime:clrGray); ObjectSetString(0,panelPrefix+"BtnATRTF",OBJPROP_TEXT,"TF: "+TimeframeToString(g_ATR_TF_Runtime)); } } void UpdatePanel() { if(!ShowPanel) return; // ESTADO VERSIÓN color licColor=clrLime; if(StringFind(g_license_status,"❌")>=0) licColor=clrRed; else if(StringFind(g_license_status,"⚠️")>=0) licColor=clrOrange; ObjectSetString(0,panelPrefix+"LicStatus",OBJPROP_TEXT,g_license_status); ObjectSetInteger(0,panelPrefix+"LicStatus",OBJPROP_COLOR,licColor); ObjectSetString(0,panelPrefix+"RSIValue",OBJPROP_TEXT,DoubleToString(currentRSI,2)); // SEÑAL if(lastSignalType!="-") { string ds=lastSignalType; color sc=clrCyan; if(StringFind(lastSignalType,"BUY")>=0){ ds="🟢 COMPRA"; sc=clrLime; } if(StringFind(lastSignalType,"SELL")>=0){ ds="🔴 VENTA"; sc=clrRed; } ObjectSetString(0,panelPrefix+"SignalType",OBJPROP_TEXT,ds); ObjectSetInteger(0,panelPrefix+"SignalType",OBJPROP_COLOR,sc); ObjectSetString(0,panelPrefix+"SignalTime",OBJPROP_TEXT,TimeToString(lastSignalTime,TIME_DATE|TIME_MINUTES)); } // ESTADO GENERAL string blockReason=""; if(IsTradeLocked()) blockReason=g_lock_reason; else if(g_daily_loss_hit) blockReason="🛑 DD LOSS"; else if(g_daily_target_hit) blockReason="🎯 TARGET"; else if(TimeCurrent()=0)?clrLime:clrRed; ObjectSetString(0,panelPrefix+"BkBuyPnL",OBJPROP_TEXT,"$"+DoubleToString(g_basket_buy_pnl,2)); ObjectSetInteger(0,panelPrefix+"BkBuyPnL",OBJPROP_COLOR,pnlBuyColor); if(g_basket_buy_last_result!="-") { string shortResult=g_basket_buy_last_result; if(StringLen(shortResult)>45) shortResult=StringSubstr(shortResult,0,42)+"..."; ObjectSetString(0,panelPrefix+"BkBuyLastResult",OBJPROP_TEXT,"Último: "+shortResult); } // SELL basket string bkSellStatus=g_basket_sell_active?"🔴 ACTIVA":"⏸ Inactiva"; color bkSellColor=g_basket_sell_active?clrRed:clrGray; ObjectSetString(0,panelPrefix+"BkSellStatus",OBJPROP_TEXT,bkSellStatus); ObjectSetInteger(0,panelPrefix+"BkSellStatus",OBJPROP_COLOR,bkSellColor); ObjectSetString(0,panelPrefix+"BkSellLvl",OBJPROP_TEXT,IntegerToString(g_basket_sell_levels)); ObjectSetString(0,panelPrefix+"BkSellPos",OBJPROP_TEXT,IntegerToString(g_basket_sell_total_pos)); color pnlSellColor=(g_basket_sell_pnl>=0)?clrLime:clrRed; ObjectSetString(0,panelPrefix+"BkSellPnL",OBJPROP_TEXT,"$"+DoubleToString(g_basket_sell_pnl,2)); ObjectSetInteger(0,panelPrefix+"BkSellPnL",OBJPROP_COLOR,pnlSellColor); if(g_basket_sell_last_result!="-") { string shortResult=g_basket_sell_last_result; if(StringLen(shortResult)>45) shortResult=StringSubstr(shortResult,0,42)+"..."; ObjectSetString(0,panelPrefix+"BkSellLastResult",OBJPROP_TEXT,"Último: "+shortResult); } } // DAILY LIMITS if(EnableDailyLimits) { double pnl=GetDailyPnL(); ObjectSetString(0,panelPrefix+"DailyPnLValue",OBJPROP_TEXT,"$"+DoubleToString(pnl,2)); ObjectSetInteger(0,panelPrefix+"DailyPnLValue",OBJPROP_COLOR,(pnl>=0)?clrLime:clrRed); ObjectSetString(0,panelPrefix+"DailyDDValue",OBJPROP_TEXT,DoubleToString(g_current_dd_percent,2)+"%"); string ds="✅ OK"; color dc=clrLime; if(g_daily_loss_hit){ ds="🛑 LOSS HIT"; dc=clrRed; } else if(g_daily_target_hit){ ds="🎯 TARGET HIT"; dc=clrGold; } ObjectSetString(0,panelPrefix+"DailyStatusValue",OBJPROP_TEXT,ds); ObjectSetInteger(0,panelPrefix+"DailyStatusValue",OBJPROP_COLOR,dc); } // CIRCUIT BREAKER if(EnableCircuitBreaker) { string bs=(TimeCurrent()0)?(winTrades*100.0/totalTrades):0.0; ObjectSetString(0,panelPrefix+"WinRateValue",OBJPROP_TEXT,DoubleToString(winRate,1)+"%"); color profitColor=(totalProfit>=0)?clrLime:clrRed; ObjectSetString(0,panelPrefix+"ProfitValue",OBJPROP_TEXT,"$"+DoubleToString(totalProfit,2)); ObjectSetInteger(0,panelPrefix+"ProfitValue",OBJPROP_COLOR,profitColor); ObjectSetString(0,panelPrefix+"BalanceValue",OBJPROP_TEXT,"$"+DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE),2)); // ATR if(EMA_ShowInPanel) { string btnText=g_ema_filter_runtime?"EMA: ON":"EMA: OFF"; color btnBg=g_ema_filter_runtime?clrLime:clrGray; ObjectSetString(0,panelPrefix+"BtnEMA",OBJPROP_TEXT,btnText); ObjectSetInteger(0,panelPrefix+"BtnEMA",OBJPROP_BGCOLOR,btnBg); } if(ATR_ShowInPanel) { ObjectSetString(0,panelPrefix+"ATRStatus",OBJPROP_TEXT,atrStatus); ObjectSetInteger(0,panelPrefix+"ATRStatus",OBJPROP_COLOR,GetATRColor()); ObjectSetString(0,panelPrefix+"ATRCurrent",OBJPROP_TEXT,"ATR: "+DoubleToString(currentATR,g_digits_cached)+" | Avg: "+DoubleToString(avgATR,g_digits_cached)); } UpdateButtonStates(); } //+------------------------------------------------------------------+ //| LOAD INPUTS | //+------------------------------------------------------------------+ void LoadInputsToRuntime() { g_ActivarVentas=ActivarVentas; g_UseRiskCalculation=UseRiskCalculation; g_FixedLotSize=FixedLotSize; g_EnableNewsFilter=EnableNewsFilter; g_EnableATRFilter=EnableATRFilter; g_ATR_Enabled_Runtime=EnableATRFilter; g_ATR_TF_Runtime=ATR_Timeframe; g_alarm_cooldown_sec=AlarmCooldownSeconds; g_buyLevel1Enabled=Buy1_Enable; g_buyLevel2Enabled=Buy2_Enable; g_buyLevel3Enabled=Buy3_Enable; g_buyLevel4Enabled=Buy4_Enable; g_sellLevel1Enabled=Sell1_Enable; g_sellLevel2Enabled=Sell2_Enable; g_sellLevel3Enabled=Sell3_Enable; g_sellLevel4Enabled=Sell4_Enable; g_buy_en[0]=Buy1_Enable; g_buy_rsi[0]=Buy1_RSI; g_buy_orders[0]=Buy1_Orders; g_buy_risk[0]=Buy1_Risk; g_buy_tp[0]=Buy1_TP_Points; g_buy_sl[0]=Buy1_SL_Points; g_buy_ts_en[0]=Buy1_TS_Enable; g_buy_ts_start[0]=Buy1_TS_Start; g_buy_ts_step[0]=Buy1_TS_Step; g_buy_ts_dist[0]=Buy1_TS_Dist; g_buy_en[1]=Buy2_Enable; g_buy_rsi[1]=Buy2_RSI; g_buy_orders[1]=Buy2_Orders; g_buy_risk[1]=Buy2_Risk; g_buy_tp[1]=Buy2_TP_Points; g_buy_sl[1]=Buy2_SL_Points; g_buy_ts_en[1]=Buy2_TS_Enable; g_buy_ts_start[1]=Buy2_TS_Start; g_buy_ts_step[1]=Buy2_TS_Step; g_buy_ts_dist[1]=Buy2_TS_Dist; g_buy_en[2]=Buy3_Enable; g_buy_rsi[2]=Buy3_RSI; g_buy_orders[2]=Buy3_Orders; g_buy_risk[2]=Buy3_Risk; g_buy_tp[2]=Buy3_TP_Points; g_buy_sl[2]=Buy3_SL_Points; g_buy_ts_en[2]=Buy3_TS_Enable; g_buy_ts_start[2]=Buy3_TS_Start; g_buy_ts_step[2]=Buy3_TS_Step; g_buy_ts_dist[2]=Buy3_TS_Dist; g_buy_en[3]=Buy4_Enable; g_buy_rsi[3]=Buy4_RSI; g_buy_orders[3]=Buy4_Orders; g_buy_risk[3]=Buy4_Risk; g_buy_tp[3]=Buy4_TP_Points; g_buy_sl[3]=Buy4_SL_Points; g_buy_ts_en[3]=Buy4_TS_Enable; g_buy_ts_start[3]=Buy4_TS_Start; g_buy_ts_step[3]=Buy4_TS_Step; g_buy_ts_dist[3]=Buy4_TS_Dist; g_sell_en[0]=Sell1_Enable; g_sell_rsi[0]=Sell1_RSI; g_sell_orders[0]=Sell1_Orders; g_sell_risk[0]=Sell1_Risk; g_sell_tp[0]=Sell1_TP_Points; g_sell_sl[0]=Sell1_SL_Points; g_sell_ts_en[0]=Sell1_TS_Enable; g_sell_ts_start[0]=Sell1_TS_Start; g_sell_ts_step[0]=Sell1_TS_Step; g_sell_ts_dist[0]=Sell1_TS_Dist; g_sell_en[1]=Sell2_Enable; g_sell_rsi[1]=Sell2_RSI; g_sell_orders[1]=Sell2_Orders; g_sell_risk[1]=Sell2_Risk; g_sell_tp[1]=Sell2_TP_Points; g_sell_sl[1]=Sell2_SL_Points; g_sell_ts_en[1]=Sell2_TS_Enable; g_sell_ts_start[1]=Sell2_TS_Start; g_sell_ts_step[1]=Sell2_TS_Step; g_sell_ts_dist[1]=Sell2_TS_Dist; g_sell_en[2]=Sell3_Enable; g_sell_rsi[2]=Sell3_RSI; g_sell_orders[2]=Sell3_Orders; g_sell_risk[2]=Sell3_Risk; g_sell_tp[2]=Sell3_TP_Points; g_sell_sl[2]=Sell3_SL_Points; g_sell_ts_en[2]=Sell3_TS_Enable; g_sell_ts_start[2]=Sell3_TS_Start; g_sell_ts_step[2]=Sell3_TS_Step; g_sell_ts_dist[2]=Sell3_TS_Dist; g_sell_en[3]=Sell4_Enable; g_sell_rsi[3]=Sell4_RSI; g_sell_orders[3]=Sell4_Orders; g_sell_risk[3]=Sell4_Risk; g_sell_tp[3]=Sell4_TP_Points; g_sell_sl[3]=Sell4_SL_Points; g_sell_ts_en[3]=Sell4_TS_Enable; g_sell_ts_start[3]=Sell4_TS_Start; g_sell_ts_step[3]=Sell4_TS_Step; g_sell_ts_dist[3]=Sell4_TS_Dist; Print("✅ Inputs cargados a runtime"); } void CacheSymbolInfo() { g_point_cached=SymbolInfoDouble(InpSymbol,SYMBOL_POINT); g_digits_cached=(int)SymbolInfoInteger(InpSymbol,SYMBOL_DIGITS); g_tick_value_cached=SymbolInfoDouble(InpSymbol,SYMBOL_TRADE_TICK_VALUE); g_tick_size_cached=SymbolInfoDouble(InpSymbol,SYMBOL_TRADE_TICK_SIZE); g_lot_step_cached=SymbolInfoDouble(InpSymbol,SYMBOL_VOLUME_STEP); g_lot_min_cached=SymbolInfoDouble(InpSymbol,SYMBOL_VOLUME_MIN); g_lot_max_cached=SymbolInfoDouble(InpSymbol,SYMBOL_VOLUME_MAX); if(g_point_cached>0&&g_digits_cached>0) Print("✅ Symbol: ",InpSymbol," | Point: ",g_point_cached," | Digits: ",g_digits_cached); else Print("❌ ERROR: datos del símbolo inválidos para ",InpSymbol); } //+------------------------------------------------------------------+ //| PRESETS | //+------------------------------------------------------------------+ void ApplyPresetConfiguration() { LoadInputsToRuntime(); g_EnableDailyDrawdownControl=EnableDailyLimits; g_MaxDailyDrawdownPercent=DailyLossLimitPercent; if(!ApplyPresetOnInit||AccountPreset==PRESET_CUSTOM){ Print("📋 Preset: PERSONALIZADO"); return; } if(AccountPreset==PRESET_PROP_AGGRESSIVE) { Print("🏆 Preset: FTMO/PROP AGRESIVO"); g_buy_rsi[0]=14.0; g_buy_orders[0]=12; g_buy_risk[0]=1.0; g_buy_rsi[1]=8.0; g_buy_orders[1]=26; g_buy_risk[1]=3.0; g_buy_en[2]=false; g_buy_en[3]=false; g_ActivarVentas=false; g_buy_sl[0]=3000; g_buy_tp[0]=1150; g_buy_sl[1]=3000; g_buy_tp[1]=1150; g_EnableDailyDrawdownControl=true; g_MaxDailyDrawdownPercent=5.0; } else if(AccountPreset==PRESET_PROP_CONSERVATIVE) { Print("🏆 Preset: FTMO/PROP CONSERVADOR"); g_buy_rsi[0]=25.0; g_buy_orders[0]=30; g_buy_risk[0]=1.0; g_buy_rsi[1]=15.0; g_buy_orders[1]=20; g_buy_risk[1]=1.5; g_buy_en[2]=false; g_buy_en[3]=false; g_ActivarVentas=false; g_buy_sl[0]=3000; g_buy_tp[0]=1150; g_buy_sl[1]=3000; g_buy_tp[1]=1150; } Print("✅ Preset aplicado"); } //+------------------------------------------------------------------+ //| INDICADORES | //+------------------------------------------------------------------+ int CreateRSIHandle(ENUM_TIMEFRAMES tf) { int h=iRSI(InpSymbol,tf,RSIPeriod,PRICE_CLOSE); if(h==INVALID_HANDLE) Print("❌ Error RSI TF: ",EnumToString(tf)); else Print("✅ RSI Handle: ",EnumToString(tf)); return h; } void SetupIndicatorHandles() { rsiHandleTrade=CreateRSIHandle(TF_Signal); if(Alarm1_Enabled){ if(Alarm1_TF==TF_Signal){ rsiHandleA1=rsiHandleTrade; ownA1=false; } else{ rsiHandleA1=CreateRSIHandle(Alarm1_TF); ownA1=true; } } if(Alarm2_Enabled){ if(Alarm2_TF==TF_Signal){ rsiHandleA2=rsiHandleTrade; ownA2=false; } else if(Alarm2_TF==Alarm1_TF&&Alarm1_Enabled){ rsiHandleA2=rsiHandleA1; ownA2=false; } else{ rsiHandleA2=CreateRSIHandle(Alarm2_TF); ownA2=true; } } if(Alarm3_Enabled){ if(Alarm3_TF==TF_Signal){ rsiHandleA3=rsiHandleTrade; ownA3=false; } else if(Alarm3_TF==Alarm1_TF&&Alarm1_Enabled){ rsiHandleA3=rsiHandleA1; ownA3=false; } else if(Alarm3_TF==Alarm2_TF&&Alarm2_Enabled){ rsiHandleA3=rsiHandleA2; ownA3=false; } else{ rsiHandleA3=CreateRSIHandle(Alarm3_TF); ownA3=true; } } if(g_EnableATRFilter){ atrHandle=iATR(InpSymbol,g_ATR_TF_Runtime,ATRPeriod); if(atrHandle!=INVALID_HANDLE) Print("✅ ATR Handle: ",EnumToString(g_ATR_TF_Runtime)); } // ✅ EMA Trend Filter if(EMA_Filter_Enable) SetupEMAHandle(); } void ReleaseHandles() { if(rsiHandleTrade!=INVALID_HANDLE) IndicatorRelease(rsiHandleTrade); if(ownA1&&rsiHandleA1!=INVALID_HANDLE) IndicatorRelease(rsiHandleA1); if(ownA2&&rsiHandleA2!=INVALID_HANDLE) IndicatorRelease(rsiHandleA2); if(ownA3&&rsiHandleA3!=INVALID_HANDLE) IndicatorRelease(rsiHandleA3); if(atrHandle!=INVALID_HANDLE) IndicatorRelease(atrHandle); if(g_ema_handle!=INVALID_HANDLE){ IndicatorRelease(g_ema_handle); g_ema_handle=INVALID_HANDLE; } } //+------------------------------------------------------------------+ //| EVENTS BOTONES | //+------------------------------------------------------------------+ void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { if(id!=CHARTEVENT_OBJECT_CLICK) return; if(sparam==panelPrefix+"BtnBuy1"){ g_buyLevel1Enabled=!g_buyLevel1Enabled; g_buyArmed[0]=g_buyLevel1Enabled; ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); return; } if(sparam==panelPrefix+"BtnBuy2"){ g_buyLevel2Enabled=!g_buyLevel2Enabled; g_buyArmed[1]=g_buyLevel2Enabled; ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); return; } if(sparam==panelPrefix+"BtnBuy3"){ g_buyLevel3Enabled=!g_buyLevel3Enabled; g_buyArmed[2]=g_buyLevel3Enabled; ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); return; } if(sparam==panelPrefix+"BtnBuy4"){ g_buyLevel4Enabled=!g_buyLevel4Enabled; g_buyArmed[3]=g_buyLevel4Enabled; ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); return; } if(sparam==panelPrefix+"BtnSell1"){ g_sellLevel1Enabled=!g_sellLevel1Enabled; g_sellArmed[0]=g_sellLevel1Enabled; ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); return; } if(sparam==panelPrefix+"BtnSell2"){ g_sellLevel2Enabled=!g_sellLevel2Enabled; g_sellArmed[1]=g_sellLevel2Enabled; ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); return; } if(sparam==panelPrefix+"BtnSell3"){ g_sellLevel3Enabled=!g_sellLevel3Enabled; g_sellArmed[2]=g_sellLevel3Enabled; ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); return; } if(sparam==panelPrefix+"BtnSell4"){ g_sellLevel4Enabled=!g_sellLevel4Enabled; g_sellArmed[3]=g_sellLevel4Enabled; ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); return; } if(sparam==panelPrefix+"BtnEMA") { g_ema_filter_runtime=!g_ema_filter_runtime; if(!g_ema_filter_runtime){ g_ema_trend_status="Desactivado"; Print("📊 EMA Filter: DESACTIVADO"); } else{ EvaluateEMATrend(); Print("📊 EMA Filter: ACTIVADO | ",g_ema_trend_status); } ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); UpdatePanel(); return; } if(sparam==panelPrefix+"BtnATR") { g_ATR_Enabled_Runtime=!g_ATR_Enabled_Runtime; if(!g_ATR_Enabled_Runtime){ atrAllowTrade=true; atrStatus="Desactivado"; } else{ if(atrHandle==INVALID_HANDLE) RecreateATRHandle(); else EvaluateATRStatus(); } ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); UpdatePanel(); return; } if(sparam==panelPrefix+"BtnATRTF") { if(g_ATR_TF_Runtime==PERIOD_M5) g_ATR_TF_Runtime=PERIOD_M15; else if(g_ATR_TF_Runtime==PERIOD_M15) g_ATR_TF_Runtime=PERIOD_H1; else if(g_ATR_TF_Runtime==PERIOD_H1) g_ATR_TF_Runtime=PERIOD_M5; else g_ATR_TF_Runtime=PERIOD_M15; RecreateATRHandle(); ObjectSetInteger(0,sparam,OBJPROP_STATE,false); UpdateButtonStates(); UpdatePanel(); return; } } //+------------------------------------------------------------------+ //| ONINIT | //+------------------------------------------------------------------+ int OnInit() { Print("════════════════════════════════════════════"); Print("🔥 XCRIPE 2.5 FULL / Sin Licencia - BASKET MODE"); Print("════════════════════════════════════════════"); SymbolSelect(InpSymbol,true); CacheSymbolInfo(); if(g_point_cached<=0) return INIT_FAILED; LoadInputsToRuntime(); ApplyPresetConfiguration(); // Validar entorno if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)){ Print("❌ AutoTrading desactivado"); return INIT_FAILED; } if(InpSymbol!=_Symbol&&!IsBacktesting()){ Alert("⚠️ SÍMBOLO INCORRECTO\nInpSymbol: ",InpSymbol,"\nGráfico: ",_Symbol); return INIT_FAILED; } // Validar Basket if(Basket_Enable) { int ml=Basket_MinLevels; if(ml<2||ml>4){ Print("⚠️ Basket_MinLevels debe ser 2-4. Usando 2."); } Print("🧺 Basket Mode ACTIVO | MinLevels: ",MathMax(2,MathMin(4,ml))); Print(" BUY → Target: $",DoubleToString(Basket_Buy_TargetMoney,2)," | SL: $",DoubleToString(Basket_Buy_StopLossMoney,2)); Print(" SELL → Target: $",DoubleToString(Basket_Sell_TargetMoney,2)," | SL: $",DoubleToString(Basket_Sell_StopLossMoney,2)); } SetupIndicatorHandles(); if(rsiHandleTrade==INVALID_HANDLE){ Print("❌ RSI handle inválido"); return INIT_FAILED; } initialBalance=AccountInfoDouble(ACCOUNT_BALANCE); ResetDailyAnchor(); if(g_EnableNewsFilter){ LoadNewsFromForexFactory(); FindNextNews_FF(); } if(EnableTimeFilter){ InitTimeFilter(); EvaluateTimeFilter(); } if(g_ATR_Enabled_Runtime) EvaluateATRStatus(); // ✅ EMA Trend Filter init g_ema_filter_runtime=EMA_Filter_Enable; if(EMA_Filter_Enable) { EvaluateEMATrend(); Print("📊 EMA Filter ACTIVO | TF=",EnumToString(EMA_Timeframe)," | Period=",EMA_Period," | Tendencia: ",g_ema_trend_status); } if(ShowPanel){ DeletePanel(); CreatePanel(); UpdateStats(); UpdatePanel(); } if(EnableTelegramAlerts) Telegram_SendTestMessage(); EventSetTimer(2); Print("════════════════════════════════════════════"); Print("✅ EA XCRIPE 2.5 FULL INICIADO CORRECTAMENTE"); Print(" ✅ Basket Mode: ",(Basket_Enable?"ACTIVO":"DESACTIVADO")); Print(" ✅ EMA Trend Filter: ",(EMA_Filter_Enable?"ACTIVO | TF="+EnumToString(EMA_Timeframe)+" | EMA"+IntegerToString(EMA_Period):"DESACTIVADO")); Print(" ✅ Daily PnL REAL (balance + flotante)"); Print(" ✅ Circuit Breaker anti-spike"); Print(" ✅ Rate Limiter anti-429"); Print(" ✅ Time/News/ATR filters activos"); Print("════════════════════════════════════════════"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| ONDEINIT | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); DeletePanel(); ReleaseHandles(); Print("🧹 EA XCRIPE 2.5 FULL detenido. Reason=",reason); } //+------------------------------------------------------------------+ //| ONTIMER | //+------------------------------------------------------------------+ void OnTimer() { UpdateDailyResetIfDayChanged(); if(g_EnableNewsFilter) UpdateNewsFilter_FF(); if(g_ATR_Enabled_Runtime) EvaluateATRStatus(); if(EMA_Filter_Enable&&g_ema_filter_runtime) EvaluateEMATrend(); if(EnableTimeFilter) EvaluateTimeFilter(); UpdateStats(); if(ShowPanel){ if(ObjectFind(0,panelPrefix+"BG")<0) CreatePanel(); UpdatePanel(); } } //+------------------------------------------------------------------+ //| ONTICK | //+------------------------------------------------------------------+ void OnTick() { UpdateDailyResetIfDayChanged(); // ✅ BASKET: gestionar cestas en cada tick (núcleo del sistema) if(Basket_Enable) ManageBaskets(); ManageTrailingStop(); CheckRSIAlarms(); datetime barTime=iTime(InpSymbol,TF_Signal,0); if(barTime<=0) return; CheckSignalTrading(barTime); } //+------------------------------------------------------------------+ //| FIN XCRIPE 2.5 FULL - TODAS LAS PARTES COMPLETAS | //+------------------------------------------------------------------+