285 lines
26 KiB
MQL5
285 lines
26 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Countdown.mq5 |
|
|
//| Copyright 2019-2025, Virologista Kerntopf Corp. |
|
|
//| https://www.mql5.com/pt/users/virologustavo/news |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2019-2025, Virologista Kerntopf Corp."
|
|
#property link "https://www.mql5.com/pt/users/virologustavo/news"
|
|
#property version "2.0"
|
|
//--- Configurações do indicador
|
|
#property indicator_chart_window
|
|
#property indicator_buffers 0
|
|
#property indicator_plots 0
|
|
//--- Parâmetros de entrada
|
|
enum DisplayPosition
|
|
{
|
|
CommentDisplay = 0, // Exibir no comentário
|
|
ChartCorner = 1, // Exibir nas bordas do gráfico
|
|
NearPrice = 2, // Exibir próximo ao preço
|
|
};
|
|
input DisplayPosition InpTimerPosition = NearPrice; // Posição do temporizador
|
|
input color InpTextColor = clrGoldenrod; // Cor do texto
|
|
input int InpFontSize = 8; // Tamanho da fonte
|
|
input ENUM_ANCHOR_POINT InpAnchorPoint = ANCHOR_LEFT_LOWER; // Ponto de ancoragem
|
|
input ENUM_BASE_CORNER InpCornerPosition = CORNER_LEFT_LOWER; // Canto de posicionamento
|
|
input string InpOpenMarket = "09:00"; // Hora de abertura
|
|
input string InpCloseMarket = "18:30"; // Hora de fechamento
|
|
//--- Variáveis globais
|
|
string countdownLabel = "lblNextCandle";
|
|
int openMarketHour, openingMinute;
|
|
int closeMarketHour, closingMinute;
|
|
datetime nextMarketOpen; // Armazena o próximo horário de abertura
|
|
//+------------------------------------------------------------------+
|
|
//| Função para obter o horário atual no fuso horário de Brasília |
|
|
//+------------------------------------------------------------------+
|
|
datetime GetBrasiliaTime()
|
|
{
|
|
datetime gmtTime = TimeCurrent();
|
|
int brasiliaOffset = -3 * 3600; // UTC-3 em segundos
|
|
int currentOffset = TimeGMTOffset(); // Deslocamento automático (ajuste DST)
|
|
return gmtTime + brasiliaOffset + currentOffset;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função de inicialização do indicador |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
ResetLastError(); // Reseta o último erro antes de iniciar
|
|
if(!ParseTime(InpOpenMarket, openMarketHour, openingMinute))
|
|
{
|
|
Print("Erro: Formato de horário de abertura inválido. Use HH:MM (ex.: 09:00).");
|
|
return INIT_FAILED;
|
|
}
|
|
if(!ParseTime(InpCloseMarket, closeMarketHour, closingMinute))
|
|
{
|
|
Print("Erro: Formato de horário de fechamento inválido. Use HH:MM (ex.: 18:30).");
|
|
return INIT_FAILED;
|
|
}
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função de desinicialização do indicador |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
ResetLastError(); // Reseta o último erro antes de limpar
|
|
Comment(""); // Limpa comentários
|
|
if(ObjectFind(0, countdownLabel) != -1)
|
|
{
|
|
ObjectDelete(0, countdownLabel); // Remove o rótulo, se existir
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função para processar strings de tempo no formato HH:MM |
|
|
//+------------------------------------------------------------------+
|
|
bool ParseTime(string timeStr, int &hour, int &minute)
|
|
{
|
|
int colonIndex = StringFind(timeStr, ":");
|
|
if(colonIndex == -1 || StringLen(timeStr) != 5) // Verifica formato HH:MM
|
|
{
|
|
Print("Erro: Formato de hora inválido. Use HH:MM (ex.: 09:00).");
|
|
return false;
|
|
}
|
|
hour = StringToInteger(StringSubstr(timeStr, 0, colonIndex));
|
|
minute = StringToInteger(StringSubstr(timeStr, colonIndex + 1));
|
|
if(hour < 0 || hour > 23 || minute < 0 || minute > 59)
|
|
{
|
|
Print("Erro: Horário fora do intervalo válido. Use 00:00 a 23:59.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função para verificar se o mercado está aberto |
|
|
//+------------------------------------------------------------------+
|
|
bool IsMarketOpen(int openHour, int openMinute, int closeHour, int closeMinute)
|
|
{
|
|
datetime currentTime = GetBrasiliaTime();
|
|
int rawDay = (int)MathMod((currentTime / 86400), 7); // Dia bruto (0=domingo, 1=segunda, ..., 6=sábado)
|
|
int currentDay = (int)MathMod((rawDay + 3), 7); // Ajusta para Segunda-feira = 0, Domingo = 6
|
|
if(currentDay >= 0 && currentDay <= 4) // Segunda a Sexta-feira
|
|
{
|
|
datetime marketOpenTime = StringToTime(TimeToString(currentTime, TIME_DATE) + " " +
|
|
IntegerToString(openHour, 2, '0') + ":" +
|
|
IntegerToString(openMinute, 2, '0'));
|
|
datetime marketCloseTime = StringToTime(TimeToString(currentTime, TIME_DATE) + " " +
|
|
IntegerToString(closeHour, 2, '0') + ":" +
|
|
IntegerToString(closeMinute, 2, '0'));
|
|
if(currentDay == 4 && currentTime > marketCloseTime) // Sexta-feira após o fechamento
|
|
{
|
|
return false; // Mercado fechado
|
|
}
|
|
if(currentTime >= marketOpenTime && currentTime < marketCloseTime)
|
|
{
|
|
return true; // Mercado aberto
|
|
}
|
|
}
|
|
return false; // Mercado fechado
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função para calcular o próximo horário de abertura |
|
|
//+------------------------------------------------------------------+
|
|
datetime CalculateNextMarketOpen(datetime currentTime, int openHour, int openMinute)
|
|
{
|
|
int rawDay = (int)MathMod((currentTime / 86400), 7); // Dia bruto (0=domingo, 1=segunda, ..., 6=sábado)
|
|
int currentDay = (int)MathMod((rawDay + 3), 7); // Ajusta para Segunda-feira = 0, Domingo = 6
|
|
datetime nextMarketOpen = StringToTime(TimeToString(currentTime, TIME_DATE) + " " +
|
|
IntegerToString(openHour, 2, '0') + ":" +
|
|
IntegerToString(openMinute, 2, '0'));
|
|
//--- Se for sábado ou domingo, ajusta para a próxima segunda-feira
|
|
if(currentDay == 5 || currentDay == 6) // Sábado (5) ou Domingo (6)
|
|
{
|
|
int daysUntilMonday = (7 - currentDay) % 7; // Dias até a próxima segunda-feira
|
|
nextMarketOpen += daysUntilMonday * 24 * 3600; // Avança para segunda-feira
|
|
nextMarketOpen = StringToTime(TimeToString(nextMarketOpen, TIME_DATE) + " " +
|
|
IntegerToString(openHour, 2, '0') + ":" +
|
|
IntegerToString(openMinute, 2, '0'));
|
|
}
|
|
else
|
|
{
|
|
//--- Se for dia de semana, avança para o próximo dia útil
|
|
while(true)
|
|
{
|
|
if(currentDay >= 0 && currentDay <= 4) // Verifica dias úteis (Segunda a Sexta)
|
|
{
|
|
if(currentTime < nextMarketOpen)
|
|
{
|
|
break; // Horário válido encontrado
|
|
}
|
|
nextMarketOpen += 24 * 3600; // Avança para o próximo dia
|
|
}
|
|
else
|
|
{
|
|
nextMarketOpen += 24 * 3600; // Avança para o próximo dia
|
|
}
|
|
currentDay = (int)MathMod(((nextMarketOpen / 86400) + 3), 7); // Atualiza o dia da semana
|
|
}
|
|
}
|
|
return nextMarketOpen;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função para criar e configurar o rótulo |
|
|
//+------------------------------------------------------------------+
|
|
void CreateLabel(string text, int x, int y, bool isLabel = true, color textColor = clrGoldenrod)
|
|
{
|
|
ResetLastError(); // Reseta o último erro antes de criar o objeto
|
|
if(ObjectFind(0, countdownLabel) == -1)
|
|
{
|
|
if(isLabel)
|
|
{
|
|
ObjectCreate(0, countdownLabel, OBJ_LABEL, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
ObjectCreate(0, countdownLabel, OBJ_TEXT, 0, x, y);
|
|
}
|
|
}
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_COLOR, textColor);
|
|
ObjectSetString(0, countdownLabel, OBJPROP_TEXT, text);
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_FONTSIZE, InpFontSize);
|
|
ObjectSetString(0, countdownLabel, OBJPROP_FONT, "Arial");
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_ANCHOR, InpAnchorPoint);
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_CORNER, InpCornerPosition);
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_SELECTABLE, true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função principal do indicador |
|
|
//+------------------------------------------------------------------+
|
|
int OnCalculate(const int rates_total,
|
|
const int prev_calculated,
|
|
const datetime &time[],
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[],
|
|
const long &tick_volume[],
|
|
const long &volume[],
|
|
const int &spread[])
|
|
{
|
|
ArraySetAsSeries(time, true);
|
|
ArraySetAsSeries(close, true);
|
|
ResetLastError();
|
|
|
|
datetime currentTime = GetBrasiliaTime();
|
|
|
|
if(!IsMarketOpen(openMarketHour, openingMinute, closeMarketHour, closingMinute))
|
|
{
|
|
//--- Calcula o próximo horário de abertura
|
|
if(nextMarketOpen == 0 || currentTime >= nextMarketOpen)
|
|
{
|
|
nextMarketOpen = CalculateNextMarketOpen(currentTime, openMarketHour, openingMinute);
|
|
}
|
|
//--- Calcula o tempo restante até a próxima abertura
|
|
long timeRemainingInSeconds = MathMax(0, nextMarketOpen - currentTime);
|
|
int days = (int)(timeRemainingInSeconds / 86400); // Total de dias
|
|
int hours = (int)((timeRemainingInSeconds % 86400) / 3600); // Horas restantes
|
|
int minutes = (int)(((timeRemainingInSeconds % 86400) % 3600) / 60); // Minutos restantes
|
|
int seconds = (int)(((timeRemainingInSeconds % 86400) % 3600) % 60); // Segundos restantes
|
|
//--- Formata a mensagem com base no tempo restante
|
|
string message;
|
|
if(days > 0)
|
|
{
|
|
message = StringFormat("Falta %d dia(s), %02d:%02d:%02d", days, hours, minutes, seconds);
|
|
}
|
|
else
|
|
{
|
|
message = StringFormat("Próxima Abertura: %02d:%02d:%02d", hours, minutes, seconds);
|
|
}
|
|
//--- Exibe o cronômetro no formato escolhido
|
|
switch(InpTimerPosition)
|
|
{
|
|
case CommentDisplay:
|
|
Comment(message);
|
|
break;
|
|
case ChartCorner:
|
|
CreateLabel(message, 0, 0, true, InpTextColor);
|
|
break;
|
|
case NearPrice:
|
|
//--- Acompanha o preço em tempo real
|
|
if(ObjectFind(0, countdownLabel) == -1)
|
|
{
|
|
ObjectCreate(0, countdownLabel, OBJ_TEXT, 0, time[0] + PeriodSeconds() * 2, close[0]);
|
|
}
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_COLOR, InpTextColor);
|
|
ObjectSetString(0, countdownLabel, OBJPROP_TEXT, message);
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_FONTSIZE, InpFontSize);
|
|
ObjectSetString(0, countdownLabel, OBJPROP_FONT, "Arial");
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_ANCHOR, InpAnchorPoint);
|
|
ObjectMove(0, countdownLabel, 0, time[0] + PeriodSeconds() * 2, close[0]);
|
|
break;
|
|
}
|
|
return rates_total;
|
|
}
|
|
//--- Calcula o tempo restante até o próximo candlestick (mercado aberto)
|
|
datetime nextCandleTime = iTime(Symbol(), Period(), 0) + PeriodSeconds();
|
|
long timeRemainingInSeconds = MathMax(0, nextCandleTime - currentTime);
|
|
int iH = (int)(timeRemainingInSeconds / 3600);
|
|
int iM = (int)((timeRemainingInSeconds / 60) % 60);
|
|
int iS = (int)(timeRemainingInSeconds % 60);
|
|
string countdown = StringFormat("%02d:%02d:%02d", iH, iM, iS);
|
|
//--- Exibe o cronômetro no formato escolhido
|
|
switch(InpTimerPosition)
|
|
{
|
|
case CommentDisplay:
|
|
Comment("Contagem Regressiva: " + countdown);
|
|
break;
|
|
case ChartCorner:
|
|
CreateLabel(countdown, 0, 0, true, InpTextColor);
|
|
break;
|
|
case NearPrice:
|
|
//--- Acompanha o preço em tempo real
|
|
if(ObjectFind(0, countdownLabel) == -1)
|
|
{
|
|
ObjectCreate(0, countdownLabel, OBJ_TEXT, 0, time[0] + PeriodSeconds() * 2, close[0]);
|
|
}
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_COLOR, InpTextColor);
|
|
ObjectSetString(0, countdownLabel, OBJPROP_TEXT, countdown);
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_FONTSIZE, InpFontSize);
|
|
ObjectSetString(0, countdownLabel, OBJPROP_FONT, "Arial");
|
|
ObjectSetInteger(0, countdownLabel, OBJPROP_ANCHOR, InpAnchorPoint);
|
|
ObjectMove(0, countdownLabel, 0, time[0] + PeriodSeconds() * 2, close[0]);
|
|
break;
|
|
}
|
|
return rates_total;
|
|
}
|
|
//+------------------------------------------------------------------+
|