Spécifications
Olá, bom dia.
Segue abaixo um estratégia que precisa de correção, os pontos principais estão inseridos na estratégia, falta correções para seguir as regras e os parametros;
Regras:
TP: 10 e SL: 10 ( Simulação Inicial de COMPRA )
Ex1: Compra com lote de 1, se o preço subir 10 pips vai atingir o TP e encerrar essa operação, após o TP vai ser aberta uma ordem oposta (VENDA) com lote de 0,5 e TP:20 e SL:10, seguindo o simulação - se o preço continuar subindo encerra a estratégia, porém se o preço cair e atingir o TP de 20 pips essa ordem é encerrada, porém automaticamente abre uma ordem oposta (Compra) nesse caso e formando um Canal que vai se repetir indefinidamente, o lote agora é a metade do anterior 0,25 e SL: 10 e TP:20 e repetir até o SL em um dos lados do Canal;
Ex2: Compra com lote de 1, se o preço cair 10 pips vai atingir o SL e encerrar essa operação, após o SL da Compra vai ser aberta uma ordem oposta de (VENDA) com lote de 0,50 com TP:10 e SL:30 , se o preço continuar caindo e atingir o TP a operação é encerrada, porém se o preço começar a subir , assim que o preço atingir -10 pips é aberta uma nova ordem de COMPRA (Assim temos 2 ordens que estão abertas em direções diferentes) essa nova ordem tem SL:20 e TP:20 com lote de 1 (O lote Dobrou) e caso o preço continue subindo vai até atingir o SL da primeira ordem e o TP da segunda ordem, porém se o preço voltar a cair -10 pips, novamente é aberta uma nova ordem de VENDA (É criado assim um Canal que vai se repetir indefinidamente até atingir o TP e o SL das ordens abertas) essa nova ordem tem lote de 2 (dobro da anterior) e TP:10 e SL:30 e seguindo a estratégia de o preço continuar caindo vai encerrar todas operações, caso volte a subir continuamos com o canal.
Os Erros estão no Inicio da primeira operação onde esta parametrizado SL de 10 pips e TP de 10 pips e no MT5 aparece sempre 100 Pips de TP e SL;
O Segundo erro esta na execução das ordens com SL, TP e Lotes para formação dos Canais ( Iniciando com Compra e Iniciando Venda também )
* Correção dos SL e TP e Correção da Formação dos Canais ( Compra e Venda )
//+------------------------------------------------------------------+
//| HedgeChannelEA |
//| Copyright 2023, NeoFlow Labs |
//| https://www.neoflowlab.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, NeoFlow Labs"
#property version "1.11" // Versão atualizada: separação dos cálculos para ordens de mercado e de canal
#property strict
// Declarações antecipadas
void RefreshRates(); // Em MQL5 os preços já são atualizados automaticamente
void HandleSLSequence(bool closedByTP);
void OpenOrderWithFixedPrice(ENUM_ORDER_TYPE newType, double lot, int tpPips, int slPips, double basePrice);
// Parâmetros de entrada
input double InitialLot = 1.0; // Lote inicial
input int TP_Pips = 10; // Take Profit (pips) para a ordem inicial
input int SL_Pips = 10; // Stop Loss (pips) para a ordem inicial
input double MaxDailyProfit = 500.0; // Lucro máximo diário ($)
input double MaxDailyLoss = -300.0; // Perda máxima diária ($)
input string StartTime = "01:05"; // Horário inicial de negociação
input string EndTime = "22:30"; // Horário final de negociação
// Variáveis globais
double DailyPL = 0;
bool TradingActive = true;
datetime lastCheckDay = 0;
datetime g_lastCandleTime = 0;
enum SequenceType { NONE, TP_SEQUENCE, SL_SEQUENCE };
SequenceType currentSequence = NONE;
bool waitingForReversal = false;
double hedgeOpenPrice = 0; // Preço base congelado para as ordens de canal
double lastTradeLot = InitialLot;
ENUM_POSITION_TYPE lastTradeType = POSITION_TYPE_BUY;
//+------------------------------------------------------------------+
//| Stub para RefreshRates() |
//+------------------------------------------------------------------+
void RefreshRates()
{
// Em MQL5 os preços são atualizados automaticamente.
}
//+------------------------------------------------------------------+
//| Obtém o valor de 1 pip de forma robusta |
//| (Força a interpretação correta para símbolos como EURUSD, mesmo |
//| quando o nome do símbolo contém sufixos) |
//+------------------------------------------------------------------+
double GetPipValue()
{
// Se o símbolo contiver "EURUSD", forçamos 1 pip = 0.0001
string symbol = Symbol();
if(StringFind(symbol, "EURUSD") >= 0)
return 0.0001;
// Caso geral: utiliza SYMBOL_DIGITS para determinar o valor do pip
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
if(digits == 5 || digits == 4)
return 0.0001;
if(digits == 3)
return 0.001;
if(digits == 2)
return 0.01;
return SymbolInfoDouble(symbol, SYMBOL_POINT);
}
//+------------------------------------------------------------------+
//| Função de inicialização |
//+------------------------------------------------------------------+
int OnInit()
{
EventSetTimer(60);
g_lastCandleTime = iTime(Symbol(), PERIOD_CURRENT, 0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Função de desinicialização |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Função principal |
//+------------------------------------------------------------------+
void OnTick()
{
if(!TradingActive || CheckDailyLimits())
return;
datetime currentCandleTime = iTime(Symbol(), PERIOD_CURRENT, 0);
if(currentCandleTime != g_lastCandleTime)
{
g_lastCandleTime = currentCandleTime;
if(PositionsTotal() == 0 && IsTradingTime())
OpenInitialTrade();
}
if(PositionsTotal() > 0)
ManageOpenPositions();
}
//+------------------------------------------------------------------+
//| Verifica limites diários |
//+------------------------------------------------------------------+
bool CheckDailyLimits()
{
MqlDateTime currentDate;
datetime current_time = TimeCurrent();
TimeToStruct(current_time, currentDate);
currentDate.hour = 0;
currentDate.min = 0;
currentDate.sec = 0;
datetime currentDay = StructToTime(currentDate);
if(currentDay > lastCheckDay)
{
DailyPL = 0;
lastCheckDay = currentDay;
}
if(DailyPL >= MaxDailyProfit || DailyPL <= MaxDailyLoss)
{
TradingActive = false;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Verifica se está dentro do horário de negociação |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
datetime now = TimeCurrent();
datetime start = StringToTime(StartTime);
datetime end = StringToTime(EndTime);
if(start < end)
return (now >= start && now <= end);
else
return (now >= start || now <= end);
}
//+------------------------------------------------------------------+
//| Fecha uma posição |
//+------------------------------------------------------------------+
void ClosePosition(ulong ticket)
{
if(!PositionSelectByTicket(ticket))
{
Print("Posição ", ticket, " não existe para fechamento.");
return;
}
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.position = ticket;
request.symbol = Symbol();
request.volume = PositionGetDouble(POSITION_VOLUME);
request.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
request.price = (request.type == ORDER_TYPE_SELL) ? SymbolInfoDouble(Symbol(), SYMBOL_BID) : SymbolInfoDouble(Symbol(), SYMBOL_ASK);
request.deviation= 3;
request.type_filling = ORDER_FILLING_FOK;
if(OrderSend(request, result))
Print("Posição fechada: Ticket ", ticket);
else
Print("Falha ao fechar posição. Erro: ", GetLastError());
}
//+------------------------------------------------------------------+
//| Abre a ordem inicial (market order) |
//+------------------------------------------------------------------+
void OpenInitialTrade()
{
// Envia ordem de COMPRA com TP e SL de 10 pips utilizando os preços atuais
OpenOrder(ORDER_TYPE_BUY, InitialLot, TP_Pips, SL_Pips);
currentSequence = NONE;
}
//+------------------------------------------------------------------+
//| Função para abrir ordem (market order) com preços atualizados |
//+------------------------------------------------------------------+
void OpenOrder(ENUM_ORDER_TYPE newType, double lot, int tpPips, int slPips)
{
RefreshRates();
MqlTradeRequest request = {};
MqlTradeResult result = {};
double price, sl, tp;
double pip = GetPipValue();
if(newType == ORDER_TYPE_SELL)
{
price = SymbolInfoDouble(Symbol(), SYMBOL_BID);
sl = price + slPips * pip;
tp = price - tpPips * pip;
}
else
{
price = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
sl = price - slPips * pip;
tp = price + tpPips * pip;
}
tp = NormalizeDouble(tp, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
sl = NormalizeDouble(sl, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
request.action = TRADE_ACTION_DEAL;
request.symbol = Symbol();
request.volume = lot;
request.type = newType;
request.price = price;
request.sl = sl;
request.tp = tp;
request.deviation = 3;
request.type_filling = ORDER_FILLING_FOK;
if(OrderSend(request, result))
{
Print("Ordem inicial aberta: ", EnumToString(newType), " Lote: ", lot,
" TP: ", tpPips, " pips, SL: ", slPips, " pips, Preço: ", price);
hedgeOpenPrice = price; // Armazena o preço base para as ordens de canal
}
else
Print("Erro na ordem inicial: ", GetLastError());
}
//+------------------------------------------------------------------+
//| Função para abrir ordens de canal (pending orders) usando preço fixo |
//| Os cálculos de SL/TP para ordens de canal utilizam os parâmetros |
//| definidos para o canal, sem interferir na ordem inicial |
//+------------------------------------------------------------------+
void OpenOrderWithFixedPrice(ENUM_ORDER_TYPE newType, double lot, int tpPips, int slPips, double basePrice)
{
RefreshRates();
MqlTradeRequest request = {};
MqlTradeResult result = {};
double pip = GetPipValue();
double sl, tp;
// Utiliza o preço base congelado (hedgeOpenPrice) sem ajuste com a cotação atual
if(newType == ORDER_TYPE_SELL)
{
// Ordem pendente de venda: SELL STOP
sl = basePrice + slPips * pip;
tp = basePrice - tpPips * pip;
request.type = ORDER_TYPE_SELL_STOP;
}
else
{
// Ordem pendente de compra: BUY STOP
sl = basePrice - slPips * pip;
tp = basePrice + tpPips * pip;
request.type = ORDER_TYPE_BUY_STOP;
}
tp = NormalizeDouble(tp, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
sl = NormalizeDouble(sl, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
request.action = TRADE_ACTION_PENDING;
request.symbol = Symbol();
request.volume = lot;
request.price = basePrice;
request.sl = sl;
request.tp = tp;
request.deviation = 3;
request.type_filling = ORDER_FILLING_FOK;
if(OrderSend(request, result))
Print("Ordem fixa aberta: ", EnumToString(request.type), " Lote: ", lot,
" TP: ", tpPips, " pips, SL: ", slPips, " pips, Preço base: ", basePrice);
else
Print("Erro na ordem fixa: ", GetLastError());
}
//+------------------------------------------------------------------+
//| Gerenciamento de posições |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
double profit = PositionGetDouble(POSITION_PROFIT);
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double positionTP = PositionGetDouble(POSITION_TP);
double positionSL = PositionGetDouble(POSITION_SL);
bool exitCondition = false;
if(type == POSITION_TYPE_BUY)
exitCondition = (currentPrice >= positionTP || currentPrice <= positionSL);
else if(type == POSITION_TYPE_SELL)
exitCondition = (currentPrice <= positionTP || currentPrice >= positionSL);
if(exitCondition)
{
lastTradeLot = PositionGetDouble(POSITION_VOLUME);
lastTradeType = type;
ClosePosition(ticket);
DailyPL += profit;
bool closedByTP = (profit >= 0);
HandleSequence(closedByTP);
break; // Processa apenas uma posição por tick para evitar duplicidade
}
}
}
if(waitingForReversal)
CheckSLReversal();
}
//+------------------------------------------------------------------+
//| Lógica de sequência após fechamento |
//+------------------------------------------------------------------+
void HandleSequence(bool closedByTP)
{
if(currentSequence == NONE)
currentSequence = closedByTP ? TP_SEQUENCE : SL_SEQUENCE;
if(currentSequence == TP_SEQUENCE)
OpenNextOrder_TP();
else if(currentSequence == SL_SEQUENCE)
HandleSLSequence(closedByTP);
}
//+------------------------------------------------------------------+
//| Função HandleSLSequence para ordens pendentes de canal |
//+------------------------------------------------------------------+
void HandleSLSequence(bool closedByTP)
{
if(!closedByTP)
OpenNextOrder_SL_initial();
else
{
waitingForReversal = true;
hedgeOpenPrice = (lastTradeType == POSITION_TYPE_SELL) ?
SymbolInfoDouble(Symbol(), SYMBOL_BID) :
SymbolInfoDouble(Symbol(), SYMBOL_ASK);
}
}
//+------------------------------------------------------------------+
//| Para sequência TP: abre ordem oposta fixa com lote = metade |
//+------------------------------------------------------------------+
void OpenNextOrder_TP()
{
ENUM_ORDER_TYPE newType = (lastTradeType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
double newLot = lastTradeLot / 2;
int newTP = 20;
int newSL = 10;
// Utiliza o preço base congelado para a ordem de canal
OpenOrderWithFixedPrice(newType, newLot, newTP, newSL, hedgeOpenPrice);
UpdateSequenceInfo(newType, newLot);
}
//+------------------------------------------------------------------+
//| Para sequência SL inicial: abre ordem oposta fixa com lote = metade |
//+------------------------------------------------------------------+
void OpenNextOrder_SL_initial()
{
ENUM_ORDER_TYPE newType = (lastTradeType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
double newLot = lastTradeLot / 2;
int newTP = 10;
int newSL = 30;
OpenOrderWithFixedPrice(newType, newLot, newTP, newSL, hedgeOpenPrice);
UpdateSequenceInfo(newType, newLot);
}
//+------------------------------------------------------------------+
//| Para sequência SL continuada: abre ordem oposta fixa com lote dobrado |
//+------------------------------------------------------------------+
void OpenNextOrder_SL()
{
ENUM_ORDER_TYPE newType = (lastTradeType == POSITION_TYPE_SELL) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
double newLot = lastTradeLot * 2;
int newTP = (newType == ORDER_TYPE_BUY) ? 20 : 10;
int newSL = (newType == ORDER_TYPE_BUY) ? 20 : 30;
OpenOrderWithFixedPrice(newType, newLot, newTP, newSL, hedgeOpenPrice);
UpdateSequenceInfo(newType, newLot);
}
//+------------------------------------------------------------------+
//| Atualiza informações da sequência |
//+------------------------------------------------------------------+
void UpdateSequenceInfo(ENUM_ORDER_TYPE newType, double newLot)
{
lastTradeLot = newLot;
lastTradeType = (newType == ORDER_TYPE_BUY) ? POSITION_TYPE_BUY : POSITION_TYPE_SELL;
waitingForReversal = false;
}
//+------------------------------------------------------------------+
//| Verificação de reversão para SL |
//+------------------------------------------------------------------+
void CheckSLReversal()
{
double pip = GetPipValue();
double reversalDistance = 10 * pip;
double currentPrice = (lastTradeType == POSITION_TYPE_SELL) ?
SymbolInfoDouble(Symbol(), SYMBOL_ASK) :
SymbolInfoDouble(Symbol(), SYMBOL_BID);
if((lastTradeType == POSITION_TYPE_SELL && currentPrice >= hedgeOpenPrice + reversalDistance) ||
(lastTradeType == POSITION_TYPE_BUY && currentPrice <= hedgeOpenPrice - reversalDistance))
{
OpenNextOrder_SL();
}
}
//+------------------------------------------------------------------+
//| Função Timer para reset diário |
//+------------------------------------------------------------------+
void OnTimer()
{
if(IsTradingTime())
{
if(!TradingActive && CheckDailyLimits())
TradingActive = true;
}
else
TradingActive = false;
}