//+------------------------------------------------------------------+
//| 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);
}