2800 lines
207 KiB
MQL4
2800 lines
207 KiB
MQL4
//+------------------------------------------------------------------+
|
||
//| 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);
|
||
}
|