//+------------------------------------------------------------------+ //| WinQ25_Scalper_Clear.mq5 | //| Copyright 2024, Scalper Trading Bot | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Scalper Trading Bot" #property link "https://www.mql5.com" #property version "1.00" #property description "Robô Scalper para WinQ25 - Mercado Brasileiro BMF" //--- Includes #include #include #include //--- Objetos globais CTrade trade; CPositionInfo position; CAccountInfo account; //+------------------------------------------------------------------+ //| Parâmetros de entrada | //+------------------------------------------------------------------+ input group "=== CONFIGURAÇÕES BÁSICAS ===" input double LotePadrao = 1.0; // Lote padrão para baixa volatilidade input double LoteAltaVolatilidade = 5.0; // Lote para alta volatilidade input int TakeProfitPadrao = 50; // Take Profit padrão (pontos) input int TakeProfitAltaVol = 200; // Take Profit alta volatilidade (pontos) input int MagicNumber = 123456; // Número mágico input string Simbolo = "WINQ25"; // Símbolo a ser operado input group "=== GESTÃO DE RISCO ===" input double RiscoMaximoDiario = 2.0; // Risco máximo diário (% do saldo) input int MaxOperacoesPerdedoras = 3; // Máximo de operações perdedoras consecutivas input int MaxTradesPorDia = 20; // Máximo de trades por dia input double StopLossPercentual = 50.0; // Stop Loss como % do Take Profit input group "=== PRICE ACTION ===" input int PeriodoMedia = 20; // Período da média móvel input double MinimoPontosMovimento = 30.0; // Mínimo de pontos para considerar movimento input int VelasAnalise = 3; // Número de velas para análise input group "=== HORÁRIOS DE TRADING ===" input bool OperarAberturaBMF = true; // Operar abertura BMF (09:15-10:00) input bool OperarAberturaBovespa = true; // Operar abertura Bovespa (10:15-10:30) input bool OperarAberturaEUA = true; // Operar abertura EUA (10:30-12:00) input bool OperarTarde = true; // Operar período tarde (14:00-16:45) input bool OperarFechamento = true; // Operar fechamento (17:00-18:00) //+------------------------------------------------------------------+ //| Variáveis globais | //+------------------------------------------------------------------+ datetime ultimoDia = 0; int tradesHoje = 0; int tradesVencedoresHoje = 0; int tradesPerdedoresConsecutivos = 0; double saldoInicialDia = 0; double perdaMaximaDia = 0; bool tradingPermitido = true; //--- Handles dos indicadores int handleMA; //+------------------------------------------------------------------+ //| Função de inicialização | //+------------------------------------------------------------------+ int OnInit() { //--- Configurar Magic Number trade.SetExpertMagicNumber(MagicNumber); //--- Criar handle da média móvel handleMA = iMA(Simbolo, PERIOD_M1, PeriodoMedia, 0, MODE_SMA, PRICE_CLOSE); if(handleMA == INVALID_HANDLE) { Print("Erro ao criar handle da média móvel"); return INIT_FAILED; } //--- Inicializar dados do dia InicializarDadosDia(); Print("WinQ25 Scalper Clear inicializado com sucesso!"); Print("Símbolo: ", Simbolo); Print("Lote padrão: ", LotePadrao); Print("Lote alta volatilidade: ", LoteAltaVolatilidade); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Função de desinicialização | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Liberar handles if(handleMA != INVALID_HANDLE) IndicatorRelease(handleMA); Print("WinQ25 Scalper Clear finalizado. Motivo: ", reason); } //+------------------------------------------------------------------+ //| Função principal - executada a cada tick | //+------------------------------------------------------------------+ void OnTick() { //--- Verificar se é um novo dia if(VerificarNovoDia()) { InicializarDadosDia(); } //--- Verificar se trading está permitido if(!TradingPermitido()) return; //--- Verificar horário de trading if(!HorarioTradingPermitido()) return; //--- Verificar se já temos posição aberta if(TemPosicaoAberta()) { GerenciarPosicao(); return; } //--- Procurar oportunidades de entrada AnalisarEntrada(); } //+------------------------------------------------------------------+ //| Verificar se é um novo dia | //+------------------------------------------------------------------+ bool VerificarNovoDia() { datetime agora = TimeCurrent(); MqlDateTime dt; TimeToStruct(agora, dt); datetime diaAtual = StringToTime(StringFormat("%04d.%02d.%02d 00:00", dt.year, dt.mon, dt.day)); if(ultimoDia != diaAtual) { ultimoDia = diaAtual; return true; } return false; } //+------------------------------------------------------------------+ //| Inicializar dados do dia | //+------------------------------------------------------------------+ void InicializarDadosDia() { tradesHoje = 0; tradesVencedoresHoje = 0; tradesPerdedoresConsecutivos = 0; saldoInicialDia = account.Balance(); perdaMaximaDia = saldoInicialDia * (RiscoMaximoDiario / 100.0); tradingPermitido = true; Print("=== NOVO DIA DE TRADING ==="); Print("Saldo inicial: R$ ", DoubleToString(saldoInicialDia, 2)); Print("Perda máxima permitida: R$ ", DoubleToString(perdaMaximaDia, 2)); } //+------------------------------------------------------------------+ //| Verificar se trading está permitido | //+------------------------------------------------------------------+ bool TradingPermitido() { //--- Verificar se trading foi desabilitado por gestão de risco if(!tradingPermitido) return false; //--- Verificar máximo de trades por dia if(tradesHoje >= MaxTradesPorDia) { if(tradesHoje == MaxTradesPorDia) { Print("Máximo de trades por dia atingido: ", MaxTradesPorDia); tradingPermitido = false; } return false; } //--- Verificar máximo de operações perdedoras consecutivas if(tradesPerdedoresConsecutivos >= MaxOperacoesPerdedoras) { Print("Máximo de operações perdedoras consecutivas atingido: ", MaxOperacoesPerdedoras); tradingPermitido = false; return false; } //--- Verificar perda máxima diária double perdaAtual = saldoInicialDia - account.Balance(); if(perdaAtual >= perdaMaximaDia) { Print("Perda máxima diária atingida: R$ ", DoubleToString(perdaAtual, 2)); tradingPermitido = false; return false; } return true; } //+------------------------------------------------------------------+ //| Verificar horário de trading permitido | //+------------------------------------------------------------------+ bool HorarioTradingPermitido() { MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); //--- Verificar se é dia útil (Segunda a Sexta) if(dt.day_of_week == 0 || dt.day_of_week == 6) return false; int hora = dt.hour; int minuto = dt.min; int horaMinuto = hora * 100 + minuto; //--- Horários de trading (Brasília) // 09:15-10:00: Após leilão abertura BMF if(OperarAberturaBMF && horaMinuto >= 915 && horaMinuto < 1000) return true; // 10:15-10:30: Após leilão abertura Bovespa if(OperarAberturaBovespa && horaMinuto >= 1015 && horaMinuto < 1030) return true; // 10:30-12:00: Abertura mercado americano if(OperarAberturaEUA && horaMinuto >= 1030 && horaMinuto < 1200) return true; // 14:00-16:45: Período da tarde if(OperarTarde && horaMinuto >= 1400 && horaMinuto < 1645) return true; // 17:00-18:00: Após fechamento Bovespa if(OperarFechamento && horaMinuto >= 1700 && horaMinuto < 1800) return true; return false; } //+------------------------------------------------------------------+ //| Verificar se é horário de alta volatilidade | //+------------------------------------------------------------------+ bool HorarioAltaVolatilidade() { MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); int hora = dt.hour; int minuto = dt.min; int horaMinuto = hora * 100 + minuto; //--- Horários de alta volatilidade // 09:15-10:00: Após leilão BMF if(horaMinuto >= 915 && horaMinuto < 1000) return true; // 10:00-11:00: Abertura Bovespa + EUA if(horaMinuto >= 1000 && horaMinuto < 1100) return true; // 17:00-18:00: Fechamento if(horaMinuto >= 1700 && horaMinuto < 1800) return true; return false; } //+------------------------------------------------------------------+ //| Verificar se tem posição aberta | //+------------------------------------------------------------------+ bool TemPosicaoAberta() { return position.SelectByMagic(Simbolo, MagicNumber); } //+------------------------------------------------------------------+ //| Gerenciar posição aberta | //+------------------------------------------------------------------+ void GerenciarPosicao() { if(!position.SelectByMagic(Simbolo, MagicNumber)) return; //--- Aqui você pode adicionar lógica adicional de gestão //--- Por exemplo, trailing stop, breakeven, etc. } //+------------------------------------------------------------------+ //| Analisar entrada baseada em price action | //+------------------------------------------------------------------+ void AnalisarEntrada() { //--- Obter dados das velas MqlRates velas[]; if(CopyRates(Simbolo, PERIOD_M1, 0, VelasAnalise + 1, velas) <= 0) return; //--- Obter dados da média móvel double ma[]; if(CopyBuffer(handleMA, 0, 0, VelasAnalise + 1, ma) <= 0) return; ArraySetAsSeries(velas, true); ArraySetAsSeries(ma, true); //--- Análise de price action para entrada ENUM_ORDER_TYPE tipoOrdem = AnalisarPriceAction(velas, ma); if(tipoOrdem == ORDER_TYPE_BUY || tipoOrdem == ORDER_TYPE_SELL) { ExecutarOrdem(tipoOrdem); } } //+------------------------------------------------------------------+ //| Analisar price action para determinar direção | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE AnalisarPriceAction(const MqlRates &velas[], const double &ma[]) { double precoAtual = SymbolInfoDouble(Simbolo, SYMBOL_BID); //--- Verificar movimento mínimo double range = velas[0].high - velas[0].low; if(range < MinimoPontosMovimento * SymbolInfoDouble(Simbolo, SYMBOL_POINT)) return -1; //--- Análise baseada na média móvel e price action bool acimaDaMedia = precoAtual > ma[0]; bool tendenciaAlta = ma[0] > ma[1] && ma[1] > ma[2]; bool tendenciaBaixa = ma[0] < ma[1] && ma[1] < ma[2]; //--- Padrão de compra: preço acima da média em tendência de alta if(acimaDaMedia && tendenciaAlta) { //--- Verificar se vela anterior foi de baixa e atual é de alta if(velas[1].close < velas[1].open && velas[0].close > velas[0].open) { //--- Verificar se não estamos em topo if(velas[0].high < velas[1].high) return ORDER_TYPE_BUY; } } //--- Padrão de venda: preço abaixo da média em tendência de baixa if(!acimaDaMedia && tendenciaBaixa) { //--- Verificar se vela anterior foi de alta e atual é de baixa if(velas[1].close > velas[1].open && velas[0].close < velas[0].open) { //--- Verificar se não estamos em fundo if(velas[0].low > velas[1].low) return ORDER_TYPE_SELL; } } return -1; // Nenhum sinal } //+------------------------------------------------------------------+ //| Executar ordem | //+------------------------------------------------------------------+ void ExecutarOrdem(ENUM_ORDER_TYPE tipo) { //--- Determinar lote baseado na volatilidade double lote = HorarioAltaVolatilidade() ? LoteAltaVolatilidade : LotePadrao; //--- Determinar take profit baseado na volatilidade int tp = HorarioAltaVolatilidade() ? TakeProfitAltaVol : TakeProfitPadrao; //--- Calcular stop loss (50% do take profit) int sl = (int)(tp * (StopLossPercentual / 100.0)); double preco = (tipo == ORDER_TYPE_BUY) ? SymbolInfoDouble(Simbolo, SYMBOL_ASK) : SymbolInfoDouble(Simbolo, SYMBOL_BID); double ponto = SymbolInfoDouble(Simbolo, SYMBOL_POINT); double precoTP, precoSL; if(tipo == ORDER_TYPE_BUY) { precoTP = preco + (tp * ponto); precoSL = preco - (sl * ponto); } else { precoTP = preco - (tp * ponto); precoSL = preco + (sl * ponto); } //--- Executar ordem bool resultado = false; if(tipo == ORDER_TYPE_BUY) { resultado = trade.Buy(lote, Simbolo, preco, precoSL, precoTP, "WinQ25 Scalper - Compra"); } else { resultado = trade.Sell(lote, Simbolo, preco, precoSL, precoTP, "WinQ25 Scalper - Venda"); } if(resultado) { tradesHoje++; Print("=== NOVA OPERAÇÃO ==="); Print("Tipo: ", (tipo == ORDER_TYPE_BUY) ? "COMPRA" : "VENDA"); Print("Lote: ", lote); Print("Preço: ", preco); Print("Take Profit: ", precoTP, " (", tp, " pontos)"); Print("Stop Loss: ", precoSL, " (", sl, " pontos)"); Print("Trade nº: ", tradesHoje); } else { Print("Erro ao executar ordem: ", trade.ResultRetcode()); } } //+------------------------------------------------------------------+ //| Função chamada quando há negociação | //+------------------------------------------------------------------+ void OnTrade() { //--- Verificar se a posição foi fechada if(!TemPosicaoAberta()) { //--- Analisar resultado da última operação HistorySelect(TimeCurrent() - 86400, TimeCurrent()); // Últimas 24 horas int totalDeals = HistoryDealsTotal(); if(totalDeals > 0) { ulong ticket = HistoryDealGetTicket(totalDeals - 1); if(HistoryDealGetInteger(ticket, DEAL_MAGIC) == MagicNumber) { double lucro = HistoryDealGetDouble(ticket, DEAL_PROFIT); if(lucro > 0) { tradesVencedoresHoje++; tradesPerdedoresConsecutivos = 0; Print("=== TRADE VENCEDOR ==="); Print("Lucro: R$ ", DoubleToString(lucro, 2)); } else { tradesPerdedoresConsecutivos++; Print("=== TRADE PERDEDOR ==="); Print("Perda: R$ ", DoubleToString(lucro, 2)); Print("Perdedores consecutivos: ", tradesPerdedoresConsecutivos); } //--- Estatísticas do dia double winRate = (tradesHoje > 0) ? (tradesVencedoresHoje * 100.0 / tradesHoje) : 0; Print("=== ESTATÍSTICAS DO DIA ==="); Print("Trades realizados: ", tradesHoje); Print("Trades vencedores: ", tradesVencedoresHoje); Print("Win Rate: ", DoubleToString(winRate, 2), "%"); Print("Saldo atual: R$ ", DoubleToString(account.Balance(), 2)); } } } }