417 lines
30 KiB
MQL5
417 lines
30 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| wing26Lib.mqh.mq5 |
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
#include <Trade\Trade.mqh>
|
|
#include <Trade\PositionInfo.mqh>
|
|
#include <Trade\AccountInfo.mqh>
|
|
#include <Trade\Trade.mqh>
|
|
#include <Trade\PositionInfo.mqh>
|
|
#include <Trade\AccountInfo.mqh>
|
|
#include <WinfutLib\WinFutEnvs.mqh>
|
|
#include <WinFutLib\WinFutConfig.mqh>
|
|
#include <WinFutLib\WinFutIndicadores.mqh>
|
|
#include <WinFutLib\WinFutLib.mqh>
|
|
#include <WinFutLib\WinFutHorarioBovespaLibs.mqh>
|
|
#include <WinFutLib\WinFutMsgLogs.mqh>
|
|
|
|
WinFutMsgLogs msgLogs;
|
|
|
|
CTrade setMagicNumber(CTrade &trade) {
|
|
msgLogs.WinFutMsgLogsSetMagicNumber();
|
|
msgLogs.WinFutMsgInfoTrace(MagicNumber);
|
|
trade.SetExpertMagicNumber(MagicNumber);
|
|
return trade;
|
|
}
|
|
|
|
int setMediaMovel() {
|
|
msgLogs.WinFutMsgLogsSetMediaMovel();
|
|
handleMA = iMA(
|
|
Simbolo,
|
|
PERIOD_M1,
|
|
PeriodoMedia,
|
|
0,
|
|
MODE_SMA,
|
|
PRICE_CLOSE
|
|
);
|
|
|
|
if(handleMA == INVALID_HANDLE) {
|
|
msgLogs.WinFutMsgInfoTrace("Erro ao criar handle da média móvel");
|
|
return INIT_FAILED;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void setLogsOnInit() {
|
|
msgLogs.WinFutMsgLogsSetLogsOnInit();
|
|
msgLogs.WinFutMsgInfoTrace(Simbolo + " Scalper Clear inicializado com sucesso!");
|
|
msgLogs.WinFutMsgInfoTrace("Lote padrão: " + LotePadrao);
|
|
msgLogs.WinFutMsgInfoTrace("Lote alta volatilidade: " + LoteAltaVolatilidade);
|
|
}
|
|
|
|
void setDeInit(int reason) {
|
|
msgLogs.WinFutMsgLogsSetDeInit();
|
|
msgLogs.WinFutMsgInfoTrace("Liberar handles");
|
|
if(handleMA != INVALID_HANDLE) IndicatorRelease(handleMA);
|
|
msgLogs.WinFutMsgInfoTrace(Simbolo + "Scalper Clear finalizado. Motivo: " + reason);
|
|
}
|
|
|
|
bool VerificarNovoDia() {
|
|
msgLogs.WinFutMsgLogsSetVerificarNovoDia();
|
|
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;
|
|
}
|
|
|
|
void InicializarDadosDia() {
|
|
msgLogs.WinFutMsgLogsSetInicializarDadosDia();
|
|
tradesHoje = 0;
|
|
tradesVencedoresHoje = 0;
|
|
tradesPerdedoresConsecutivos = 0;
|
|
saldoInicialDia = account.Balance();
|
|
perdaMaximaDia = saldoInicialDia * (RiscoMaximoDiario / 100.0);
|
|
tradingPermitido = true;
|
|
|
|
msgLogs.WinFutMsgInfoTrace("=== NOVO DIA DE TRADING ===");
|
|
msgLogs.WinFutMsgInfoTrace("Saldo inicial: R$ " + DoubleToString(saldoInicialDia, 2));
|
|
msgLogs.WinFutMsgInfoTrace("Perda máxima permitida: R$ " + DoubleToString(perdaMaximaDia, 2));
|
|
}
|
|
|
|
bool TradingPermitido() {
|
|
msgLogs.WinFutMsgLogsSetTradingPermitido();
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se trading foi desabilitado por gestão de risco");
|
|
if(!tradingPermitido) return false;
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar máximo de trades por dia");
|
|
if(tradesHoje >= MaxTradesPorDia) {
|
|
if(tradesHoje == MaxTradesPorDia) {
|
|
msgLogs.WinFutMsgInfoTrace("Máximo de trades por dia atingido: " + MaxTradesPorDia);
|
|
tradingPermitido = false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar máximo de operações perdedoras consecutivas");
|
|
if(tradesPerdedoresConsecutivos >= MaxOperacoesPerdedoras) {
|
|
msgLogs.WinFutMsgInfoTrace("Máximo de operações perdedoras consecutivas atingido: " + MaxOperacoesPerdedoras);
|
|
tradingPermitido = false;
|
|
return false;
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar perda máxima diária");
|
|
double perdaAtual = saldoInicialDia - account.Balance();
|
|
if(perdaAtual >= perdaMaximaDia) {
|
|
msgLogs.WinFutMsgInfoTrace("Perda máxima diária atingida: R$ " + DoubleToString(perdaAtual, 2));
|
|
tradingPermitido = false;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool HorarioTradingPermitido() {
|
|
msgLogs.WinFutMsgLogsSetHorarioTradingPermitido();
|
|
|
|
MqlDateTime dt;
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
int hora = dt.hour;
|
|
int minuto = dt.min;
|
|
int horaMinuto = hora * 100 + minuto;
|
|
|
|
if(dt.day_of_week == 0 || dt.day_of_week == 6) {
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se é dia útil (Segunda a Sexta)");
|
|
return false;
|
|
}
|
|
|
|
if(OperarAberturaBMF && horaMinuto >= 915 && horaMinuto < 1000) {
|
|
msgLogs.WinFutMsgInfoTrace("Horarios de trading (Brasília)");
|
|
msgLogs.WinFutMsgInfoTrace("09:15-10:00: Apos leilao abertura BMF");
|
|
return true;
|
|
}
|
|
|
|
if(OperarAberturaBovespa && horaMinuto >= 1015 && horaMinuto < 1030) {
|
|
msgLogs.WinFutMsgInfoTrace("10:15-10:30: Apos leilao abertura Bovespa");
|
|
return true;
|
|
}
|
|
|
|
if(OperarAberturaEUA && horaMinuto >= 1030 && horaMinuto < 1200) {
|
|
msgLogs.WinFutMsgInfoTrace("10:30-12:00: Abertura mercado americano");
|
|
return true;
|
|
}
|
|
|
|
if(OperarTarde && horaMinuto >= 1400 && horaMinuto < 1645) {
|
|
msgLogs.WinFutMsgInfoTrace("14:00-16:45: Período da tarde");
|
|
return true;
|
|
}
|
|
|
|
if(OperarFechamento && horaMinuto >= 1700 && horaMinuto < 1800) {
|
|
msgLogs.WinFutMsgInfoTrace("17:00-18:00: Após fechamento Bovespa");
|
|
return true;
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("fora do horario Bovespa");
|
|
return false;
|
|
}
|
|
|
|
bool HorarioAltaVolatilidade() {
|
|
msgLogs.WinFutMsgLogsSetHorarioAltaVolatilidade();
|
|
MqlDateTime dt;
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
|
|
int hora = dt.hour;
|
|
int minuto = dt.min;
|
|
int horaMinuto = hora * 100 + minuto;
|
|
|
|
if(horaMinuto >= 915 && horaMinuto < 1000) {
|
|
msgLogs.WinFutMsgInfoTrace("Horários de alta volatilidade");
|
|
msgLogs.WinFutMsgInfoTrace("09:15-10:00: Após leilão BMF");
|
|
return true;
|
|
}
|
|
|
|
if(horaMinuto >= 1000 && horaMinuto < 1100) {
|
|
msgLogs.WinFutMsgInfoTrace("10:00-11:00: Abertura Bovespa + EUA");
|
|
return true;
|
|
}
|
|
|
|
if(horaMinuto >= 1700 && horaMinuto < 1800) {
|
|
msgLogs.WinFutMsgInfoTrace("17:00-18:00: Fechamento");
|
|
return true;
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("fora do horario de alta volatilidade Bovespa");
|
|
return false;
|
|
}
|
|
|
|
bool TemPosicaoAberta() {
|
|
msgLogs.WinFutMsgLogsSetTemPosicaoAberta();
|
|
return position.SelectByMagic(Simbolo, MagicNumber);
|
|
}
|
|
|
|
void GerenciarPosicao() {
|
|
msgLogs.WinFutMsgLogsSetGerenciarPosicao();
|
|
if(!position.SelectByMagic(Simbolo, MagicNumber))
|
|
return;
|
|
|
|
//--- Aqui você pode adicionar lógica adicional de gestão
|
|
//--- Por exemplo, trailing stop, breakeven, etc.
|
|
}
|
|
|
|
void AnalisarEntrada() {
|
|
msgLogs.WinFutMsgLogsSetAnalisarEntrada();
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Obter dados das velas");
|
|
MqlRates velas[];
|
|
if(CopyRates(Simbolo, PERIOD_M1, 0, VelasAnalise + 1, velas) <= 0) return;
|
|
|
|
msgLogs.WinFutMsgInfoTrace("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);
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Análise de price action para entrada");
|
|
ENUM_ORDER_TYPE tipoOrdem = AnalisarPriceAction(velas, ma);
|
|
|
|
if(tipoOrdem == ORDER_TYPE_BUY || tipoOrdem == ORDER_TYPE_SELL) {
|
|
msgLogs.WinFutMsgInfoTrace("executando boleta de");
|
|
msgLogs.WinFutMsgInfoTrace(tipoOrdem);
|
|
ExecutarOrdem(tipoOrdem);
|
|
}
|
|
}
|
|
|
|
ENUM_ORDER_TYPE AnalisarPriceAction(const MqlRates &velas[], const double &ma[]) {
|
|
msgLogs.WinFutMsgLogsSetAnalisarPriceAction();
|
|
double precoAtual = SymbolInfoDouble(Simbolo, SYMBOL_BID);
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar movimento mínimo");
|
|
double range = velas[0].high - velas[0].low;
|
|
if(range < MinimoPontosMovimento * SymbolInfoDouble(Simbolo, SYMBOL_POINT))
|
|
return -1;
|
|
|
|
msgLogs.WinFutMsgInfoTrace("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];
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Padrão de compra: preço acima da média em tendência de alta");
|
|
if(acimaDaMedia && tendenciaAlta) {
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se vela anterior foi de baixa e atual é de alta");
|
|
if(velas[1].close < velas[1].open && velas[0].close > velas[0].open) {
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se não estamos em topo");
|
|
if(velas[0].high < velas[1].high)
|
|
|
|
msgLogs.WinFutMsgInfoTrace("ordem do tipo de compra");
|
|
return ORDER_TYPE_BUY;
|
|
}
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Padrão de venda: preço abaixo da média em tendência de baixa");
|
|
if(!acimaDaMedia && tendenciaBaixa) {
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se vela anterior foi de alta e atual é de baixa");
|
|
if(velas[1].close > velas[1].open && velas[0].close < velas[0].open) {
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se não estamos em fundo");
|
|
if(velas[0].low > velas[1].low)
|
|
msgLogs.WinFutMsgInfoTrace("ordem tipo de venda");
|
|
return ORDER_TYPE_SELL;
|
|
}
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Nenhum sinal");
|
|
return -1;
|
|
}
|
|
|
|
void ExecutarOrdem(ENUM_ORDER_TYPE tipo) {
|
|
msgLogs.WinFutMsgLogsSetExecutarOrdem();
|
|
msgLogs.WinFutMsgInfoTrace("Determinar lote baseado na volatilidade");
|
|
double lote = HorarioAltaVolatilidade() ? LoteAltaVolatilidade : LotePadrao;
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Determinar take profit baseado na volatilidade");
|
|
int tp = HorarioAltaVolatilidade() ? TakeProfitAltaVol : TakeProfitPadrao;
|
|
|
|
msgLogs.WinFutMsgInfoTrace("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;
|
|
|
|
msgLogs.WinFutMsgInfoTrace("setando TP/SL para tipo de venda");
|
|
precoTP = preco - (tp * ponto);
|
|
precoSL = preco + (sl * ponto);
|
|
|
|
if(tipo == ORDER_TYPE_BUY) {
|
|
msgLogs.WinFutMsgInfoTrace("setando TP/SL para tipo de compra");
|
|
precoTP = preco + (tp * ponto);
|
|
precoSL = preco - (sl * ponto);
|
|
}
|
|
|
|
bool resultado = false;
|
|
string scalper = "";
|
|
if(tipo == ORDER_TYPE_BUY) {
|
|
scalper = Simbolo + "Scalper - Compra";
|
|
msgLogs.WinFutMsgInfoTrace("executando boletada de compra" + scalper);
|
|
resultado = trade.Buy(
|
|
lote,
|
|
Simbolo,
|
|
preco,
|
|
precoSL,
|
|
precoTP,
|
|
scalper
|
|
);
|
|
|
|
} else {
|
|
scalper = Simbolo + "Scalper - Venda";
|
|
msgLogs.WinFutMsgInfoTrace("executando boletada de venda" + scalper);
|
|
resultado = trade.Sell(
|
|
lote,
|
|
Simbolo,
|
|
preco,
|
|
precoSL,
|
|
precoTP,
|
|
scalper
|
|
);
|
|
}
|
|
|
|
if(resultado) {
|
|
tradesHoje++;
|
|
msgLogs.WinFutMsgInfoTrace("=== NOVA OPERAÇÃO ===");
|
|
msgLogs.WinFutMsgInfoTrace("Tipo: " + scalper);
|
|
msgLogs.WinFutMsgInfoTrace("Lote: " + lote);
|
|
msgLogs.WinFutMsgInfoTrace("Preço: " + preco);
|
|
msgLogs.WinFutMsgInfoTrace("Take Profit: " + precoTP + " (" + tp + " pontos)");
|
|
msgLogs.WinFutMsgInfoTrace("Stop Loss: " + precoSL + " (" + sl + " pontos)");
|
|
msgLogs.WinFutMsgInfoTrace("Trade nº: " + tradesHoje);
|
|
} else {
|
|
msgLogs.WinFutMsgInfoTrace("Erro ao executar ordem: " + trade.ResultRetcode());
|
|
}
|
|
}
|
|
|
|
void setOnTickTrader() {
|
|
msgLogs.WinFutMsgLogsSetOnTickTrader();
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se é um novo dia");
|
|
if(VerificarNovoDia()) {
|
|
InicializarDadosDia();
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se trading está permitido");
|
|
if(!TradingPermitido()) return;
|
|
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar horário de trading");
|
|
if(!HorarioTradingPermitido()) return;
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se já temos posição aberta");
|
|
if(TemPosicaoAberta()) {
|
|
GerenciarPosicao();
|
|
return;
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Procurar oportunidades de entrada");
|
|
AnalisarEntrada();
|
|
}
|
|
|
|
void setOnTrade() {
|
|
msgLogs.WinFutMsgLogsSetOnTrade();
|
|
msgLogs.WinFutMsgInfoTrace("Verificar se a posição foi fechada");
|
|
|
|
if(!TemPosicaoAberta()) {
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Analisar resultado da última operação - Últimas 24 horas");
|
|
HistorySelect(TimeCurrent() - 86400, TimeCurrent());
|
|
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;
|
|
msgLogs.WinFutMsgInfoTrace("=== TRADE VENCEDOR ===");
|
|
msgLogs.WinFutMsgInfoTrace("Lucro: R$ " + DoubleToString(lucro, 2));
|
|
} else {
|
|
tradesPerdedoresConsecutivos++;
|
|
msgLogs.WinFutMsgInfoTrace("=== TRADE PERDEDOR ===");
|
|
msgLogs.WinFutMsgInfoTrace("Perda: R$ " + DoubleToString(lucro, 2));
|
|
msgLogs.WinFutMsgInfoTrace("Perdedores consecutivos: " + tradesPerdedoresConsecutivos);
|
|
}
|
|
|
|
msgLogs.WinFutMsgInfoTrace("Estatísticas do dia");
|
|
double winRate = (tradesHoje > 0) ? (tradesVencedoresHoje * 100.0 / tradesHoje) : 0;
|
|
msgLogs.WinFutMsgInfoTrace("=== ESTATÍSTICAS DO DIA ===");
|
|
msgLogs.WinFutMsgInfoTrace("Trades realizados: " + tradesHoje);
|
|
msgLogs.WinFutMsgInfoTrace("Trades vencedores: " + tradesVencedoresHoje);
|
|
msgLogs.WinFutMsgInfoTrace("Win Rate: " + DoubleToString(winRate, 2));
|
|
msgLogs.WinFutMsgInfoTrace("Saldo atual: R$ " + DoubleToString(account.Balance(), 2));
|
|
}
|
|
}
|
|
}
|
|
}
|