Lok_EA/FUSION LOK TRADE.mq4

2800 lines
207 KiB
MQL4
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//+------------------------------------------------------------------+
//| 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 <WinUser32.mqh>
#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
return UsePriceAboveMA ? (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<cnt; k++)
if(OrderSelect(tickets[k],SELECT_BY_TICKET,MODE_TRADES))
{
double price=(OrderType()==OP_BUY?Bid:Ask);
Robo4_SafeOrderClose(tickets[k],OrderLots(),
NormalizeDouble(price,Digits),
Robo4_Slippage,clrMagenta);
Sleep(50);
}
Robo4_ordersOpened=false;
Robo4_gridRepeats=0;
}
}
//+------------------------------------------------------------------+
//| Grid Martingale |
//+------------------------------------------------------------------+
void Robo4_CheckGridReopen()
{
if(Robo4_gridRepeats>=Robo4_MaxGridRepeats) return;
if(TimeCurrent()-Robo4_lastGridTime<Robo4_GridReopenDelaySeconds) return;
RefreshRates();
// BUY
if(Robo4_lastBuyPrice>0)
{
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);
}