NUNA/Logs/Indicators/Downloads/Countdown_v2.mq5
2026-01-06 05:44:21 +00:00

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;
}
//+------------------------------------------------------------------+