//+------------------------------------------------------------------+ //| Fusion LokTrade - | //| Enhanced EA com estrutura modular, tratamento detalhado de | //| erros, gerenciamento de risco e melhor feedback ao usuário. | //| | //| © 2007-2025, Grupo Telegran | //| http://t.me/Lokman_Trader | //+------------------------------------------------------------------+ #property copyright "© Fusion LokTrade" #property link "http://t.me/Lokman_Trader" #property description "@Lok_Trade, Desejo Bons Lucros Amigos " #property description "Mantenha o Padrão. Pouco Saldo Use contas Cents. Minimo 10Mil Cents" #property description "." #property description " XAUUSD, GOLD, (( M5 )) Alterações, WhatsApp do Desenvolvedor: +55 71 99918-7219" #property description "." #property description "ESTE EA POSSUI SEGURANÇA CONTRA MANIPULAÇÕES DE PREÇOs <^> CORRETORAS!!" #property description "Minhas Criações não implicam vínculo,de responsável no Trader " #resource "\\Images\\Fusion_LokTrade.bmp" #define IS_TESTING() (MQLInfoInteger(MQL_TESTER) == 1) #include #property strict // Habilita verificações estritas de erro no código //---------------------------------------------------------------------- // Macros e Constantes //---------------------------------------------------------------------- #define MAX_TRIES 3 // Número máximo de tentativas para envio/fechamento #define WAIT_TIME 500 // Tempo de espera entre tentativas (ms) //---------------------------------------------------------------------- // Variáveis Globais e Flags //---------------------------------------------------------------------- datetime lastSLCheck = 0; // Timestamp do último check de SL datetime lastTradeTime = 0; // Timestamp da última trade bool highSpreadAlerted = false; // Flag de alerta de spread alto bool manualOrderClosed = false; // Detecta se houve fechamento manual de ordem bool renewalNotified = false; // Indica se notificação de vencimento já foi enviada bool stopTrading = false; // Interrompe execução dos robôs bool protectionTriggered = false; // Garante única ativação da proteção bool spikeAlerted = false; // Evita notificações de spike contínuas double pointAdjusted = 0; // Valor ajustado de ponto dependendo do ativo double basePrice = 0; // Preço base para cálculos double lastAsk = 0.0; // Último preço ASK registrado double lastBid = 0.0; // Último preço BID registrado // Flags de configuração do terminal bool emailSetupOk = false; // Indica configuração do e-mail bool pushSetupOk = false; // Indica configuração do push //---------------------------------------------------------------------- // Variáveis para efeito de piscar de labels //---------------------------------------------------------------------- int blinkColorIndex = 0; // Índice para cores de blink color blinkColors[] = {clrRed, clrGreen, clrBlue, clrYellow, clrMagenta, clrCyan}; // Paleta de blinking int fpBlinkColorIndex = 0; // Índice de cor para lucro flutuante blink //══════════════════════════════════════════════════════════════════ // Variáveis Globais para o DRL (Memória de Lucro) //══════════════════════════════════════════════════════════════════ static double avgProfitMemory[]; // Buffer circular para lucros anteriores static int memoryIndex = 0; // Índice atual de memória static bool initialized = false; // Verifica inicialização do DRL // Robo4 globals bool Robo4_ordersOpened = false; double Robo4_lastBuyPrice = 0.0; double Robo4_lastSellPrice = 0.0; datetime Robo4_lastOpenTime = 0; int Robo4_gridRepeats = 0; datetime Robo4_lastGridTime = 0; double Robo4_pointValue = 0.0; //---------------------------------------------------------------------- // Controle de fechamento manual do Robo4 //---------------------------------------------------------------------- int lastRobo4Orders = 0; // passa a ser visível em todo o EA //---------------------------------------------------------------------- // Enums de modo DRL e presets //---------------------------------------------------------------------- // Modo de aprendizado DRL enum ENUM_DRLMODE { DRLMode_Adaptive = 0, DRLMode_Aggressive, DRLMode_Conservative }; // Presets de configuração DRL enum ENUM_DRL_PRESET { DRL_Preset_Default = 0, DRL_Preset_AggressiveTurbo, DRL_Preset_ConservativeSafe, DRL_Preset_UltraAdaptive, DRL_Preset_StaticMode, DRL_Preset_MomentumFocus, DRL_Preset_VolatilityOptimized, DRL_Preset_RiskOn, DRL_Preset_RiskOff, DRL_Preset_BalancedConservative, DRL_Preset_BalancedAggressive }; //---------------------------------------------------------------------- // Cópias internas dos inputs de DRL (para uso interno) //---------------------------------------------------------------------- double DRLLearningRate; // Taxa de aprendizado interna int DRLProfitRewardThreshold; // Limiar de recompensa por lucro int DRLLossPunishThreshold; // Limiar de punição por perda int DRL_MinMovementThreshold; // Movimento mínimo em pts int DRL_MaxMovementThreshold; // Movimento máximo em pts double DRL_MinLotSize; // Lote mínimo internal double DRL_MaxLotSize; // Lote máximo internal //---------------------------------------------------------------------- // Funções //---------------------------------------------------------------------- /** * Helper único para emitir alerta + e‑mail + push * @param message Mensagem de alerta */ void Notificar(string message) { // 1) Alerta visual Alert(message); // 2) E‑mail (se habilitado) if(EnableEmailAlerts && emailSetupOk) SendMail(AlertMailSubject, message); // 3) Push (se habilitado) if(EnablePushAlerts && pushSetupOk) SendNotification(AlertPushPrefix + ": " + message); } //---------------------------------------------------------------------- // Parâmetros de Entrada (Inputs) //---------------------------------------------------------------------- input bool EnableRobo1 = false; // Ativa ROBÔ1 {M5} (RSI e movimento) input bool EnableRobo2 = false; // Ativa ROBÔ2 {M5} (Martingale) //+------------------------------------------------------------------+ //| Robo4: SimultBuySell com Grid Martingale | //+------------------------------------------------------------------+ input bool EnableRobo4 = true; // Ativa Robo4 SimultBuySell‑Grid input double Robo4_BuyLot = 0.02; // Lote para BUY input double Robo4_SellLot = 0.01; // Lote para SELL input double Robo4_ProfitTarget = 50.0; // Alvo lucro USD input bool Robo4_EnableAutoProfitClose = true; // Fecha tudo ao atingir alvo input int Robo4_GridDistancePoints = 1500; // Distância pts martingale input int Robo4_MaxGridRepeats = 20; // Níveis martingale input int Robo4_GridReopenDelaySeconds = 20; // Delay reabertura grid (s) input int Robo4_ReopenDelaySeconds = 1; // Delay aberturas iniciais (s) input int Robo4_Slippage = 3; // Slippage máximo input int Robo4_MaxSpreadPoints = 50; // Spread máximo (pts) input int Robo4_SellTPPoints = 1500; // TP em pontos para cada SELL // IA Adaptativa input bool EnableAdaptiveML = true; // IA ADAPTATIVA (DESATiVa ROBÔ2,E HEDGE)Pr. Contrária input int ML_CandlesLookback = 5; // Quantidade de candles analisados input double ML_TrendStrengthThreshold = 0.15; // % mínimo de candles a favor da tendência // Deep Reinforcement Learning Adaptativo input bool EnableDRLModule = true; // Ativar Deep RL Module Revolução input double DRLLearningRateInput = 0.07; // Taxa de aprendizado do DRL input int DRLMemorySize = 100; // Tamanho da memória input int DRLProfitRewardThresholdInput = 100; // Recompensa se lucro acima ($) input int DRLLossPunishThresholdInput = -10; // Punição se perda abaixo ($) input int DRL_MinMovementThresholdInput = 100; // Mínimo de movimento em pts input int DRL_MaxMovementThresholdInput = 300; // Máximo de movimento em pts input double DRL_MinLotSizeInput = 0.01; // Lote mínimo DRL input double DRL_MaxLotSizeInput = 0.10; // Lote máximo DRL // Em seguida, defina seus inputs: input ENUM_DRLMODE DRLMode = DRLMode_Adaptive; // Seleção de modo input ENUM_DRL_PRESET DRLPreset = DRL_Preset_Default; // Escolha de preset // Validade do EA string ValidadeEA = "2029.12.20 23:59"; // Data de validade int RenewalNotificationDays = 30; // Dias antes de aviso // Assuntos/prefixos opcionais input string AlertMailSubject = "EA Fusion"; input string AlertPushPrefix = "Fusion EA"; // Novos limites de ordens permitidas input int MaxOrdersRobo1 = 20; // Máx ordens Robo1 input int MaxOrdersRobo2 = 20; // Máx ordens Robo2 // Suporte Externo (Indicators/Presets) input bool EnableExternalIndicator = false; // ATIVAR INDICADOR EXTERNO (Colocar o Nome Abaixo) input string ExternalIndicatorName = "NomeIndicator"; input int BufferBuyIndex = 0; // Buffer compra input int BufferSellIndex = 1; // Buffer venda input bool EnablePresetReader = false; // Ativa leitura preset externo input string ExternalPresetFile = "NomePreset.txt"; // Nome do Arquivo Presets (na pasta MQL4/Files/) // Inputs Avançados: SL/TP/Lucro SELL dinâmicos input int lastGlobalSLInput = 5002; // SL global input int lastGlobalTPInput = 300; // TP global input double lastSellProfitPtsInput = 1005.0; // Fechar SELL ao atingir pts // Distâncias e thresholds input int MinDistanceBetweenOrders = 1000; // Distância mínima pts entre ordens BUY SELL input double movementThreshold = 1000; // Quanto o preço precisa se mover (em pontos) no {Robo1} input double lotBuy = 0.02; // Lote inicial compra (Robo1) input double lotSell = 0.01; // Lote inicial venda (Robo1) // Parâmetros Robo2 (Martingale) input double ExtLot = 0.01; // Lote base Martingale input double linkedSellLot = 0.01; // Lote venda vinculada input int GlobalStopLoss = 5002; // SL geral_pts input int LinkedSell_SL = 1000; // SL venda vinculada input int ExtLot_SL = 5002; // SL compra Martingale_pts input int GlobalTakeProfit = 300; // TP geral_pts input int ExtLot_TP = 1002; // TP compra Martingale input double profitTarget = 1000.0; // Fechar Robo1 lucro total input int LinkedSell_TP = 1000; // TP venda vinculada input double LinkedSellProfitTarget = 1000.0; // Fechar SELL lucro total input double SymbolProfitTarget = 50.0; // Fechar lucro total símbolo input double SellProfitThresholdPoints = 1005.0; // Fechar venda ao atingir pts // Hedge Contrarian TP/SL input int ContrarianTPPoints = 100000; // TP hedge pts input int ContrarianSLPoints = 100000; // SL hedge pts // Martingale Negativo input int NegativeMartingaleThreshold = 200; // Pts Martingale Negativo Lt Extra input double NegativeMartingaleLot = 0.02; // Lote mart_negativo // Proteção Contrária input bool EnableContrarianProtection = true; // Habilita proteção contrária atingir perda definida input double ActiveLossThreshold = 550.0; // PERDA MAXÍMA Acionar Hedge > input double EquityStopLossPercent = 80.0; // % drawdown equity Max. input double ContrarianProtectionPercentage = 40.0; // (HEDGE) Ex:80% EA Dispara a Proteção Contrária input double ProfitCloseAfterProtectionThreshold = 50.0; // ≥$ 100 USD Após Proteção Contrária (o hedge) Fechar Tudo Reiniciar // Fechamento por lucro do ativo input double AssetProfitCloseThreshold = 70.0; // >$USD Fecha Ordens ATIVO FECHAMENTO INDEPENDENTE // Janela de Trading input int TradeStartHour = 0; // Hora início (0–23) input int TradeStartMinute = 0; // Minuto início (0–59) input int TradeEndHour = 23; // Hora fim (0–23) input int TradeEndMinute = 0; // Minuto fim (0–59) // Proteção contra spikes/falsos ticks input bool EnableSpikeFilter = true; // Filtro Est. Robustez do EA Contra Golpes Corretoras! input bool EnableATRSpikeFilter = true; // Habilita filtro ATR input double ATRSpikeMultiplier = 3.0; // Mult ATR p/ limiar input int MaxSpikeThresholdPoints = 500; // Max movimento por tick_pts // Notificações input bool EnableEmailAlerts = false; // Habilita alertas e-mail input bool EnablePushAlerts = false; // Habilita alertas push // Botão Fecha Tudo input bool EnableFechaTudoButton = true; // Habilita botão input string FechaTudoBtnName = "btnFechaTudo"; // Nome botão input bool ForceCloseAll = false; // FECHARTUDO do ATIVO! Não ATIVAR (TRUE) AQUI!) // AutoRecreate e modos input bool EnableAutoRecreate = true; // Recriar após fechamento manual ONBot2 Recria, OFPausa Bt1 ONNDa input bool EnableCloseOnAssetProfit = true; // Fecha se lucro ativo > limite input bool EnableNegativeMartingale = true; // Habilita martingale negativo input bool PauseAfterEquityStop = false; // Pausar após EQ 80% Stop.TRUE.Pausa FALSE Continua Operando input bool EnableRescueMode = true; // Habilita modo Rescue após hedge // Lote Dinâmico input bool EnableDynamicLot = false; // Habilita lote dinâmico input double DynamicLotFactorBuy = 0.000001; // Fator lote BUY input double DynamicLotFactorSell = 0.000001; // Fator lote SELL input double DynamicLotFactorExtLot = 0.000001; // Fator lote EXT input double DynamicLotFactorLinkedSell = 0.000001; // Fator lote linked //--------------------------------------------------------------------------- // 1) Defina no topo, junto com os outros inputs //--------------------------------------------------------------------------- input int magicNumber = 1; // magic genérico (Robo1) input int magicNumberRobo2 = 2; // magic exclusivo para Robo2 input int magicNumberMartNeg = 3; // magic para Martingale Negativo input int Robo4_MagicNumber = 4; // MagicNumber Robo4 // Outros parâmetros input int MaxAllowedSpread = 50; // Spread máximo permitido_pts input int slippage = 10; // Slippage máximo_pts input int rsiPeriod = 14; // Período RSI input double rsiBuyLevel = 30; // Nível RSI compra // Filtro de tendência TF superior input bool EnableHigherTFFilter = false; // Habilita filtro TF superior input ENUM_TIMEFRAMES TrendTF = PERIOD_H1; // Timeframe tendência input int TrendMAPeriod = 200; // Período MA tendência input bool UsePriceAboveMA = false; // Compra só se preço > MA // Simulação realista Strategy Tester input bool EnableRealisticTest = true; // Ativar simulação realista input int MinSpreadPips = 5; // Spread mínimo_pips input int MaxSpreadPips = 55; // Spread máximo_pips input int MaxSlippagePips = 6; // Slippage máximo_pips input double CommissionPerLot = 0.01; // Comissão por lote (USD) // Variáveis que vão armazenar o lote efetivo a cada tick double actualLotBuy, actualLotSell; double actualExtLot, actualLinkedSellLot; //--------------------------------------------------------------------- // Estrutura para armazenar informações do Martingale Negativo //--------------------------------------------------------------------- struct MartingaleInfo { int ticket; // Ticket da ordem original int count; // Quantos níveis (disparos) já foram acionados para essa ordem }; MartingaleInfo martingaleList[]; // Array dinâmico para armazenar as informações //---------------------------------------------------------------------- // Funções Utilitárias //---------------------------------------------------------------------- //═════════════════════════════════════════════════════ // Inicializa suporte externo (indicadores e presets) //═════════════════════════════════════════════════════ void InitializeExternalSupport() { if(EnableExternalIndicator) { double check = iCustom(Symbol(), 0, ExternalIndicatorName, 0, 0); if(check == 0.0 && GetLastError() == ERR_CUSTOM_INDICATOR_ERROR) Print(" Indicador Externo NÃO encontrado: ", ExternalIndicatorName); else Print(" Indicador Externo encontrado: ", ExternalIndicatorName); } if(EnablePresetReader) { int handle = FileOpen(ExternalPresetFile, FILE_READ | FILE_TXT | FILE_ANSI); if(handle != INVALID_HANDLE) { Print(" Arquivo de preset encontrado: ", ExternalPresetFile); FileClose(handle); } else { Print(" Arquivo de preset NÃO encontrado: ", ExternalPresetFile); } } } //═════════════════════════════════════════════════════ // Função para ler presets externos //═════════════════════════════════════════════════════ void ReadExternalPreset() { if(!EnablePresetReader) return; int handle = FileOpen(ExternalPresetFile, FILE_READ | FILE_TXT | FILE_ANSI); if(handle == INVALID_HANDLE) { Print(" Falha ao abrir preset externo: ", ExternalPresetFile); return; } while(!FileIsEnding(handle)) { string linha = FileReadString(handle); Print("🔹 Linha lida do preset: ", linha); // Aqui você pode tratar as linhas lidas } FileClose(handle); } //═════════════════════════════════════════════════════ // Função para checar sinais externos de indicador //═════════════════════════════════════════════════════ void CheckExternalSignals() { if(!EnableExternalIndicator) return; double buySignal = iCustom(Symbol(), 0, ExternalIndicatorName, BufferBuyIndex, 0); double sellSignal = iCustom(Symbol(), 0, ExternalIndicatorName, BufferSellIndex, 0); if(buySignal > 0.0) Print(" Sinal de COMPRA detectado do indicador externo."); if(sellSignal > 0.0) Print(" Sinal de VENDA detectado do indicador externo."); } //— Retorna spread simulado (em pontos) — double GetSimulatedSpread() { double spread; if(IS_TESTING() && EnableRealisticTest) { int rnd = MinSpreadPips + MathRand() % (MaxSpreadPips - MinSpreadPips + 1); spread = rnd * pointAdjusted; } else { spread = (MarketInfo(Symbol(), MODE_ASK) - MarketInfo(Symbol(), MODE_BID)); } return spread; } //— Retorna slippage simulado (em pontos) — int GetSimulatedSlippage() { if(IS_TESTING() && EnableRealisticTest) return MathRand() % (MaxSlippagePips + 1); return 0; } //— Subtrai comissão da ordem fechada — void ApplyCommission(int ticket, double lots) { if(IS_TESTING() && EnableRealisticTest && CommissionPerLot > 0) { double comm = lots * CommissionPerLot; for(int i=OrdersHistoryTotal()-1; i>=0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY) && OrderTicket() == ticket) { Print(" Comissão simulada: $", DoubleToStr(comm,2), " para ticket ", ticket); break; } } } } //---------------------------------------------------------------------- // Retorna true se a tendência no TF selecionado estiver alinhada //---------------------------------------------------------------------- bool IsHigherTFTrendAligned(bool isBuySignal) { // SMA no TF superior double ma = iMA(Symbol(), TrendTF, TrendMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0); // preço atual no M5 double price = iClose(Symbol(), 0, 0); if(isBuySignal) { // se UsePriceAboveMA==true → compra somente quando price>MA // se UsePriceAboveMA==false → compra somente quando price ma) : (price < ma); } else { // para venda, inverte a condição anterior return UsePriceAboveMA ? (price < ma) : (price > ma); } } // Registra erros detalhadamente void LogError(string message, int errorCode) { Print(message, " Error Code: ", errorCode); } // Valida os parâmetros de entrada para evitar valores inválidos void ValidateOrderInputs() { if(lotBuy <= 0 || lotSell <= 0 || ExtLot <= 0 || linkedSellLot <= 0) { Print("Tamanhos de lote inválidos. Verifique os parâmetros de entrada."); } // Outras validações podem ser adicionadas conforme necessário } //---------------------------------------------------------------------- // Retorna true se TimeCurrent() estiver entre os inputs Start e End //---------------------------------------------------------------------- bool IsWithinTradingWindow() { datetime now = TimeCurrent(); int h = TimeHour(now), m = TimeMinute(now); // Antes do início? if(h < TradeStartHour || (h == TradeStartHour && m < TradeStartMinute)) return false; // Após o fim? if(h > TradeEndHour || (h == TradeEndHour && m > TradeEndMinute)) return false; return true; } //---------------------------------------------------------------------- // Configura os parâmetros visuais do gráfico //---------------------------------------------------------------------- void ConfigureChart() { long chart_id = ChartID(); ChartSetInteger(chart_id, CHART_COLOR_BACKGROUND, clrBlack); ChartSetInteger(chart_id, CHART_COLOR_FOREGROUND, clrWhite); ChartSetInteger(chart_id, CHART_COLOR_GRID, clrDimGray); ChartSetInteger(chart_id, CHART_COLOR_VOLUME, DarkGray); ChartSetInteger(chart_id, CHART_COLOR_CHART_UP, LimeGreen); ChartSetInteger(chart_id, CHART_COLOR_CHART_DOWN, Red); ChartSetInteger(chart_id, CHART_COLOR_CHART_LINE, Gray); ChartSetInteger(chart_id, CHART_COLOR_CANDLE_BULL, Green); ChartSetInteger(chart_id, CHART_COLOR_CANDLE_BEAR, Red); ChartSetInteger(chart_id, CHART_COLOR_BID, DarkGray); ChartSetInteger(chart_id, CHART_COLOR_ASK, DarkGray); ChartSetInteger(chart_id, CHART_COLOR_LAST, DarkGray); ChartSetInteger(chart_id, CHART_COLOR_STOP_LEVEL, DarkGray); ChartSetInteger(chart_id, CHART_SHOW_TRADE_LEVELS, true); ChartSetInteger(chart_id, CHART_DRAG_TRADE_LEVELS, true); ChartSetInteger(chart_id, CHART_SHOW_DATE_SCALE, true); ChartSetInteger(chart_id, CHART_SHOW_PRICE_SCALE, true); ChartSetInteger(chart_id, CHART_SHOW_ONE_CLICK, false); ChartSetInteger(chart_id, CHART_MODE, CHART_CANDLES); ChartSetInteger(chart_id, CHART_SHIFT, false); ChartSetInteger(chart_id, CHART_AUTOSCROLL, true); ChartSetInteger(chart_id, CHART_SCALE, 3); ChartSetInteger(chart_id, CHART_SCALEFIX, false); ChartSetInteger(chart_id, CHART_SHOW_OHLC, false); ChartSetInteger(chart_id, CHART_SHOW_BID_LINE, false); ChartSetInteger(chart_id, CHART_SHOW_ASK_LINE, false); ChartSetInteger(chart_id, CHART_SHOW_LAST_LINE, false); ChartSetInteger(chart_id, CHART_SHOW_PERIOD_SEP, false); ChartSetInteger(chart_id, CHART_SHOW_GRID, false); ChartSetInteger(chart_id, CHART_SHOW_VOLUMES, false); ChartSetInteger(chart_id, CHART_SHOW_OBJECT_DESCR, false); } //---------------------------------------------------------------------- // Exibe a logo do EA no gráfico //---------------------------------------------------------------------- void ShowLogo() { string objName = "LogoEA"; // Nome do objeto de imagem ObjectDelete(0, objName); // Deleta o objeto se já existir ChartSetInteger(0, CHART_FOREGROUND, false); // Cria o objeto de imagem if(ObjectCreate(0, objName, OBJ_BITMAP_LABEL, 0, 0, 0)) { ObjectSetString(0, objName, OBJPROP_BMPFILE, "::Images\\Fusion_LokTrade.bmp"); // Sem a extensão .bmp ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 300); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 70); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); } else { Print("Falha ao criar a imagem da logo.", GetLastError()); } } // <<<< COLOQUE AQUI DEixar LEVE >>>> void UpdateLabel(string nome, string texto, int x, int y, color cor, int tamanhoFonte) { if(ObjectFind(0, nome) < 0) { ObjectCreate(0, nome, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, nome, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, nome, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, nome, OBJPROP_YDISTANCE, y); ObjectSetInteger(0, nome, OBJPROP_FONTSIZE, tamanhoFonte); ObjectSetInteger(0, nome, OBJPROP_COLOR, cor); ObjectSetString(0, nome, OBJPROP_FONT, "Arial"); } ObjectSetString(0, nome, OBJPROP_TEXT, texto); ObjectSetInteger(0, nome, OBJPROP_COLOR, cor); } //---------------------------------------------------------------------- // IA Adaptativa: Análise de Candles Recentes para confirmar Sinal //---------------------------------------------------------------------- bool UseAdaptiveMLSignal(bool isBuySignal) { if(!EnableAdaptiveML) return true; // Se IA desligada, sempre libera. int totalCandles = ML_CandlesLookback; int favorableCandles = 0; for(int i = 1; i <= totalCandles; i++) { double open = iOpen(Symbol(), 0, i); double close = iClose(Symbol(), 0, i); // Para compra, contamos candles de alta if(isBuySignal && close > open) favorableCandles++; // Para venda, contamos candles de baixa if(!isBuySignal && close < open) favorableCandles++; } double ratio = (double)favorableCandles / totalCandles; if(ratio >= ML_TrendStrengthThreshold) return true; // Aprova a entrada return false; // Veta a entrada } //---------------------------------------------------------------------- // Exibe informações dos parâmetros em um label no gráfico //---------------------------------------------------------------------- void DisplayParameterInfo() { string info = "===== EA Parameters Info =====\n"; info += "MAX_TRIES: " + IntegerToString(MAX_TRIES) + " (Tentativas para envio/fechamento)\n"; info += "WAIT_TIME: " + IntegerToString(WAIT_TIME) + "ms (Entre tentativas)\n"; info += "EnableRobo1: " + (EnableRobo1 ? "True" : "False") + " (Estratégia Robo1: RSI e movimento)\n"; info += "EnableRobo2: " + (EnableRobo2 ? "True" : "False") + " (Estratégia Robo2: Martingale)\n"; info += "MinDistanceBetweenOrders: " + IntegerToString(MinDistanceBetweenOrders) + " pts\n"; info += "lotBuy: " + DoubleToStr(lotBuy,2) + " | lotSell: " + DoubleToStr(lotSell,2) + "\n"; info += "slippage: " + IntegerToString(slippage) + "\n"; info += "movementThreshold_runtime: " + DoubleToStr(movementThreshold_runtime,0) + " pts\n"; info += "rsiPeriod: " + IntegerToString(rsiPeriod) + " | rsiBuyLevel: " + DoubleToStr(rsiBuyLevel,2) + "\n"; info += "profitTarget: $" + DoubleToStr(profitTarget,2) + "\n"; info += "GlobalStopLoss: " + IntegerToString(GlobalStopLoss) + " pts | GlobalTakeProfit: " + IntegerToString(GlobalTakeProfit) + " pts\n"; info += "magicNumber: " + IntegerToString(magicNumber) + "\n"; info += "ExtLot: " + DoubleToStr(ExtLot,2) + " | linkedSellLot: " + DoubleToStr(linkedSellLot,2) + "\n"; info += "ExtLot_SL: " + IntegerToString(ExtLot_SL) + " pts | ExtLot_TP: " + IntegerToString(ExtLot_TP) + " pts\n"; info += "LinkedSell_SL: " + IntegerToString(LinkedSell_SL) + " pts | LinkedSell_TP: " + IntegerToString(LinkedSell_TP) + " pts\n"; info += "LinkedSellProfitTarget: $" + DoubleToStr(LinkedSellProfitTarget,2) + "\n"; info += "SymbolProfitTarget: $" + DoubleToStr(SymbolProfitTarget,2) + "\n"; info += "MaxAllowedSpread: " + IntegerToString(MaxAllowedSpread) + " pts\n"; // Novos parâmetros de fechamento automático pelo lucro do ativo info += "EnableCloseOnAssetProfit: " + (EnableCloseOnAssetProfit ? "True" : "False") + "\n"; info += "AssetProfitCloseThreshold: $" + DoubleToStr(AssetProfitCloseThreshold,2) + "\n"; // Novos parâmetros de proteção contrária info += "EnableContrarianProtection: " + (EnableContrarianProtection ? "True" : "False") + "\n"; info += "ActiveLossThreshold: $" + DoubleToStr(ActiveLossThreshold,2) + "\n"; info += "ContrarianProtectionPercentage: " + DoubleToStr(ContrarianProtectionPercentage,2) + "%\n"; info += "--------------------------------\n"; info += "EnableNegativeMartingale: " + (EnableNegativeMartingale ? "True" : "False") + "\n"; info += "NegativeMartingaleThreshold: " + IntegerToString(NegativeMartingaleThreshold) + " pts\n"; info += "NegativeMartingaleLot: " + DoubleToStr(NegativeMartingaleLot,2) + "\n"; info += "================================"; string objName = "ParameterInfoLabel"; if(ObjectFind(0, objName) < 0) ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 10); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, objName, OBJPROP_COLOR, clrWhite); ObjectSetString(0, objName, OBJPROP_TEXT, info); } //---------------------------------------------------------------------- // Função para exibir e piscar o label "CONTA REAL" ou "CONTA DEMO" no gráfico //---------------------------------------------------------------------- void BlinkAccountType() { // Verifica se a conta é demo comparando o trade mode bool isDemo = (AccountInfoInteger(ACCOUNT_TRADE_MODE) == ACCOUNT_TRADE_MODE_DEMO); string accountLabel = isDemo ? "CONTA DEMO" : "CONTA REAL"; string objName = "AccountTypeLabel"; if(ObjectFind(0, objName) < 0) { ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 1420); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 20); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 10); ObjectSetString(0, objName, OBJPROP_FONT, "Arial"); } int totalColors = ArraySize(blinkColors); ObjectSetInteger(0, objName, OBJPROP_COLOR, blinkColors[blinkColorIndex]); ObjectSetString(0, objName, OBJPROP_TEXT, accountLabel); blinkColorIndex = (blinkColorIndex + 1) % totalColors; } //---------------------------------------------------------------------- // NOVA FUNÇÃO: Exibe e pisca o logo "@LoK_Trade" abaixo do label da conta //---------------------------------------------------------------------- void BlinkLogoTrade() { string objName = "LogoTradeLabel"; // Cria o objeto se ele ainda não existir if(ObjectFind(0, objName) < 0) { ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 1435); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 50); // Posicionado abaixo do "AccountTypeLabel" ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 10); ObjectSetString(0, objName, OBJPROP_FONT, "Arial"); } // Atualiza a cor para o efeito de piscar (usa a mesma variável global blinkColorIndex) int totalColors = ArraySize(blinkColors); ObjectSetInteger(0, objName, OBJPROP_COLOR, blinkColors[blinkColorIndex]); // Define o texto fixo do logo ObjectSetString(0, objName, OBJPROP_TEXT, "LoK_Trade"); } //---------------------------------------------------------------------- // Exibe aviso visual piscando quando detectar fechamento manual //---------------------------------------------------------------------- void ShowManualClosureWarning() { string objName = "ManualClosureWarning"; if(ObjectFind(0, objName) < 0) { ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 15); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 610); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 9); ObjectSetString(0, objName, OBJPROP_FONT, "Arial Bold"); } // Deixa o label piscando int totalColors = ArraySize(blinkColors); ObjectSetInteger(0, objName, OBJPROP_COLOR, blinkColors[blinkColorIndex]); ObjectSetString(0, objName, OBJPROP_TEXT, " Ordem Manual Fechada! Aguardando novo movimento..."); blinkColorIndex = (blinkColorIndex + 1) % totalColors; } //---------------------------------------------------------------------- // Função: Remove o aviso de fechamento manual do gráfico //---------------------------------------------------------------------- void HideManualClosureWarning() { string objName = "ManualClosureWarning"; if(ObjectFind(0, objName) >= 0) { ObjectDelete(0, objName); Print(" Aviso de fechamento manual removido do gráfico."); } } //---------------------------------------------------------------------- // NOVA FUNÇÃO: Exibe e pisca o label "Lucro Flutuante" no gráfico //---------------------------------------------------------------------- void BlinkFloatingProfit() { string objName = "FloatingProfitLabel"; // Cria o objeto se ele ainda não existir if(ObjectFind(0, objName) < 0) { ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 1530); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 610); // Posicionamento escolhido; ajuste se necessário ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 8); ObjectSetString(0, objName, OBJPROP_FONT, "Arial"); } // Atualiza o texto com o valor atual do Lucro Flutuante double floatingProfit = GetSymbolProfit(); string fpText = " (" + Symbol() + "): $ " + DoubleToStr(floatingProfit,2); ObjectSetString(0, objName, OBJPROP_TEXT, fpText); // Atualiza a cor para o efeito de piscar utilizando a variável fpBlinkColorIndex int totalColors = ArraySize(blinkColors); ObjectSetInteger(0, objName, OBJPROP_COLOR, blinkColors[fpBlinkColorIndex]); fpBlinkColorIndex = (fpBlinkColorIndex + 1) % totalColors; } //---------------------------------------------------------------------- // Funções de Gestão de Ordens e Cálculo de Lucro //---------------------------------------------------------------------- int CountAllOrdersOfSymbol() { int count = 0; for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol()) count++; } return count; } //+------------------------------------------------------------------+ //| Função que conta quantas ordens abertas existem de um tipo | //+------------------------------------------------------------------+ int CountOrdersByType(int orderType) { int count = 0; for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderType() == orderType && OrderSymbol() == Symbol()) count++; } } return count; } void CountBuySellOrders(int &buyCount, int &sellCount) { buyCount = CountOrdersByType(OP_BUY); sellCount = CountOrdersByType(OP_SELL); } double GetTotalProfit() { double total = 0; for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) total += OrderProfit(); } return total; } double GetTotalSellProfit() { double total = 0; for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderType() == OP_SELL) total += OrderProfit(); } return total; } double GetSymbolProfit() { double profit = 0; for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol()) profit += OrderProfit(); } return profit; } double GetDailySymbolProfit() { double dailyProfit = 0.0; datetime todayStart = StringToTime(TimeToString(TimeCurrent(), TIME_DATE)); for(int i = OrdersHistoryTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderSymbol() == Symbol() && OrderCloseTime() >= todayStart) dailyProfit += OrderProfit(); } } return dailyProfit; } double GetWeeklySymbolProfit() { double weeklyProfit = 0.0; // Início do dia atual (meia-noite) datetime todayStart = StringToTime(TimeToString(TimeCurrent(), TIME_DATE)); // TimeDayOfWeek retorna 0 para domingo, 1 para segunda, etc. int dw = TimeDayOfWeek(TimeCurrent()); int daysSinceMonday = (dw + 6) % 7; // Segunda = 0, domingo = 6 datetime weekStart = todayStart - daysSinceMonday * 86400; // 86400 s = 1 dia for(int i = OrdersHistoryTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderSymbol() == Symbol() && OrderCloseTime() >= weekStart) weeklyProfit += OrderProfit(); } } return weeklyProfit; } double GetMonthlySymbolProfit() { double monthlyProfit = 0.0; MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); // Define para o primeiro dia do mês, meia-noite dt.day = 1; dt.hour = 0; dt.min = 0; dt.sec = 0; datetime monthStart = StructToTime(dt); for(int i = OrdersHistoryTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderSymbol() == Symbol() && OrderCloseTime() >= monthStart) monthlyProfit += OrderProfit(); } } return monthlyProfit; } double GetLifetimeSymbolProfit() { double lifetimeProfit = 0.0; for(int i = OrdersHistoryTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderSymbol() == Symbol()) lifetimeProfit += OrderProfit(); } } return lifetimeProfit; } //---------------------------------------------------------------------- // Funções de Fechamento Seguro de Ordens com Simulação Realística //---------------------------------------------------------------------- bool SafeOrderClose(int ticket, double lots, double price, int slip, color arrow) { // Sobrescreve slippage no Strategy Tester int testSlip = slip + (IS_TESTING() && EnableRealisticTest ? MathRand() % (MaxSlippagePips + 1) : 0); // Calcula spread simulado ou real double spread = (IS_TESTING() && EnableRealisticTest) ? ( (MinSpreadPips + MathRand() % (MaxSpreadPips - MinSpreadPips + 1)) * pointAdjusted ) : (MarketInfo(Symbol(), MODE_ASK) - MarketInfo(Symbol(), MODE_BID)); // Ajusta preço considerando spread if(OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) { if(OrderType() == OP_BUY) price -= spread; else /*OP_SELL*/ price += spread; } // Tenta fechar com tentativas e aplica comissão no tester for(int i = 0; i < MAX_TRIES; i++) { if(OrderClose(ticket, lots, price, testSlip, arrow)) { if(IS_TESTING() && EnableRealisticTest && CommissionPerLot > 0) { double comm = lots * CommissionPerLot; Print(" Comissão simulada: $", DoubleToStr(comm,2), " no ticket ", ticket); } return true; } Sleep(WAIT_TIME); } LogError("Falha ao fechar a ordem", GetLastError()); return false; } int SafeOrderSend(string symbol, int type, double lots, double price, int slip, double sl, double tp, string comment, int magic, datetime expiration, color arrow) { // Sobrescreve slippage no Strategy Tester int testSlip = slip + (IS_TESTING() && EnableRealisticTest ? MathRand() % (MaxSlippagePips + 1) : 0); // Calcula spread simulado ou real double spread = (IS_TESTING() && EnableRealisticTest) ? ( (MinSpreadPips + MathRand() % (MaxSpreadPips - MinSpreadPips + 1)) * pointAdjusted ) : (MarketInfo(symbol, MODE_ASK) - MarketInfo(symbol, MODE_BID)); // Ajusta preço de entrada if(type == OP_BUY) price += spread; else price -= spread; // Envia ordem com tentativas int ticket; for(int i = 0; i < MAX_TRIES; i++) { ticket = OrderSend(symbol, type, lots, price, testSlip, sl, tp, comment, magic, expiration, arrow); if(ticket >= 0) return ticket; Sleep(WAIT_TIME); } LogError("Falha ao enviar ordem do tipo " + IntegerToString(type), GetLastError()); return -1; } //---------------------------------------------------------------------- // Funções Auxiliares para Fechamento de Ordens Baseado em Lucro //---------------------------------------------------------------------- bool OrdersReadyForClosure() { for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(TimeCurrent() - OrderOpenTime() < 60) return false; } } return true; } //──────────────────────────────────────────────────────────────── // RestartEA – limpa estados, labels e refaz configuração (como OnInit) //──────────────────────────────────────────────────────────────── void RestartEA() { // … limpa todos os OBJ_LABEL … ObjectsDeleteAll(0, OBJ_LABEL); // reseta flags, limpa lista, reconfigura… ValidateOrderInputs(); ConfigureChart(); ShowLogo(); DisplayParameterInfo(); // <<< recria o botão após o reset CreateFechaTudoButton(); Print("EA reiniciado após Take positivo de proteção contrária."); } void AttemptCloseOrders() { // Se o lucro total das ordens SELL atingir o alvo, fecha as ordens SELL if(GetTotalSellProfit() >= LinkedSellProfitTarget) CloseAllSellOrders(); // Se o fechamento automático pelo lucro do ativo estiver habilitado e o lucro do ativo // for maior ou igual ao limiar (por padrão: 50 USD), fecha todas as ordens if(EnableCloseOnAssetProfit && GetSymbolProfit() >= AssetProfitCloseThreshold) { Print("Lucro do ativo atingiu $ ", DoubleToStr(AssetProfitCloseThreshold,2), ". Fechando todas as ordens."); CloseAllOrders(); } // Outras condições de fechamento já existentes if(GetSymbolProfit() >= SymbolProfitTarget) CloseAllOrders(); if(GetTotalProfit() >= profitTarget) CloseAllOrders(); } void CloseAllOrders() { for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { double price = (OrderType() == OP_BUY) ? Bid : Ask; SafeOrderClose(OrderTicket(), OrderLots(), price, slippage, clrRed); } } lastTradeTime = TimeLocal(); } void CloseAllSellOrders() { for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderType() == OP_SELL) { SafeOrderClose(OrderTicket(), OrderLots(), Ask, slippage, clrRed); } } lastTradeTime = TimeLocal(); } //---------------------------------------------------------------------- // Função que verifica se uma ordem está suficientemente distante // da última ordem do mesmo tipo //---------------------------------------------------------------------- bool IsFarEnoughFromLastOrder(double currentPrice, int orderType) { // Garante que pointAdjusted esteja sempre válido if(pointAdjusted <= 0.0) pointAdjusted = MarketInfo(Symbol(), MODE_POINT); // Snapshot do total de ordens para evitar que o loop varie dinamicamente int totalOrders = OrdersTotal(); for(int i = 0; i < totalOrders; i++) { // Seleciona a ordem; ignora falhas de seleção if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; // Filtra apenas ordens deste símbolo e do tipo especificado if(OrderSymbol() != Symbol() || OrderType() != orderType) continue; // Calcula diferença em pontos entre o preço de abertura e o preço atual double priceDiff = MathAbs(OrderOpenPrice() - currentPrice) / pointAdjusted; // Se a diferença for menor que a mínima permitida, não está longe o bastante if(priceDiff < MinDistanceBetweenOrders) return false; } // Todas as ordens estão suficientemente distantes return true; } //+------------------------------------------------------------------+ //| Wrapper seguro de envio para Robo4 | //+------------------------------------------------------------------+ int Robo4_SafeOrderSend(int type, double lots, double price, int slippage, double sl, double tp, string comment, int magic, color arrowColor) { for(int attempt=1; attempt<=3; attempt++) { ResetLastError(); RefreshRates(); int ticket = OrderSend(Symbol(), type, lots, price, slippage, sl, tp, comment, magic, 0, arrowColor); if(ticket>0) return(ticket); if(GetLastError()==ERR_TRADE_CONTEXT_BUSY) Sleep(100); else break; } return(-1); } //+------------------------------------------------------------------+ //| Wrapper seguro de fechamento para Robo4 | //+------------------------------------------------------------------+ bool Robo4_SafeOrderClose(int ticket, double lots, double price, int slippage, color arrowColor) { for(int attempt=1; attempt<=3; attempt++) { ResetLastError(); RefreshRates(); if(OrderClose(ticket, lots, price, slippage, arrowColor)) return(true); if(GetLastError()==ERR_TRADE_CONTEXT_BUSY) Sleep(100); else break; } return(false); } //+------------------------------------------------------------------+ //| Abre BUY & SELL simultâneos | //+------------------------------------------------------------------+ void Robo4_OpenSimultaneousOrders() { if(TimeCurrent() - Robo4_lastOpenTime < Robo4_ReopenDelaySeconds) return; if(Robo4_HasOpenOrdersInRegion()) return; if(MarketInfo(Symbol(), MODE_SPREAD) > Robo4_MaxSpreadPoints) return; RefreshRates(); double pb = NormalizeDouble(Ask, Digits); double ps = NormalizeDouble(Bid, Digits); double tp = NormalizeDouble(ps - Robo4_SellTPPoints * Robo4_pointValue, Digits); int t1 = Robo4_SafeOrderSend(OP_BUY, Robo4_BuyLot, pb, Robo4_Slippage, 0, 0, "Robo4", Robo4_MagicNumber, clrBlue); int t2 = Robo4_SafeOrderSend(OP_SELL, Robo4_SellLot, ps, Robo4_Slippage, 0, tp, "Robo4", Robo4_MagicNumber, clrRed); if(t1>0 && t2>0) { Robo4_ordersOpened = true; Robo4_lastBuyPrice = pb; Robo4_lastSellPrice = ps; Robo4_lastOpenTime = TimeCurrent(); Robo4_gridRepeats = 0; } } //+------------------------------------------------------------------+ //| Fecha por ProfitTarget | //+------------------------------------------------------------------+ void Robo4_CheckProfitAndClose() { double profit = 0; for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderMagicNumber()==Robo4_MagicNumber) profit += OrderProfit()+OrderSwap()+OrderCommission(); if(profit>=Robo4_ProfitTarget) { int tickets[], cnt=0; for(int j=OrdersTotal()-1; j>=0; j--) if(OrderSelect(j,SELECT_BY_POS,MODE_TRADES) && OrderMagicNumber()==Robo4_MagicNumber) { ArrayResize(tickets, cnt+1); tickets[cnt++]=OrderTicket(); } for(int k=0; k=Robo4_MaxGridRepeats) return; if(TimeCurrent()-Robo4_lastGridTime0) { double d=(Robo4_lastBuyPrice - Ask)/Robo4_pointValue; if(d>=Robo4_GridDistancePoints) { Robo4_gridRepeats++; Robo4_lastGridTime = TimeCurrent(); Robo4_ordersOpened = false; Robo4_OpenSimultaneousOrders(); return; } } // SELL if(Robo4_lastSellPrice>0) { double d=(Bid - Robo4_lastSellPrice)/Robo4_pointValue; if(d>=Robo4_GridDistancePoints) { Robo4_gridRepeats++; Robo4_lastGridTime=TimeCurrent(); Robo4_ordersOpened=false; Robo4_OpenSimultaneousOrders(); } } } //+------------------------------------------------------------------+ //| Verifica ordens abertas na região | //+------------------------------------------------------------------+ bool Robo4_HasOpenOrdersInRegion() { for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderMagicNumber()==Robo4_MagicNumber) { double diff = MathAbs((OrderType()==OP_BUY?Ask:Bid) -OrderOpenPrice())/Robo4_pointValue; if(diff<=Robo4_GridDistancePoints) return(true); } return(false); } //+------------------------------------------------------------------+ //| Trade context busy? | //+------------------------------------------------------------------+ bool Robo4_TradeIsBusy() { if(GetLastError()==ERR_TRADE_CONTEXT_BUSY) { Sleep(50); ResetLastError(); return(true); } return(false); } //+------------------------------------------------------------------+ //| Executa Robo4 no OnTick | //+------------------------------------------------------------------+ void ExecuteRobo4() { if(Robo4_TradeIsBusy()) return; if(Robo4_pointValue==0) Robo4_pointValue = MarketInfo(Symbol(), MODE_POINT); if(Robo4_EnableAutoProfitClose) Robo4_CheckProfitAndClose(); if(!Robo4_ordersOpened) Robo4_OpenSimultaneousOrders(); else Robo4_CheckGridReopen(); } //--------------------------------------------------------------------------- // Estratégias de Operação: Robo1 e Robo2 (Martingale) //--------------------------------------------------------------------------- //==================== EXECUTE ROBO2 FINALIZADO ==================== void ExecuteRobo2() { double ask = MarketInfo(Symbol(), MODE_ASK); double bid = MarketInfo(Symbol(), MODE_BID); bool hasBuy = false; bool hasSell = false; // --- LIMITADOR DE ORDENS ROBO2 (filtra por magicNumberRobo2) --- int totalOrders = OrdersTotal(); int ordersRobo2 = 0; int buyOrders2 = 0; int sellOrders2 = 0; for(int i = 0; i < totalOrders; i++) { if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; if(OrderSymbol() != Symbol() || OrderMagicNumber() != magicNumberRobo2) continue; ordersRobo2++; if(OrderType() == OP_BUY) buyOrders2++; if(OrderType() == OP_SELL) sellOrders2++; } if(ordersRobo2 >= MaxOrdersRobo2) { Print("Robo2: limite de ordens atingido: ", ordersRobo2); return; } // --- Detecta se já existem BUY/SELL do Robo2 --- hasBuy = (buyOrders2 > 0); hasSell = (sellOrders2 > 0); // --- Pausa se fechamento manual e auto-recreate desligado --- if(!EnableAutoRecreate && manualOrderClosed) return; // --- Abre BUY do Robo2 se não existir --- if(!hasBuy && IsFarEnoughFromLastOrder(ask, OP_BUY)) { double sl = NormalizeDouble(bid - ExtLot_SL * pointAdjusted, Digits); double tp = NormalizeDouble(bid + ExtLot_TP * pointAdjusted, Digits); int ticket = SafeOrderSend( Symbol(), OP_BUY, actualExtLot, ask, slippage, sl, tp, "Robo2 BUY", magicNumberRobo2, 0, clrBlue ); if(ticket < 0) LogError("Robo2: falha ao abrir BUY", GetLastError()); else manualOrderClosed = false; } // --- Abre SELL do Robo2 se não existir --- if(!hasSell && IsFarEnoughFromLastOrder(bid, OP_SELL)) { double sl = NormalizeDouble(ask + LinkedSell_SL * pointAdjusted, Digits); double tp = NormalizeDouble(ask - LinkedSell_TP * pointAdjusted, Digits); int ticket = SafeOrderSend( Symbol(), OP_SELL, actualLinkedSellLot, bid, slippage, sl, tp, "Robo2 SELL", magicNumberRobo2, 0, clrRed ); if(ticket < 0) LogError("Robo2: falha ao abrir SELL", GetLastError()); else manualOrderClosed = false; } } //==================== ABERTURA DE ORDENS ==================== // Abre uma ordem de compra e, se bem-sucedido, abre ordem de venda vinculada void OpenBuy(double lot, double sl, double tp) { sl = NormalizeDouble(sl, Digits); tp = NormalizeDouble(tp, Digits); int buyTicket = SafeOrderSend( Symbol(), OP_BUY, lot, Ask, slippage, sl, tp, "BUY Fusion @Lok_Trade", magicNumberRobo2, 0, clrBlue ); if(buyTicket >= 0) { manualOrderClosed = false; double sellSL = NormalizeDouble(Ask + LinkedSell_SL * pointAdjusted, Digits); double sellTP = NormalizeDouble(Ask - LinkedSell_TP * pointAdjusted, Digits); int sellTicket = SafeOrderSend( Symbol(), OP_SELL, linkedSellLot, Bid, slippage, sellSL, sellTP, "Venda Vinc.", magicNumberRobo2, 0, clrRed ); if(sellTicket < 0) LogError("Falha ao abrir ordem de venda vinculada", GetLastError()); } else LogError("Falha ao abrir ordem de compra", GetLastError()); } // Abre uma ordem de venda simples void OpenSell(double lot, double sl, double tp) { sl = NormalizeDouble(sl, Digits); tp = NormalizeDouble(tp, Digits); int sellTicket = SafeOrderSend( Symbol(), OP_SELL, lot, Ask, slippage, sl, tp, "SELL Fusion @Lok_Trade", magicNumberRobo2, 0, clrRed ); if(sellTicket >= 0) manualOrderClosed = false; else LogError("Falha ao abrir ordem de venda", GetLastError()); } //==================== FECHAMENTO POR PONTOS (SELL) ==================== // Fecha SELLs que atingirem lucro em pontos, desde que abertas >60s void CloseSellOrdersByProfitPoints() { int totalOrders = OrdersTotal(); for(int i = 0; i < totalOrders; i++) { if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; if(OrderSymbol() != Symbol() || OrderType() != OP_SELL || OrderMagicNumber() != magicNumberRobo2) continue; // Só ordens abertas há pelo menos 60 segundos if(TimeCurrent() - OrderOpenTime() < 60) continue; double profitPoints = (OrderOpenPrice() - MarketInfo(Symbol(), MODE_BID)) / pointAdjusted; if(profitPoints >= SellProfitThresholdPoints) { Print("Fechando SELL ticket ", OrderTicket(), " lucro de ", DoubleToStr(profitPoints,1), " puntos."); SafeOrderClose( OrderTicket(), OrderLots(), MarketInfo(Symbol(), MODE_BID), slippage, clrGreen ); } } } //---------------------------------------------------------------------- // Função para o Martingale Negativo //---------------------------------------------------------------------- // Retorna a contagem de níveis já acionados para uma ordem (se não encontrada, retorna 0) int GetMartingaleCount(int ticket) { for(int i = 0; i < ArraySize(martingaleList); i++) { if(martingaleList[i].ticket == ticket) return martingaleList[i].count; } return 0; } // Atualiza (ou adiciona) a contagem para uma ordem void UpdateMartingaleCount(int ticket, int newCount) { for(int i = 0; i < ArraySize(martingaleList); i++) { if(martingaleList[i].ticket == ticket) { martingaleList[i].count = newCount; return; } } int currentSize = ArraySize(martingaleList); ArrayResize(martingaleList, currentSize + 1); martingaleList[currentSize].ticket = ticket; martingaleList[currentSize].count = newCount; } // Remove da lista as informações de ordens que já foram fechadas void CleanMartingaleList() { for(int i = ArraySize(martingaleList) - 1; i >= 0; i--) { // Se não for possível selecionar a ordem ativa com esse ticket, presume que foi fechada if(!OrderSelect(martingaleList[i].ticket, SELECT_BY_TICKET, MODE_TRADES)) { for(int j = i; j < ArraySize(martingaleList) - 1; j++) { martingaleList[j] = martingaleList[j + 1]; } ArrayResize(martingaleList, ArraySize(martingaleList) - 1); } } } //---------------------------------------------------------------------- // Função que verifica as ordens abertas e dispara o Martingale Negativo //---------------------------------------------------------------------- void CheckAndTriggerMartingaleOrders() { // 0) Se Martingale Negativo estiver desativado, sai if(!EnableNegativeMartingale) return; // 1) Limpa da lista as ordens que já foram fechadas CleanMartingaleList(); // 2) Snapshot do total de ordens para não variar durante o loop int totalOrders = OrdersTotal(); for(int i = 0; i < totalOrders; i++) { // 2.1) Seleciona a ordem if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; // 2.2) Filtra apenas ordens do Robo2 original if(OrderMagicNumber() != magicNumberRobo2) continue; // 2.3) Filtra apenas ordens deste símbolo if(OrderSymbol() != Symbol()) continue; // 2.4) Ignora ordens já marcadas como Martingale if(StringFind(OrderComment(), "Martingale") >= 0) continue; // 2.5) Só BUY ou SELL int orderType = OrderType(); if(orderType != OP_BUY && orderType != OP_SELL) continue; // 3) Calcula perda em pontos double lossPoints = 0.0; if(orderType == OP_BUY) lossPoints = (OrderOpenPrice() - MarketInfo(Symbol(), MODE_BID)) / pointAdjusted; else // OP_SELL lossPoints = (MarketInfo(Symbol(), MODE_ASK) - OrderOpenPrice()) / pointAdjusted; // 4) Se não atingiu o limiar, continua if(lossPoints < NegativeMartingaleThreshold) continue; // 5) Determina quantos níveis já foram atingidos int reachedLevels = int(lossPoints / NegativeMartingaleThreshold); int alreadyTriggered = GetMartingaleCount(OrderTicket()); int newTriggers = reachedLevels - alreadyTriggered; if(newTriggers <= 0) continue; // 6) Dispara cada novo nível de Martingale for(int t = 0; t < newTriggers; t++) { double price = (orderType == OP_BUY ? MarketInfo(Symbol(), MODE_ASK) : MarketInfo(Symbol(), MODE_BID)); double newSL = (orderType == OP_BUY ? price - ExtLot_SL * pointAdjusted : price + ExtLot_SL * pointAdjusted); double newTP = (orderType == OP_BUY ? price + ExtLot_TP * pointAdjusted : price - ExtLot_TP * pointAdjusted); string comment = (orderType == OP_BUY ? "Martingale Buy" : "Martingale Sell"); int type = (orderType == OP_BUY ? OP_BUY : OP_SELL); color arrow = (orderType == OP_BUY ? clrBlue : clrRed); int ticket = SafeOrderSend( Symbol(), type, NegativeMartingaleLot, price, slippage, NormalizeDouble(newSL, Digits), NormalizeDouble(newTP, Digits), comment, magicNumberMartNeg, 0, arrow ); if(ticket < 0) LogError("Falha ao abrir ordem " + comment, GetLastError()); else Print(comment, " disparada para ticket ", OrderTicket(), ". Nova ordem ticket: ", ticket); } // 7) Atualiza quantos níveis já foram acionados para esta ordem UpdateMartingaleCount(OrderTicket(), reachedLevels); } } //---------------------------------------------------------------------- // Função: Exibe o spread atual no gráfico usando objetos gráficos //---------------------------------------------------------------------- void ShowSpreadOnChart() { string spreadLabel = "LiveSpreadLabel"; double spread = (MarketInfo(Symbol(), MODE_ASK) - MarketInfo(Symbol(), MODE_BID)) / pointAdjusted; if(ObjectFind(0, spreadLabel) < 0) ObjectCreate(0, spreadLabel, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, spreadLabel, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, spreadLabel, OBJPROP_XDISTANCE, 30); ObjectSetInteger(0, spreadLabel, OBJPROP_YDISTANCE, 30); ObjectSetInteger(0, spreadLabel, OBJPROP_FONTSIZE, 15); ObjectSetInteger(0, spreadLabel, OBJPROP_COLOR, clrYellow); ObjectSetString(0, spreadLabel, OBJPROP_TEXT, "Spread Atual: " + DoubleToStr(spread, 1) + " pts"); } //---------------------------------------------------------------------- // Checa se uma ordem de compra foi fechada por SL e, se sim, fecha as ordens de venda //---------------------------------------------------------------------- void CheckClosedBuyBySL() { datetime now = TimeCurrent(); if(now == lastSLCheck) return; lastSLCheck = now; for(int i = OrdersHistoryTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderType() == OP_BUY && OrderCloseTime() > TimeCurrent()-10 && OrderClosePrice() <= OrderStopLoss() && OrderProfit() < 0) { MessageBox("SL para COMPRA acionado!\nFechando todas as ordens de VENDA", "Alerta SL COMPRA", MB_OK | MB_ICONWARNING); CloseAllSellOrders(); break; } } } } //+------------------------------------------------------------------+ //| Detecta fechamento manual das ordens do Robo4 e reseta a flag | //+------------------------------------------------------------------+ void CheckManualClosureRobo4() { static datetime lastCheckTime = 0; // Executa no máximo uma vez por segundo if(TimeCurrent() == lastCheckTime) return; lastCheckTime = TimeCurrent(); // Conta quantas ordens do Robo4 ainda estão abertas int openCount = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderMagicNumber() == Robo4_MagicNumber) openCount++; } // Se diminuiu → fechamento manual detectado if(openCount < lastRobo4Orders) { Robo4_ordersOpened = false; // marca que não há ordens abertas do Robo4 Robo4_lastOpenTime = 0; // zera o timer para reabertura imediata Print("Detecção Robo4: ordem fechada manualmente. Pronto para reabrir."); } // Atualiza o contador global lastRobo4Orders = openCount; } //---------------------------------------------------------------------- // NOVA FUNÇÃO: TriggerContrarianProtection // // Calcula a exposição líquida (soma dos lotes de compra menos dos de venda) // e, se houver exposição, abre uma ordem contrária proporcional ao percentual definido. // Também exibe mensagens de log para acompanhamento. //---------------------------------------------------------------------- //──────────────────────────────────────────────────────────────── // TriggerContrarianProtection – abre hedge com SL/TP e seta flags //──────────────────────────────────────────────────────────────── void TriggerContrarianProtection() { // calcula exposição líquida double netExposure = 0.0; for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol()) netExposure += (OrderType() == OP_BUY ? OrderLots() : -OrderLots()); } if(netExposure == 0.0) { Print("Exposição neutra. Proteção Contrária não acionada."); return; } int hedgeType; double price, sl, tp; if(netExposure > 0) // posição LONG → abre SELL hedge { hedgeType = OP_SELL; price = MarketInfo(Symbol(), MODE_BID); sl = price + ContrarianSLPoints * pointAdjusted; tp = price - ContrarianTPPoints * pointAdjusted; } else // posição SHORT → abre BUY hedge { hedgeType = OP_BUY; price = MarketInfo(Symbol(), MODE_ASK); sl = price - ContrarianSLPoints * pointAdjusted; tp = price + ContrarianTPPoints * pointAdjusted; } double hedgeLot = NormalizeDouble(fabs(netExposure) * (ContrarianProtectionPercentage/100.0), 2); int ticket = SafeOrderSend( Symbol(), hedgeType, hedgeLot, price, slippage, NormalizeDouble(sl, Digits), NormalizeDouble(tp, Digits), (hedgeType == OP_SELL ? "Prot. Contr – SELL TP/SL" : "Prot. Contr – BUY TP/SL"), magicNumber, 0, (hedgeType == OP_SELL ? clrRed : clrBlue) ); if(ticket >= 0) { Print("Proteção Contrária acionada (hedge ticket ", ticket, ") | SL=", DoubleToStr(sl, Digits), " | TP=", DoubleToStr(tp, Digits)); // impede novas execuções até fechar o take combinado stopTrading = true; protectionTriggered = true; } else LogError("Falha ao abrir hedge da proteção", GetLastError()); } //──────────────────────────────────────────────────────────────── //---------------------------------------------------------------------- // Atualiza no gráfico as informações de spread, lucros e ordens em aberto //---------------------------------------------------------------------- void UpdateDrawdown() { double spread = (MarketInfo(Symbol(), MODE_ASK) - MarketInfo(Symbol(), MODE_BID)) / pointAdjusted; // Obtém os lucros do ativo double floatingProfit = GetSymbolProfit(); double dailyProfit = GetDailySymbolProfit(); double weeklyProfit = GetWeeklySymbolProfit(); double monthlyProfit = GetMonthlySymbolProfit(); double lifetimeProfit = GetLifetimeSymbolProfit(); // Lucro acumulado desde o início da conta int buyCount = 0, sellCount = 0; double totalBuyLots = 0.0, totalSellLots = 0.0; // Conta as ordens abertas para o símbolo atual for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol()) { if(OrderType() == OP_BUY) { buyCount++; totalBuyLots += OrderLots(); } else if(OrderType() == OP_SELL) { sellCount++; totalSellLots += OrderLots(); } } } // Monta a string de informação para exibir no gráfico string info = "Spread Atual: " + DoubleToStr(spread, 1) + " pts\n\n"; info += "Lucro Flutuante (" + Symbol() + "): $ " + DoubleToStr(floatingProfit,2) + "\n"; info += "Lucro Diário (" + Symbol() + "): $ " + DoubleToStr(dailyProfit,2) + "\n"; info += "Lucro Semanal (" + Symbol() + "): $ " + DoubleToStr(weeklyProfit,2) + "\n"; info += "Lucro Mensal (" + Symbol() + "): $ " + DoubleToStr(monthlyProfit,2) + "\n"; info += "Lucro Geral (Conta): $ " + DoubleToStr(lifetimeProfit,2) + "\n\n"; info += "BUY: " + IntegerToString(buyCount) + " ordens | " + DoubleToStr(totalBuyLots,2) + " lote(s)\n "; info += "SELL: " + IntegerToString(sellCount) + " ordens | " + DoubleToStr(totalSellLots,2) + " lote(s) "; // Exibe a informação na área de comentários do gráfico Comment(info); } //+------------------------------------------------------------------+ //| Cria o botão “FechaTudo” no gráfico com visual 3D melhorado | //+------------------------------------------------------------------+ void CreateFechaTudoButton() { if(!EnableFechaTudoButton) return; // Se já existe, deleta para recriar if(ObjectFind(0, FechaTudoBtnName) >= 0) ObjectDelete(0, FechaTudoBtnName); // Cria o botão if(!ObjectCreate(0, FechaTudoBtnName, OBJ_BUTTON, 0, 0, 0)) { Print("Erro ao criar botão FechaTudo: ", GetLastError()); return; } // Posição e tamanho ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_XDISTANCE, 133); ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_YDISTANCE, 632); ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_XSIZE, 90); ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_YSIZE, 25); // Estilo visual bonito e 3D ObjectSetString (0, FechaTudoBtnName, OBJPROP_TEXT, " Fecha Tudo"); // Texto com emoji ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_FONTSIZE, 11); // Tamanho do texto ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_COLOR, clrWhite); // Cor do texto ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_BGCOLOR, clrFireBrick); // Cor de fundo vermelha escura ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_BORDER_TYPE, BORDER_RAISED); // Borda 3D ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_BORDER_COLOR, clrBlack); // Cor da borda ObjectSetInteger(0, FechaTudoBtnName, OBJPROP_BACK, true); // Fundo habilitado } //---------------------------------------------------------------------- // Função: Detecta fechamento manual de ordens e gerencia avisos //---------------------------------------------------------------------- void CheckManualOrderClosure() { static int lastOrders = 0; static datetime lastCheckTime = 0; if(TimeCurrent() == lastCheckTime) return; lastCheckTime = TimeCurrent(); int currentOrders = OrdersTotal(); if(currentOrders < lastOrders) { manualOrderClosed = true; Print(" Detecção: Ordem foi fechada manualmente! Recriação pausada."); ShowManualClosureWarning(); } else if(currentOrders > lastOrders && manualOrderClosed) { manualOrderClosed = false; HideManualClosureWarning(); Print(" Novo movimento detectado. Recriação automática liberada."); } lastOrders = currentOrders; } //══════════════════════════════════════════════════════════════════ // Função: Aplicar o Preset escolhido para o DRL //══════════════════════════════════════════════════════════════════ void ApplyDRLPreset() { switch(DRLPreset) { case DRL_Preset_Default: // Padrão de Lok_Trade DRLLearningRate = 0.03; DRLProfitRewardThreshold = 100; DRLLossPunishThreshold = -50; DRL_MinMovementThreshold = 200; DRL_MaxMovementThreshold = 1000; DRL_MinLotSize = 0.01; DRL_MaxLotSize = 1.0; break; case DRL_Preset_AggressiveTurbo: // Agressivo Turbo DRLLearningRate = 0.5; DRLProfitRewardThreshold = 50; DRLLossPunishThreshold = -30; DRL_MinMovementThreshold = 100; DRL_MaxMovementThreshold = 1500; DRL_MinLotSize = 0.02; DRL_MaxLotSize = 10.0; break; case DRL_Preset_ConservativeSafe: // Conservador Protegido DRLLearningRate = 0.02; DRLProfitRewardThreshold = 150; DRLLossPunishThreshold = -100; DRL_MinMovementThreshold = 300; DRL_MaxMovementThreshold = 2500; DRL_MinLotSize = 0.01; DRL_MaxLotSize = 1.0; break; case DRL_Preset_UltraAdaptive: // Ultra Adaptativo DRLLearningRate = 0.07; DRLProfitRewardThreshold = 80; DRLLossPunishThreshold = -40; DRL_MinMovementThreshold = 150; DRL_MaxMovementThreshold = 1800; DRL_MinLotSize = 0.01; DRL_MaxLotSize = 2.0; break; case DRL_Preset_StaticMode: // Modo Estático (DRL OFF) DRLLearningRate = 0.0; DRLProfitRewardThreshold = 99999; DRLLossPunishThreshold = -99999; DRL_MinMovementThreshold = (int)movementThreshold; DRL_MaxMovementThreshold = (int)movementThreshold; DRL_MinLotSize = lotBuy; DRL_MaxLotSize = lotBuy; break; // Novos Presets case DRL_Preset_MomentumFocus: // Foco em Momentum DRLLearningRate = 0.08; DRLProfitRewardThreshold = 120; DRLLossPunishThreshold = -60; DRL_MinMovementThreshold = 250; DRL_MaxMovementThreshold = 2200; DRL_MinLotSize = 0.02; DRL_MaxLotSize = 2.0; break; case DRL_Preset_VolatilityOptimized: // Otimizado para Volatilidade DRLLearningRate = 0.03; DRLProfitRewardThreshold = 90; DRLLossPunishThreshold = -45; DRL_MinMovementThreshold = 300; DRL_MaxMovementThreshold = 3000; DRL_MinLotSize = 0.01; DRL_MaxLotSize = 2.0; break; case DRL_Preset_RiskOn: // Risco Ativo DRLLearningRate = 0.1; DRLProfitRewardThreshold = 70; DRLLossPunishThreshold = -20; DRL_MinMovementThreshold = 150; DRL_MaxMovementThreshold = 1600; DRL_MinLotSize = 0.03; DRL_MaxLotSize = 5.0; break; case DRL_Preset_RiskOff: // Risco Desligado DRLLearningRate = 0.03; DRLProfitRewardThreshold = 200; DRLLossPunishThreshold = -150; DRL_MinMovementThreshold = 400; DRL_MaxMovementThreshold = 3500; DRL_MinLotSize = 0.005; DRL_MaxLotSize = 1.0; break; case DRL_Preset_BalancedConservative: // Balanceado Conservador DRLLearningRate = 0.04; DRLProfitRewardThreshold = 110; DRLLossPunishThreshold = -70; DRL_MinMovementThreshold = 220; DRL_MaxMovementThreshold = 2000; DRL_MinLotSize = 0.01; DRL_MaxLotSize = 2.0; break; case DRL_Preset_BalancedAggressive: // Balanceado Agressivo DRLLearningRate = 0.09; DRLProfitRewardThreshold = 85; DRLLossPunishThreshold = -35; DRL_MinMovementThreshold = 180; DRL_MaxMovementThreshold = 1700; DRL_MinLotSize = 0.02; DRL_MaxLotSize = 5.0; break; } } // Variáveis de controle de inputs para SL/TP (declarar no escopo global) static int lastGlobalSL; static int lastGlobalTP; static double lastSellProfitPts; double movementThreshold_runtime; double lotBuy_runtime; double lotSell_runtime; //+------------------------------------------------------------------+ //| Função Principal de Inicialização do EA | //+------------------------------------------------------------------+ int OnInit() { // 1) Copiar inputs do DRL para variáveis internas DRLLearningRate = DRLLearningRateInput; DRLProfitRewardThreshold = DRLProfitRewardThresholdInput; DRLLossPunishThreshold = DRLLossPunishThresholdInput; DRL_MinMovementThreshold = DRL_MinMovementThresholdInput; DRL_MaxMovementThreshold = DRL_MaxMovementThresholdInput; DRL_MinLotSize = DRL_MinLotSizeInput; DRL_MaxLotSize = DRL_MaxLotSizeInput; ApplyDRLPreset(); ArrayResize(avgProfitMemory, DRLMemorySize); // 2) Inicializar suporte a indicadores e presets externos InitializeExternalSupport(); // 3) Inicializar variáveis de runtime com valores de input movementThreshold_runtime = movementThreshold; lotBuy_runtime = lotBuy; lotSell_runtime = lotSell; // 4) Copiar inputs de SL/TP dinâmicos para variáveis de controle lastGlobalSL = lastGlobalSLInput; lastGlobalTP = lastGlobalTPInput; lastSellProfitPts = lastSellProfitPtsInput; // 5) Exibir logo no gráfico ShowLogo(); // 6) Validar data de validade do EA e notificar { datetime expirationDate = StrToTime(ValidadeEA); int daysToExpire = int((expirationDate - TimeCurrent()) / 86400); if(daysToExpire > 0 && daysToExpire <= RenewalNotificationDays) { Alert("AVISO: EA atualiza em ", daysToExpire, " dias. Contate o programador!"); Print("AVISO: EA atualiza em ", daysToExpire, " dias. Validade: ", ValidadeEA); renewalNotified = true; } if(TimeCurrent() >= expirationDate) { Alert("EA expirado em ", ValidadeEA, ". Contate o programador!"); Print("EA expirado em ", ValidadeEA); return(INIT_FAILED); } } // 7) Verificar configuração de e‑mail e push emailSetupOk = TerminalInfoInteger(TERMINAL_EMAIL_ENABLED); pushSetupOk = TerminalInfoInteger(TERMINAL_NOTIFICATIONS_ENABLED); if(EnableEmailAlerts && !emailSetupOk) Print("EnableEmailAlerts=TRUE mas e‑mail não está configurado em Ferramentas→Opções→E‑mail."); if(EnablePushAlerts && !pushSetupOk) Print("EnablePushAlerts=TRUE mas notificações móveis não estão configuradas em Ferramentas→Opções→Notificações."); // 8) Validar parâmetros de lote ValidateOrderInputs(); // 9) Inicializar pointAdjusted ANTES de qualquer cálculo de distância pointAdjusted = MarketInfo(Symbol(), MODE_POINT); if(pointAdjusted <= 0.0) { Print("Erro ao obter pointAdjusted: ", pointAdjusted); return(INIT_FAILED); } // 10) Seed para efeitos de cor/piscar MathSrand(TimeLocal()); // 11) Configurar cores e estilo do gráfico ConfigureChart(); // 12) Re-exibir logo (caso ConfigureChart o tenha apagado) ShowLogo(); // 13) Exibir painel com os parâmetros atuais DisplayParameterInfo(); // 14) Criar botão “FechaTudo”, se habilitado if(EnableFechaTudoButton) CreateFechaTudoButton(); // 15) Inicializar lista de martingale ArrayResize(martingaleList, 0); // 16) Reajustar controles de SL/TP de acordo com inputs globais lastGlobalSL = GlobalStopLoss; lastGlobalTP = GlobalTakeProfit; lastSellProfitPts = SellProfitThresholdPoints; // 17) Capturar preços iniciais para filtro de spikes lastAsk = MarketInfo(Symbol(), MODE_ASK); lastBid = MarketInfo(Symbol(), MODE_BID); // Inicialização Robo4 Robo4_ordersOpened = false; Robo4_lastBuyPrice = 0.0; Robo4_lastSellPrice = 0.0; Robo4_lastOpenTime = 0; Robo4_gridRepeats = 0; Robo4_lastGridTime = 0; Robo4_pointValue = MarketInfo(Symbol(), MODE_POINT); // --- Ajuste específico para Robo4 --- int existing = 0; for(int i = OrdersTotal()-1; i >= 0; i--) if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderMagicNumber() == Robo4_MagicNumber) existing++; // Se já houver pelo menos um par, considera que já abriu Robo4_ordersOpened = (existing >= 2); lastRobo4Orders = existing; return(INIT_SUCCEEDED); } //---------------------------------------------------------------------- // Função principal executada a cada tick do mercado //---------------------------------------------------------------------- void OnTick() { ReadExternalPreset(); // Lê o preset externo CheckExternalSignals(); // Verifica sinais externos UpdateDRL(); ShowDRLPanel(); // … filtros de spike … // Pausa se estiver fora do horário permitido if(!IsWithinTradingWindow()) return; // --- Proteção contra spikes (estático + ATR) --- double curAsk = MarketInfo(Symbol(), MODE_ASK); double curBid = MarketInfo(Symbol(), MODE_BID); double deltaAsk = MathAbs(curAsk - lastAsk) / pointAdjusted; double deltaBid = MathAbs(curBid - lastBid) / pointAdjusted; // DEBUG: escreva no diário do tester PrintFormat("DEBUG Spike → deltaAsk = %.1f pts, deltaBid = %.1f pts", deltaAsk, deltaBid); // —— Novo filtro estático de spike com flag —— if(EnableSpikeFilter) { bool isSpike = (deltaAsk > MaxSpikeThresholdPoints || deltaBid > MaxSpikeThresholdPoints); if(isSpike && !spikeAlerted) { Notificar("Spike detectado: " + IntegerToString((int)deltaAsk) + "/" + IntegerToString((int)deltaBid) + " pts – tick ignorado."); spikeAlerted = true; } else if(!isSpike) { // Quando voltar ao normal, reseta para permitir nova notificação futuramente spikeAlerted = false; } // não damos return aqui para não pausar todo o OnTick } // —— Novo filtro ATR de spike com flag —— static bool atrSpikeAlerted = false; if(EnableATRSpikeFilter) { double atrPts = iATR(Symbol(), Period(), 14, 0) / pointAdjusted; double lim = atrPts * ATRSpikeMultiplier; bool isAtrSpike = (deltaAsk > lim || deltaBid > lim); if(isAtrSpike && !atrSpikeAlerted) { Notificar("Spike ATR detectado (desvio " + DoubleToStr(deltaAsk,1) + " pts > " + DoubleToStr(lim,1) + " pts). Tick ignorado."); atrSpikeAlerted = true; } else if(!isAtrSpike) { atrSpikeAlerted = false; } } // atualiza lastAsk/lastBid para o próximo tick lastAsk = curAsk; lastBid = curBid; // --- Rechecagem do aviso antecipado (caso o EA fique no gráfico por muito tempo) --- if(!renewalNotified) { datetime expirationDate = StrToTime(ValidadeEA); int daysToExpire = int((expirationDate - TimeCurrent()) / 86400); if(daysToExpire > 0 && daysToExpire <= RenewalNotificationDays) { Alert("AVISO: EA Entra Em Manutenção em ", daysToExpire, " dias. Chame o Programador no Menu!"); Print("AVISO: EA Entra Em Manutenção em ", daysToExpire, " dias. Data de Atualizar: ", ValidadeEA); renewalNotified = true; } } // 0) Fechamento forçado via input (teste/backtest) if(ForceCloseAll) { CloseAllOrders(); // opcionalmente: Alert("Fechando tudo via input ForceCloseAll"); } // 0) FECHAMENTO INDEPENDENTE POR LUCRO DO ATIVO if(EnableCloseOnAssetProfit && GetSymbolProfit() >= AssetProfitCloseThreshold) { Print("AssetProfitCloseThreshold atingido: fechando todas as ordens e reiniciando EA."); CloseAllOrders(); // fecha todas as ordens, sem filtrar por magic RestartEA(); // limpa estados e recria tudo como no OnInit return; // interrompe o restante do OnTick } // 1) STOP POR DRAWDOWN DE EQUITY double eq = AccountEquity(); double bal = AccountBalance(); if(eq <= bal * (1 - EquityStopLossPercent/100.0)) { Alert("Drawdown acima de ", EquityStopLossPercent, "%! ", PauseAfterEquityStop ? "Pausando EA." : "Continuando operações."); CloseAllOrders(); if(PauseAfterEquityStop) { stopTrading = true; } else { stopTrading = false; // 🔥 FORÇA CONTINUAR OPERANDO } return; } // --- PROTEÇÃO CONTRÁRIA IMEDIATA (apenas 1x) --- if(EnableContrarianProtection && !protectionTriggered && GetSymbolProfit() <= -ActiveLossThreshold) { Print(" Ativando proteção imediata (prejuízo = ", DoubleToStr(GetSymbolProfit(),2), ")"); // 1) Calcula exposição líquida (long = +, short = -) double netExposure = 0.0; for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol()) netExposure += (OrderType() == OP_BUY ? OrderLots() : -OrderLots()); } // 2) Se há exposição, abre hedge oposto if(netExposure != 0.0) { int hedgeType = (netExposure > 0 ? OP_SELL : OP_BUY); double price = (hedgeType == OP_SELL ? MarketInfo(Symbol(), MODE_BID) : MarketInfo(Symbol(), MODE_ASK)); double sl = NormalizeDouble( price + (hedgeType == OP_SELL ? ContrarianSLPoints : -ContrarianSLPoints) * pointAdjusted, Digits ); double tp = NormalizeDouble( price - (hedgeType == OP_SELL ? ContrarianTPPoints : -ContrarianTPPoints) * pointAdjusted, Digits ); double hedgeLot = NormalizeDouble( fabs(netExposure) * (ContrarianProtectionPercentage/100.0), 2 ); // 3) Envia a ordem de hedge int ticket = SafeOrderSend( Symbol(), // símbolo hedgeType, // OP_SELL ou OP_BUY hedgeLot, // lote calculado price, // preço de entrada slippage, // slippage sl, // stop loss tp, // take profit (hedgeType == OP_SELL ? "Hedge Imediato SELL" : "Hedge Imediato BUY"), magicNumber, // magic number 0, // expiration (hedgeType == OP_SELL ? clrRed : clrBlue) // cor da seta ); if(ticket >= 0) { Print(" Proteção Imediata enviada (Ticket: ", ticket, ")"); protectionTriggered = true; stopTrading = true; } else LogError(" Falha ao abrir Proteção Imediata", GetLastError()); } } // --- Detecta alterações nos inputs de SL/TP e ajusta ordens ativas --- if(GlobalStopLoss != lastGlobalSL || GlobalTakeProfit != lastGlobalTP || SellProfitThresholdPoints != lastSellProfitPts) { for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol()) { double openPrice = OrderOpenPrice(); double newSL, newTP; if(OrderType() == OP_BUY) { newSL = NormalizeDouble(openPrice - GlobalStopLoss * pointAdjusted, Digits); newTP = NormalizeDouble(openPrice + GlobalTakeProfit * pointAdjusted, Digits); } else // OP_SELL { newSL = NormalizeDouble(openPrice + GlobalStopLoss * pointAdjusted, Digits); newTP = NormalizeDouble(openPrice - GlobalTakeProfit * pointAdjusted, Digits); } if(newSL != OrderStopLoss() || newTP != OrderTakeProfit()) OrderModify(OrderTicket(), openPrice, newSL, newTP, 0, clrNONE); } } // Atualiza controle lastGlobalSL = GlobalStopLoss; lastGlobalTP = GlobalTakeProfit; lastSellProfitPts = SellProfitThresholdPoints; } // 2) MODO RESCUE PÓS‑HEDGE if(stopTrading) { if(EnableRescueMode) { double rescueThreshold = (ContrarianTPPoints * pointAdjusted) * 0.5; if(GetSymbolProfit() >= rescueThreshold) { stopTrading = false; actualLotBuy = 0.01; actualLotSell = 0.01; actualExtLot = 0.01; actualLinkedSellLot = 0.01; Print("Modo Rescue: EA reativado com micro‑lotes"); } else return; } else return; } // 3) PROTEÇÃO CONTRÁRIA E RESTART if(EnableContrarianProtection && !protectionTriggered && GetSymbolProfit() <= -ActiveLossThreshold) { Print("Saldo do ativo atingiu ", DoubleToStr(GetSymbolProfit(),2), " USD. Ativando Proteção Contrária."); TriggerContrarianProtection(); } if(protectionTriggered && GetSymbolProfit() >= ProfitCloseAfterProtectionThreshold) { Print("Lucro do ativo chegou a $", DoubleToStr(ProfitCloseAfterProtectionThreshold,2), ". Fechando todas as ordens e reiniciando EA."); CloseAllOrders(); RestartEA(); return; } // 4) CÁLCULO DO LOTE DINÂMICO if(EnableDynamicLot) { double saldo = AccountBalance(); actualLotBuy = NormalizeDouble(saldo * DynamicLotFactorBuy, 2); actualLotSell = NormalizeDouble(saldo * DynamicLotFactorSell, 2); actualExtLot = NormalizeDouble(saldo * DynamicLotFactorExtLot, 2); actualLinkedSellLot = NormalizeDouble(saldo * DynamicLotFactorLinkedSell, 2); } else { actualLotBuy = lotBuy; actualLotSell = lotSell; actualExtLot = ExtLot; actualLinkedSellLot = linkedSellLot; } // 5) DETECÇÃO DE FECHAMENTO MANUAL CheckManualOrderClosure(); // 6) LABELS PISCANTES E LOGO (Modo Turbo) UpdateLabel("AccountTypeLabel", (AccountInfoInteger(ACCOUNT_TRADE_MODE) == ACCOUNT_TRADE_MODE_DEMO ? "CONTA DEMO" : "CONTA REAL"), 1420, 20, blinkColors[blinkColorIndex], 10); UpdateLabel("LogoTradeLabel", "LoK_Trade", 1435, 50, blinkColors[blinkColorIndex], 10); UpdateLabel("FloatingProfitLabel", "Lucro: $" + DoubleToStr(GetSymbolProfit(),2), 1530, 610, blinkColors[fpBlinkColorIndex], 8); // 7) FECHAMENTO DE ORDENS SELL POR PONTOS if(OrdersReadyForClosure()) CloseSellOrdersByProfitPoints(); // 8) CONDIÇÃO APÓS PROTEÇÃO CONTRÁRIA if(protectionTriggered && GetSymbolProfit() >= ProfitCloseAfterProtectionThreshold) { CloseAllOrders(); protectionTriggered = false; stopTrading = false; Print("Proteção completada e flags resetadas."); } // 9) CHECAGEM DE SPREAD double ask = MarketInfo(Symbol(), MODE_ASK); double bid = MarketInfo(Symbol(), MODE_BID); double currentSpread = (ask - bid) / pointAdjusted; if(currentSpread > MaxAllowedSpread) { if(!highSpreadAlerted) { Alert("Spread elevado: ", DoubleToStr(currentSpread,1), " pts. Operações pausadas."); highSpreadAlerted = true; } return; } else highSpreadAlerted = false; // 10) ATUALIZAÇÃO DE DADOS E SL COMPRA UpdateDrawdown(); CheckClosedBuyBySL(); // 11) PROTEÇÃO CONTRÁRIA → TAKE COMBINADO if(protectionTriggered && GetSymbolProfit() >= ProfitCloseAfterProtectionThreshold) { CloseAllOrders(); RestartEA(); return; } //---------------------------------------------------------------------- // 12) ESTRATÉGIA ROBO1 (RSI + MOVIMENTO + FILTRO TF SUPERIOR) //---------------------------------------------------------------------- if(EnableRobo1) { // --- LIMITADOR DE ORDENS ROBO1 --- int ordersRobo1 = 0; for(int i = OrdersTotal()-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber) { ordersRobo1++; } } } if(ordersRobo1 >= MaxOrdersRobo1) { Print(" Robo1 limite de ordens atingido: ", ordersRobo1, " ordens abertas."); return; } int buyOrders = 0, sellOrders = 0; CountBuySellOrders(buyOrders, sellOrders); int totalOrdersRobo1 = buyOrders + sellOrders; if(totalOrdersRobo1 >= MaxOrdersRobo1) { // Limite atingido, bloqueia novas ordens Print(" Robo1 limite de ordens atingido: ", totalOrdersRobo1, " ordens abertas."); return; } // --- FIM DO BLOQUEIO DE ORDENS --- // calcula RSI e preço médio double rsi = iRSI(Symbol(), 0, rsiPeriod, PRICE_CLOSE, 0); double midPrice = (ask + bid) / 2; if(basePrice == 0) basePrice = midPrice; // condições de movimento, RSI, intervalo mínimo e distância entre ordens if(MathAbs(midPrice - basePrice) >= movementThreshold_runtime * pointAdjusted && rsi < rsiBuyLevel && TimeLocal() - lastTradeTime >= 30 && IsFarEnoughFromLastOrder(ask, OP_BUY) && IsFarEnoughFromLastOrder(bid, OP_SELL)) { bool executed = false; // --- compra --- if(!EnableHigherTFFilter || IsHigherTFTrendAligned(true)) { if(UseAdaptiveMLSignal(true)) // ✅ IA adaptativa validando compra { int buyTicket = SafeOrderSend( Symbol(), OP_BUY, actualLotBuy, ask, slippage, 0, 0, "Compra EA Lok", magicNumber, 0, clrBlue ); if(buyTicket > 0) executed = true; } } // --- venda --- if(!EnableHigherTFFilter || IsHigherTFTrendAligned(false)) { if(UseAdaptiveMLSignal(false)) // ✅ IA adaptativa validando venda { int sellTicket = SafeOrderSend( Symbol(), OP_SELL, actualLotSell, bid, slippage, 0, 0, "Venda EA Lok", magicNumber, 0, clrRed ); if(sellTicket > 0) executed = true; } } // atualiza basePrice e timestamp se executou ao menos uma ordem if(executed) { basePrice = midPrice; lastTradeTime = TimeLocal(); } } // fecha tudo se atingir o alvo de lucro if(GetTotalProfit() >= profitTarget) CloseAllOrders(); } // 13) ESTRATÉGIA ROBO2 (MARTINGALE BÁSICO) if(EnableRobo2) ExecuteRobo2(); // 14) MARTINGALE NEGATIVO (só sem proteção ativa) if(EnableNegativeMartingale && !protectionTriggered) { CheckAndTriggerMartingaleOrders(); } // 15) FECHAMENTO GERAL E FINAL if(OrdersReadyForClosure()) AttemptCloseOrders(); if(OrdersReadyForClosure()) CloseSellOrdersByProfitPoints(); // 16) ESTRATÉGIA ROBO4 (SimultBuySell‑Grid) if(EnableRobo4) { CheckManualClosureRobo4(); // detecta fechamento manual e reseta flag ExecuteRobo4(); } } //+------------------------------------------------------------------+ //| Fecha todas as ordens abertas do símbolo informado | //+------------------------------------------------------------------+ void CloseAllOrdersOfSymbol(string sym) { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == sym) { double price = (OrderType() == OP_BUY) ? Bid : Ask; SafeOrderClose(OrderTicket(), OrderLots(), price, slippage, clrRed); } } } //+------------------------------------------------------------------+ //| Função de desinicialização do EA | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // remove todos os labels e bitmaps que o EA criou ObjectsDeleteAll(0, OBJ_LABEL); ObjectsDeleteAll(0, OBJ_BITMAP_LABEL); } //+------------------------------------------------------------------+ //| Captura clique em objetos do gráfico | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK && sparam == FechaTudoBtnName) CloseAllOrdersOfSymbol(Symbol()); } //══════════════════════════════════════════════════════════════════ // Deep Reinforcement Learning Adaptativo para lote e movimento //══════════════════════════════════════════════════════════════════ void UpdateDRL() { if(!EnableDRLModule) return; double currentProfit = GetSymbolProfit(); // Inicialização segura da memória na primeira chamada if(!initialized) { for(int i = 0; i < ArraySize(avgProfitMemory); i++) avgProfitMemory[i] = currentProfit; initialized = true; } // Salva a experiência atual na memória avgProfitMemory[memoryIndex] = currentProfit; memoryIndex++; if(memoryIndex >= DRLMemorySize) memoryIndex = 0; // Calcula a média apenas dos dados preenchidos double avgProfit = 0; int validEntries = 0; for(int i = 0; i < DRLMemorySize; i++) { if(avgProfitMemory[i] != 0.0 || initialized) { avgProfit += avgProfitMemory[i]; validEntries++; } } if(validEntries > 0) avgProfit /= validEntries; else avgProfit = currentProfit; // fallback de segurança double adjustFactor = DRLLearningRate; if(DRLMode == DRLMode_Aggressive) adjustFactor *= 2.0; else if(DRLMode == DRLMode_Conservative) adjustFactor *= 0.5; // Se média de lucro for positiva, recompensa (aumenta o risco) if(avgProfit >= DRLProfitRewardThreshold) { movementThreshold_runtime = MathMax(DRL_MinMovementThreshold, movementThreshold_runtime - adjustFactor * 50.0); lotBuy_runtime = MathMin(DRL_MaxLotSize, lotBuy_runtime + adjustFactor * 0.01); lotSell_runtime = MathMin(DRL_MaxLotSize, lotSell_runtime + adjustFactor * 0.01); } // Se média for negativa, pune (reduz risco) else if(avgProfit <= DRLLossPunishThreshold) { movementThreshold_runtime = MathMin(DRL_MaxMovementThreshold, movementThreshold_runtime + adjustFactor * 50.0); lotBuy_runtime = MathMax(DRL_MinLotSize, lotBuy_runtime - adjustFactor * 0.01); lotSell_runtime = MathMax(DRL_MinLotSize, lotSell_runtime - adjustFactor * 0.01); } //══════════════════════════════════════════════════════════════════ // Proteções extras: impedir valores absurdos, NaN, infinito ou anormais //══════════════════════════════════════════════════════════════════ // Proteção para movimentoThreshold_runtime movementThreshold_runtime = MathMax(DRL_MinMovementThreshold, MathMin(movementThreshold_runtime, DRL_MaxMovementThreshold)); if(movementThreshold_runtime <= 0 || movementThreshold_runtime > 99999 || !MathIsValidNumber(movementThreshold_runtime)) { Print("️ Valor anormal detectado no movimentoThreshold_runtime. Resetando para padrão."); movementThreshold_runtime = movementThreshold; } // Proteção para lotBuy_runtime lotBuy_runtime = MathMax(DRL_MinLotSize, MathMin(lotBuy_runtime, DRL_MaxLotSize)); if(lotBuy_runtime <= 0 || lotBuy_runtime > 100 || !MathIsValidNumber(lotBuy_runtime)) { Print(" Valor anormal detectado no lotBuy_runtime. Resetando para padrão."); lotBuy_runtime = lotBuy; } // Proteção para lotSell_runtime lotSell_runtime = MathMax(DRL_MinLotSize, MathMin(lotSell_runtime, DRL_MaxLotSize)); if(lotSell_runtime <= 0 || lotSell_runtime > 100 || !MathIsValidNumber(lotSell_runtime)) { Print(" Valor anormal detectado no lotSell_runtime. Resetando para padrão."); lotSell_runtime = lotSell; } } //══════════════════════════════════════════════════════════════════ // Função: ShowDRLPanel //══════════════════════════════════════════════════════════════════ void ShowDRLPanel() { // Nome do painel (objeto gráfico) no gráfico MT4 static string panelName = "DRL_Panel"; // Monta o conteúdo de texto que será exibido no painel string info = " DRL Monitor \n"; // Título do painel info += "LotBuy Runtime: " + DoubleToStr(lotBuy_runtime, 2) + "\n"; // Lote dinâmico de compra info += "LotSell Runtime: " + DoubleToStr(lotSell_runtime, 2) + "\n"; // Lote dinâmico de venda info += "Movement Threshold: " + DoubleToStr(movementThreshold_runtime, 0) + " pts\n"; // Threshold adaptado // Calcula a média de lucro atual das experiências armazenadas no DRL double avgProfit = 0; // Acumulador de lucro int validEntries = 0; // Contador de entradas válidas for(int i = 0; i < DRLMemorySize; i++) { if(avgProfitMemory[i] != 0.0) // Considera apenas valores preenchidos { avgProfit += avgProfitMemory[i]; validEntries++; } } // Se existirem entradas válidas, calcula a média if(validEntries > 0) avgProfit /= validEntries; // Adiciona a informação de Lucro Médio no painel info += "Lucro Médio DRL: $" + DoubleToStr(avgProfit, 2); // Verifica se o painel já existe no gráfico if(ObjectFind(0, panelName) < 0) { // Se não existir, cria o painel do tipo Label ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0); // Define propriedades visuais do painel ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Canto superior esquerdo ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 15); // Distância da borda esquerda ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 580); // Distância da borda superior ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE, 10); // Tamanho da fonte ObjectSetInteger(0, panelName, OBJPROP_COLOR, clrDeepSkyBlue); // Cor do texto ObjectSetString(0, panelName, OBJPROP_FONT, "Arial Bold"); // Fonte usada } // Atualiza o conteúdo do painel com as informações atuais ObjectSetString(0, panelName, OBJPROP_TEXT, info); }