//+------------------------------------------------------------------+
//| Robô Keltner.mq5 |
//| Thiago Oliveira |
//| https://olimbot.com.br |
//+------------------------------------------------------------------+
#property copyright "Thiago Oliveira"
#property link "https://olimbot.com.br"
#property version "1.00"
#define keltner "Indicators\\keltner.ex5"
#resource "\\"+keltner
#include
CTrade trade;
enum preenchimento
{
e_fok = ORDER_FILLING_FOK, // FOK
e_ioc = ORDER_FILLING_IOC, // IOC
e_ret = ORDER_FILLING_RETURN // Return
};
input ulong m_magic = 123; // ID
input double m_volume = 1; // Volume
input preenchimento m_filling = e_ret; // Prechimento
input double m_tp = 300; // Take Profit
input double m_sl = 300; // Stoploss
input int m_periodo_1 = 19; // Periodo keltner rápido
input double m_devio_1 = 2; // Desvio keltner rápido
input ENUM_MA_METHOD m_metodo_1 = MODE_SMA; // Tipo de média rápida
input int m_periodo_2 = 200; // Periodo keltner lento
input double m_devio_2 = 3; // Desvio keltner lento
input ENUM_MA_METHOD m_metodo_2 = MODE_SMA; // Tipo de média lento
input string m_hora_inicio = "09:15"; // Horario inicial
input string m_hora_fim = "15:30"; // Horario final
input string m_hora_zerar = "16:00"; // Horario zerar
int handle1, handle2;
MqlDateTime e_inicial, e_final, e_zerar, e_atual;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
handle1 = iCustom(_Symbol,PERIOD_CURRENT,"::"+keltner,m_periodo_1,m_devio_1,m_metodo_1);
handle2 = iCustom(_Symbol,PERIOD_CURRENT,"::"+keltner,m_periodo_2,m_devio_2,m_metodo_2);
trade.SetExpertMagicNumber(m_magic);
trade.SetTypeFilling((ENUM_ORDER_TYPE_FILLING)m_filling);
if(handle1 == INVALID_HANDLE)
{
printf("Erro no keltner rápido");
return INIT_FAILED;
}
if(handle2 == INVALID_HANDLE)
{
printf("Erro no keltner lento");
return INIT_FAILED;
}
datetime inicio = StringToTime(m_hora_inicio);
datetime fim = StringToTime(m_hora_fim);
datetime zerar = StringToTime(m_hora_zerar);
TimeToStruct(inicio,e_inicial);
TimeToStruct(fim,e_final);
TimeToStruct(zerar,e_zerar);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool horario_entrada()
{
if(e_atual.hour > e_inicial.hour || (e_atual.hour == e_inicial.hour && e_atual.min >= e_inicial.min))
if(e_atual.hour < e_final.hour || (e_atual.hour == e_final.hour && e_atual.min < e_final.min))
return true;
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool horario_zerar()
{
if(e_atual.hour > e_zerar.hour || (e_atual.hour == e_zerar.hour && e_atual.min >= e_zerar.min))
return true;
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool verificar_ordem()
{
for(int i=OrdersTotal()-1; i>=0; i--)
{
ulong ticket = OrderGetTicket(i);
if(!OrderSelect(ticket))
continue;
if(OrderGetInteger(ORDER_MAGIC) != m_magic)
continue;
if(OrderGetString(ORDER_SYMBOL) != _Symbol)
continue;
ENUM_ORDER_TYPE tipo = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
if(tipo == ORDER_TYPE_BUY || tipo == ORDER_TYPE_SELL)
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double posicionamento()
{
double posicao = 0.0;
for(int i=PositionsTotal()-1; i>=0; i--)
{
ulong ticket = PositionGetTicket(i);
PositionSelectByTicket(ticket);
if(PositionGetInteger(POSITION_MAGIC) != m_magic)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
ENUM_POSITION_TYPE tipo = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double volume = PositionGetDouble(POSITION_VOLUME);
if(tipo == POSITION_TYPE_BUY)
posicao += volume;
else
posicao -= volume;
}
return posicao;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void zeragem()
{
for(int i=PositionsTotal()-1; i>=0; i--)
{
ulong ticket = PositionGetTicket(i);
PositionSelectByTicket(ticket);
if(PositionGetInteger(POSITION_MAGIC) != m_magic)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
trade.PositionClose(ticket);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool verificar_compra()
{
double price[1];
if(CopyBuffer(handle1,2,0,1,price) < 1)
{
printf("Erro na cópia dos buffers inferiores");
return false;
}
if(iClose(_Symbol,PERIOD_CURRENT,0) < price[0])
return true;
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool verificar_venda()
{
double price[1];
if(CopyBuffer(handle1,1,0,1,price) < 1)
{
printf("Erro na cópia dos buffers superiores");
return false;
}
if(iClose(_Symbol,PERIOD_CURRENT,0) > price[0])
return true;
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool enviar_compra()
{
double price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double tp = (m_tp == 0) ? 0.00 : normalizar(price+(m_tp*_Point));
double sl = (m_sl == 0) ? 0.00 : normalizar(price-(m_sl*_Point));
return trade.Buy(m_volume,_Symbol,price,sl,tp,"Ordem de compra");
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool enviar_venda()
{
double price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
double tp = (m_tp == 0) ? 0.00 : normalizar(price-(m_tp*_Point));
double sl = (m_sl == 0) ? 0.00 : normalizar(price+(m_sl*_Point));
return trade.Sell(m_volume,_Symbol,price,sl,tp,"Ordem de venda");
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool filtro_compra()
{
double price[1];
if(CopyBuffer(handle2,2,0,1,price) < 1)
{
printf("Erro na cópia dos buffers lentos inferiores");
return false;
}
if(iClose(_Symbol,PERIOD_CURRENT,0) > price[0])
return true;
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool filtro_venda()
{
double price[1];
if(CopyBuffer(handle2,1,0,1,price) < 1)
{
printf("Erro na cópia dos buffers lentos superiores");
return false;
}
if(iClose(_Symbol,PERIOD_CURRENT,0) < price[0])
return true;
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void verificar_saida(const double posicao)
{
if(posicao > 0)
{
if(verificar_venda())
zeragem();
}
else
if(posicao < 0)
if(verificar_compra())
zeragem();
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
TimeToStruct(TimeCurrent(),e_atual);
double pos = posicionamento();
if(horario_zerar())
{
if(pos != 0)
zeragem();
}
else
if(pos != 0.00)
verificar_saida(pos);
else
if(horario_entrada())
if(verificar_venda())
{
if(filtro_venda())
if(verificar_ordem())
enviar_venda();
}
else
if(verificar_compra())
if(filtro_compra())
if(verificar_ordem())
enviar_compra();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double normalizar(const double price)
{
double tamanho = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
double ajuste = (price > 0 && tamanho > 0) ? (round(price/tamanho)*tamanho) : 0.0;
return NormalizeDouble(ajuste,_Digits);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Thiago Oliveira - Olimbot |
//| contato@olimbot.com.br |
//+------------------------------------------------------------------+
#property copyright "Thiago Oliveira - OlimBot"
#property link "olimbot.com.br"
#property version "1.00"
#property description "Última atualização em 11/Janeiro/2022"
#property description " "
#property description "Este expert é constituído sem o uso de bibliotecas padrões do meta trader"
#property description "Todos os recursos são adaptados para uma melhor perfomance"
#property description "Este modelo funciona tanto para contas HEDGINGS quanto para NETTINGS"
#property description "Entradas e saídas são sempre utilizando ordens limitadas para garantir melhor preço na B3"
#property description "Se existir distância mínima para ordem pendente esta é ajustada automaticamente"
#property description "Em contas HEDGINGS a saída é fechada utilizando a posição inversa"
// O indicador personalizado deve estar com o arquivo executável na pasta de indicadores
#define indicador "Indicators\\Regressao.ex5" // Indicador personalizado que será compilado na pasta Indicators\\...
// Indicador compilado deve estar no formato .ex5
#resource "\\"+indicador // Compilando junto ao EA o indicador
// Enumerador para o tipo de preenchimento
enum e_filling
{
es_fok = ORDER_FILLING_FOK, // Fok
es_ioc = ORDER_FILLING_IOC, // Ioc
es_return = ORDER_FILLING_RETURN // Return
};
// Enumerador para o tipo de validade da ordem
enum e_time
{
es_gtc = ORDER_TIME_GTC, // Até cancelar
es_day = ORDER_TIME_DAY // Hoje
};
// Caracterização de true e false para sim e não
enum e_sn
{
nao = 0, // Não
sim = 1 // Sim
};
//+------------------------------------------------------------------+
//| Parâmetros de entrada |
//+------------------------------------------------------------------+
sinput group "CONFIGURAÇÃO INICIAL";
sinput string in_nome = "SET OLIMBOT"; // Nome do expert
sinput ulong in_magic = 123; // Magic Number
sinput double in_volume = 1; // Volume
input ENUM_TIMEFRAMES in_timeframe = PERIOD_CURRENT; // Tempo gráfico do EA
sinput group "CONFIGURAÇÃO ADICIONAL";
sinput e_filling in_preenchimento = es_return; // Tipo de preenchimento
sinput e_time in_validade = es_day; // Validade da ordem
sinput e_sn in_hab_painel = true; // Habilitar painel gráfico
sinput e_sn in_hab_indicadores = false; // Inserir indicadores
sinput group "ALVOS DE SAÍDA";
input double in_takeprofit = 0; // TakeProfit (pts)
input double in_stoploss = 0; // StopLoss (pts)
sinput group "INDICADOR REGRESSÃO LINEAR";
input int in_reg_periodo = 20; // Período da regressão
input double in_reg_coef = 2.00; // Coeficiente de regressão
sinput group "INDICADOR MACD";
input int in_macd_fast = 12; // Período EMA rápida
input int in_macd_slow = 5; // Período EMA lenta
sinput group "AJUSTES DE SINAIS";
input e_sn in_cancel_sinal = true; // Atualizar pendente a cada sinal
input e_sn in_barra_atual = true; // Impedir novas entradas na mesma barra
input double in_macd_min = 0; // Tamanho mínimo do MACD
sinput double in_spread = 0; // Spread máximo (ticks)(0=off)
sinput group "REGRAS DE HORÁRIOS";
input e_sn in_hab_hora = true; // Habilitar verificação de horários
input string in_iniciar = "09:00"; // Horário de iniciar (novas posições)
input string in_parar = "14:45"; // Horário de parar (novas posições)
input e_sn in_hab_zerar = true; // Habilitar zeragem compulsória
input string in_zerar = "14:55"; // Horário de zeragem (compulsória)
int handle_macd, handle_reg; // Manipuladores de indicadores
MqlDateTime hora_iniciar, hora_parar, hora_zerar, hora_atual; // Estruturas de horários
// Estrutura para armazenar os dados das ordens pendentes
struct s_ordens
{
ulong compra_limit; // Número que corresponderá a uma ordem de compra pendente
ulong venda_limit; // Para venda pendente (ordem limitada)
};
// Estrutura para armazenar os dados das posições abertas
struct s_posicoes
{
double volume; // Lot
double lucro; // Lucro real da posição aberta, referente ao verdadeiro preço de saída
datetime abertura; // Horário que se inicio a posição
ulong ticket_compra; // Bilhete da posição se for de compra
ulong ticket_venda; // Bilhete da posição de venda se existir ambas simultanemanetes, são fechadas entre si
};
//+------------------------------------------------------------------+
//| Inicializãção do expert |
//+------------------------------------------------------------------+
int OnInit()
{
ChartSetSymbolPeriod(0,_Symbol,in_timeframe); // Força que o tempo gráfico da janela do robô fique no escolhido
if(!in_hab_indicadores) // Desabilita visualização dos indicadores no backtest, se não permitir inserir
TesterHideIndicators(true); // Deve ser chamado antes da inicialização dos indicadores
// Chamada dos indicadores utilizados
// No macd, o parâmetro de sinal não é necessário para esta estratégia
handle_macd = iMACD(_Symbol,_Period,in_macd_fast,in_macd_slow,1,PRICE_CLOSE); // Indicador nativo mql5
handle_reg = iCustom(_Symbol,_Period,"::"+indicador,in_reg_periodo,in_reg_coef); // Indicador personalizado
// Confirmando se os indicadores foram inseridos corretamente no EA
if(handle_reg == INVALID_HANDLE)
{
Print("[%s] Erro na criação do manipulador do indicador regressão linear",in_nome);
return INIT_FAILED;
}
if(handle_macd == INVALID_HANDLE)
{
Print("[%s] Erro na criação do manipulador do indicador MACD",in_nome);
return INIT_FAILED;
}
// Passando a referência de horário para o padrão correto
TimeToStruct(StringToTime(in_iniciar),hora_iniciar);
TimeToStruct(StringToTime(in_parar),hora_parar);
TimeToStruct(StringToTime(in_zerar),hora_zerar);
// Verifica se o horário de ínicio realmente antecede o horário máximo de abertura de posição
if(hora_iniciar.hour > hora_parar.hour || (hora_iniciar.hour == hora_parar.hour && hora_iniciar.min >= hora_parar.min))
{
printf("[%s] Configuração de horários de entrada e parada inválidos",in_nome);
return INIT_FAILED;
}
// Se estiver habilitada inicia o desenho do painel gráfico (dashboard)
if(in_hab_painel)
if(!iniciar_painel())
{
printf("[%s] Falha ao iniciar o painel gráfico",in_nome);
return INIT_FAILED;
}
// Adiciona as linhas de preço e retira a separação em grade para melhor visualizar
ChartSetInteger(0,CHART_SHOW_GRID,0,false); // Remove grade do gráfico
ChartSetInteger(0,CHART_SHOW_ASK_LINE,0,true); // Adiciona linha ask
ChartSetInteger(0,CHART_SHOW_BID_LINE,0,true); // Adiciona linha bid
ChartSetInteger(0,CHART_SHOW_LAST_LINE,0,true); // Adiciona linha do último negócio se for mercado de bolsa
if(in_hab_indicadores) // Possibilita a inserção dos indicadores no gráfico do EA
if(remover_indicadores()) // Remove todos os indicadores no gráfico antes de inserir os usados pelo EA
{
ChartIndicatorAdd(0,1,handle_reg); // Inserindo no modo visual o indicador de regressão
ChartIndicatorAdd(0,2,handle_macd); // Inserindo no modo visual o indicador de MACD
}
printf("[%s] ***** Expert Iniciado com sucesso",in_nome);
printf("[%s] ***** Horários: Local %s Corretora %s",
in_nome,TimeToString(TimeLocal(),TIME_SECONDS),TimeToString(TimeCurrent(),TIME_SECONDS));
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Desligamento de expert |
//+------------------------------------------------------------------+
void OnDeinit(const int motivo)
{
if(in_hab_indicadores)
remover_indicadores(); // Remove todos os indicadores do gráfico se a opção estiver habilitada
ObjectsDeleteAll(0,"painel_",0,-1); // Remove o painel do gráfico, se existir
IndicatorRelease(handle_macd); // Liberando mémoria alocada para os indicadores
IndicatorRelease(handle_reg);
printf("[%s] ***** Horários: Local %s Corretora %s",
in_nome,TimeToString(TimeLocal(),TIME_SECONDS),TimeToString(TimeCurrent(),TIME_SECONDS));
printf("[%s] ***** Expert desligado pelo motivo %d",in_nome,motivo);
// A liberação de memória dos indicadores pode ser mais lenta que na inicialização
// Se o EA for inserido enquanto ainda está o no porcesso de liberar mémoria, parte dos dados dos indicadores pode ser perdido
Sleep(1000); // Por isso é necessário aguardar a remoção completa
}
//+------------------------------------------------------------------+
//| Remoção de indicadores |
//+------------------------------------------------------------------+
bool remover_indicadores(void)
{
// Verifica todas as janelas do gráfico
for(int i=(int)ChartGetInteger(0,CHART_WINDOWS_TOTAL)-1; i>=0; i--)
while(ChartIndicatorsTotal(0,i) > 0) // Verifica todos os indicadores em cada subjanela
{
string nome = ChartIndicatorName(0,i,0); // Pega o nome do indicador
if(!ChartIndicatorDelete(0,i,nome)) // Após a obtenção do nome, o mesmo é deletado da janela
break;
}
return true;
}
//+------------------------------------------------------------------+
//| Inicialização do painel gráfico |
//+------------------------------------------------------------------+
bool iniciar_painel(void)
{
// Somente necessário chamar uma vez tal função
// Nome dado ao fundo do painel
string nome = "painel_fundo";
if(ObjectCreate(0,nome,OBJ_RECTANGLE_LABEL,0,0,0))
{
ObjectSetInteger(0,nome,OBJPROP_XDISTANCE,5); // Distância da borda
ObjectSetInteger(0,nome,OBJPROP_YDISTANCE,15);
ObjectSetInteger(0,nome,OBJPROP_XSIZE,250); // Tamanho das cordernadas do painel eixo X
ObjectSetInteger(0,nome,OBJPROP_YSIZE,130); // Eixo Y
ObjectSetInteger(0,nome,OBJPROP_BGCOLOR,clrBlack); // Cor de fundo
ObjectSetInteger(0,nome,OBJPROP_BORDER_TYPE,BORDER_FLAT);
ObjectSetInteger(0,nome,OBJPROP_CORNER,CORNER_LEFT_UPPER); // Referência de posicionamento (borda superior esquerda)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrMaroon); // Cor da borda
ObjectSetInteger(0,nome,OBJPROP_STYLE,STYLE_SOLID);
ObjectSetInteger(0,nome,OBJPROP_WIDTH,3);
ObjectSetInteger(0,nome,OBJPROP_BACK,false); // Força painel para frente do gráfico
ObjectSetInteger(0,nome,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,nome,OBJPROP_SELECTED,false);
ObjectSetInteger(0,nome,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,nome,OBJPROP_ZORDER,0);
}
else
return false;
Sleep(200); // Pausa para processar o desenho do fundo do painel
ChartRedraw(); // Força uma atualização do desenho, garantindo o fundo desenhado antes dos objetos
// Loop para desenhar cada item exibido no painel
for(int i=0; i<8; i++)
{
string texto[8];
nome = "painel_parametro_"+IntegerToString(i); // Nome de cada item pegando a sua posuição como complementação
switch(i)
{
case 0:
texto[i] = "Nome do EA";
break;
case 1:
texto[i] = "Magic Number";
break;
case 2:
texto[i] = "Posição";
break;
case 3:
texto[i] = "Abertura Pos";
break;
case 4:
texto[i] = "Lucro Real";
break;
case 5:
texto[i] = "Oscilação";
break;
case 6:
texto[i] = "Atraso Corretora";
break;
case 7:
texto[i] = "Tempo Nova Barra";
break;
default:
break;
}
// Criando cada item do painel como objeto de texto
if(ObjectCreate(0,nome,OBJ_LABEL,0,0,0))
{
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrSnow); // Cor do texto dos parâmetros
ObjectSetInteger(0,nome,OBJPROP_XDISTANCE,10);
ObjectSetInteger(0,nome,OBJPROP_YDISTANCE,20+(i*15)); // Pula uma linha com esses valores em cada passo do loop
ObjectSetInteger(0,nome,OBJPROP_FONTSIZE,9);
ObjectSetString(0,nome,OBJPROP_FONT,"Rockwell"); // Tipo da fonte de texto
ObjectSetString(0,nome,OBJPROP_TEXT,texto[i]); // Texto a ser exibido
ObjectSetInteger(0,nome,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,nome,OBJPROP_BACK,false);
}
else
return false;
// Criando os itens que receberão os dados no painel
nome = "painel_dados_"+IntegerToString(i); // Nome dos objetos criados, ainda no loop
if(ObjectCreate(0,nome,OBJ_LABEL,0,0,0))
{
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrGray);
ObjectSetInteger(0,nome,OBJPROP_XDISTANCE,140); // Distânciando mais da borda, garantido que ficará lada a lado
ObjectSetInteger(0,nome,OBJPROP_YDISTANCE,20+(i*15)); // Pulando uma linha a cada ciclo do loop
ObjectSetInteger(0,nome,OBJPROP_FONTSIZE,9);
ObjectSetString(0,nome,OBJPROP_FONT,"Rockwell");
ObjectSetString(0,nome,OBJPROP_TEXT,"-");
ObjectSetInteger(0,nome,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,nome,OBJPROP_BACK,false);
}
else
return false;
}
Sleep(200);
ChartRedraw();
// Verifica as posições e envia os dados para o painel
s_posicoes posicao = posicionamento();
atualizar_painel(posicao); // Enviando posicionamento do EA para o painel
return true;
}
//+------------------------------------------------------------------+
//| Atualização dos dados do painel |
//+------------------------------------------------------------------+
void atualizar_painel(const s_posicoes &posicao)
{
// Alguns dados exibidos no painel
string texto[8]; // Receberá os valores exibidos no painel
datetime tempo = iTime(_Symbol,_Period,0)+PeriodSeconds(_Period)-TimeCurrent(); // Tempo restante da barra
double ontem = iClose(_Symbol,PERIOD_D1,1); // Fechamento dia anterior
double dif = iClose(_Symbol,PERIOD_D1,0)-ontem; // DIferença entre fechamentos
double oscilacao = (ontem > 0) ? (dif/ontem)*100 : 0.00; // Cálculo percentual do desempenho diário do ativo
// Atribuindo os valores
for(int i=0; i<8; i++)
{
string nome = "painel_dados_"+IntegerToString(i);
switch(i)
{
case 0:
texto[i] = in_nome;
break;
case 1:
texto[i] = IntegerToString(in_magic);
break;
case 2:
texto[i] = DoubleToString(posicao.volume,2);
if(posicao.volume > 0) // Regra de coloração para alta
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrGreen);
else
if(posicao.volume < 0) // Regra de coloração para baixa
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrRed);
else
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrWhite); // Regra de coloração neutra
break;
case 3:
texto[i] = TimeToString(posicao.abertura,TIME_SECONDS);
break;
case 4:
texto[i] = DoubleToString(posicao.lucro,2);
if(posicao.lucro > 0)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrGreen);
else
if(posicao.lucro < 0)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrRed);
else
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrWhite);
break;
case 5:
texto[i] = DoubleToString(dif,_Digits)+" ("+DoubleToString(oscilacao,2)+"%)";
if(dif > 0)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrGreen);
else
if(dif < 0)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrRed);
else
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrWhite);
break;
case 6:
texto[i] = TimeToString(TimeLocal()-TimeCurrent(),TIME_SECONDS); // Sincronização do tempo local e da corretora
break;
case 7:
texto[i] = TimeToString(tempo,TIME_SECONDS);
break;
default:
break;
}
ObjectSetString(0,nome,OBJPROP_TEXT,texto[i]); // Atualiza o valor em forma de texto
}
}
//+------------------------------------------------------------------+
//| Checar se horário de operação |
//+------------------------------------------------------------------+
bool horario_entrar(void)
{
if(!in_hab_hora) // Se não for para verificar horários a função retorna que pode continuar
return true;
static bool exibir_msg = true; // Variável estática para evitar excesso de repetição da msg exibida na codicional
// Verifica se está dentro do horário permitido
if(hora_atual.hour > hora_iniciar.hour || (hora_atual.hour == hora_iniciar.hour && hora_atual.min >= hora_iniciar.min))
if(hora_atual.hour < hora_parar.hour || (hora_atual.hour == hora_parar.hour && hora_atual.min < hora_parar.min))
{
if(exibir_msg)
printf("[%s] Horário de operações permitido. Operacional das %s às %s",in_nome,in_iniciar,in_parar);
exibir_msg = false; // Impede a repetição da msg
return true;
}
if(!exibir_msg)
{
if(exibir_msg)
printf("[%s] Fora do horário de operações. Operacional das %s às %s",in_nome,in_iniciar,in_parar);
exibir_msg = true; // Atualiza que deverá repetir a msg se a condicional for satisfeita na próxima chamada
}
return false; // Não stá no horário de novas posições
}
//+------------------------------------------------------------------+
//| Horário de zeragem compulsória |
//+------------------------------------------------------------------+
bool horario_zeragem(void)
{
// Função que verifica horário de zeragem
static bool exibir_msg = true; // Idem recurso anterior
if(in_hab_zerar) // Desabilita e habilita zeragem ou não pr horário
if(hora_atual.hour > hora_zerar.hour || (hora_atual.hour == hora_zerar.hour && hora_atual.min >= hora_zerar.min))
{
if(exibir_msg)
printf("[%s] Horário de zeragem compulsória às %s",in_nome,in_zerar);
exibir_msg = false;
return true; // Confirma que terá que zerar as posições
}
exibir_msg = true;
return false;
}
//+------------------------------------------------------------------+
//| Contador de ordens pendentes |
//+------------------------------------------------------------------+
s_ordens pendentes(void)
{
// Estrutura que armzenará e retornará os valores das ordens pendentes encontradas
s_ordens ordem;
ZeroMemory(ordem);
for(int i=OrdersTotal()-1; i>=0; i--) // Loop para verificar todas as ordens
{
ulong ticket = OrderGetTicket(i); // Pegando separadamente o bilhete atribuído a cada ordem
if(!OrderSelect(ticket))
continue;
if((ulong)OrderGetInteger(ORDER_MAGIC) != in_magic) // Verificar magic number
continue;
if(OrderGetString(ORDER_SYMBOL) != _Symbol) // Verificar simbolo
continue;
ENUM_ORDER_TYPE tipo = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); // Verifica ó tipo de ordem, limitada ou stop
bool deletar_repetida = false;
// Se houver ordem colocada em barra anterior a mesma é deletada na troca de barra
if(in_cancel_sinal) // Pode ser habilitada ou não este recurso via input
if(OrderGetInteger(ORDER_TIME_SETUP) < iTime(_Symbol,_Period,0))
if(realizar_negocio(TRADE_ACTION_REMOVE,tipo,ticket))
continue;
// Se houver mais de uma ordem no mesmo sentido, a mais antiga é deletada
if(tipo == ORDER_TYPE_BUY_LIMIT)
{
if(ordem.compra_limit == 0)
ordem.compra_limit = ticket;
else
deletar_repetida = true; // Confimando que há ordens repetidas
}
else
if(tipo == ORDER_TYPE_SELL_LIMIT)
{
if(ordem.venda_limit == 0)
ordem.venda_limit = ticket;
else
deletar_repetida = true;
}
else
deletar_repetida = true;
if(deletar_repetida)
realizar_negocio(TRADE_ACTION_REMOVE,tipo,ticket); // Deletando ordem repetida, se houver
}
return ordem;
}
//+------------------------------------------------------------------+
//| Posicionamento |
//+------------------------------------------------------------------+
s_posicoes posicionamento(void)
{
// Estrutura para armazenar os dados de ordens executadas
s_posicoes posicao;
ZeroMemory(posicao);
for(int i=PositionsTotal()-1; i>=0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC) != in_magic)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
ENUM_POSITION_TYPE tipo = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double vol = PositionGetDouble(POSITION_VOLUME);
double price = PositionGetDouble(POSITION_PRICE_OPEN);
if(posicao.abertura == 0)
posicao.abertura = (datetime)PositionGetInteger(POSITION_TIME);
if(tipo == POSITION_TYPE_BUY)
{
posicao.volume += vol; // Verifica o volume aberto, positivo para compras
posicao.ticket_compra = ticket;
// Verifica o lucro real da posição com base no preço verdadeiro de saída e não pelo último negócio
if(!OrderCalcProfit(ORDER_TYPE_BUY,_Symbol,vol,price,SymbolInfoDouble(_Symbol,SYMBOL_BID),posicao.lucro))
printf("[%s] Falha no cálculo de lucro da posição comprada %d",in_nome,(string)ticket);
}
else
{
posicao.volume -= vol; // Verifica o volume aberto, negativo para vendas
posicao.ticket_venda = ticket;
if(!OrderCalcProfit(ORDER_TYPE_SELL,_Symbol,vol,price,SymbolInfoDouble(_Symbol,SYMBOL_ASK),posicao.lucro))
printf("[%s] Falha no cálculo de lucro da posição vendida %d",in_nome,(string)ticket);
}
// Se houver compra e venda abertas simultaneamente, conta tipo hedge, serão fechadas uma pela outra
if(posicao.ticket_compra > 0)
if(posicao.ticket_venda > 0)
if(realizar_negocio(TRADE_ACTION_CLOSE_BY,ORDER_TYPE_CLOSE_BY,posicao.ticket_compra,0,0,0,0,posicao.ticket_venda))
{
i = PositionsTotal(); // Reinicia o contador para contabilizar a atualização de posições
ZeroMemory(posicao); // Zera os dados armazenados para verificar novamente
}
}
return posicao;
}
//+------------------------------------------------------------------+
//| Verificação de spread entre compra e venda |
//+------------------------------------------------------------------+
bool check_spread(void)
{
if(in_spread == 0) // Se selecionado zero via input, desabilita a verificação
return true;
double spread = SymbolInfoInteger(_Symbol,SYMBOL_SPREAD)*_Point; // Verificando o spread atual
double spr = normalizar(in_spread*SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE)); // Convertendo para tamanho de tick
if(spread > spr) // Verificando se o spread é superior ao selecionado
{
printf("[%s] Entrada não permitida. Spread de %f supera o máximo de %f",in_nome,spread,spr);
return false;
}
else
return true;
}
//+------------------------------------------------------------------+
//| Verificação da distância mínima do stop |
//+------------------------------------------------------------------+
double check_stoplevel(double price, const ENUM_ORDER_TYPE tipo)
{
// Distância mínima permitida do preço para colocar ordens pendentes
double stoplevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point;
double referencia = 0.00; // Iniciando variavel
switch(tipo)
{
case ORDER_TYPE_BUY_LIMIT:
referencia = SymbolInfoDouble(_Symbol,SYMBOL_ASK)-stoplevel; // Referência para o stoplevel
if(price > referencia)
price = normalizar(referencia); // Altera para o valor mínimo aceito se a ordem estiver fora do parâmetro
break;
case ORDER_TYPE_SELL_LIMIT:
referencia = SymbolInfoDouble(_Symbol,SYMBOL_BID)+stoplevel;
if(price < referencia)
price = normalizar(referencia);
break;
default:
break;
}
return price;
}
//+------------------------------------------------------------------+
//| Filtro de ordens a mercado repetidas |
//+------------------------------------------------------------------+
bool check_permissao(void)
{
// Verifica se tem ordem aguardando ser executada ou não, evita ordens a mercado repetidas ou excessivas
for(int i=OrdersTotal()-1; i>=0; i--)
{
ulong ticket = OrderGetTicket(i);
if(!OrderSelect(ticket))
continue;
if((ulong)OrderGetInteger(ORDER_MAGIC) != in_magic)
continue;
if(OrderGetString(ORDER_SYMBOL) != _Symbol)
continue;
ENUM_ORDER_TYPE tipo = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
// Verificando ordens a mercado que ainda não foram confirmadas pela corretora
if(tipo == ORDER_TYPE_BUY || tipo == ORDER_TYPE_SELL || tipo == SYMBOL_ORDER_CLOSEBY)
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Filtro de posição executada em barra atual |
//+------------------------------------------------------------------+
bool check_barra(void)
{
// Verifica se houve ordem executada na barra atual, evitando entradas excessivas na mesma barra
// Pode ser habilitada ou não
if(!in_barra_atual)
return false;
// Selecionando apenas a barra atual para o período
HistorySelect(iTime(_Symbol,_Period,0),TimeCurrent());
for(int i=HistoryDealsTotal()-1; i>=0; i--)
{
ulong ticket = HistoryDealGetTicket(i);
if(ticket == 0)
continue;
if(HistoryDealGetInteger(ticket,DEAL_MAGIC) != in_magic)
continue;
if(HistoryDealGetString(ticket,DEAL_SYMBOL) != _Symbol)
continue;
ENUM_DEAL_TYPE tipo = (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket,DEAL_TYPE);
// Confirmando que a ordem foi de compra ou venda, não pendente nem fechamento pela oposta
if(tipo == DEAL_TYPE_BUY || tipo == DEAL_TYPE_SELL)
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Deletar ordens pendentes |
//+------------------------------------------------------------------+
void deletar_pendentes(const s_ordens &ordem)
{
// Verifica se o bilhete da ordem existe e remove se positivo
if(ordem.compra_limit != 0)
if(OrderSelect(ordem.compra_limit)) // Confirmando e selecionando a ordem pelo bilhete único
realizar_negocio(TRADE_ACTION_REMOVE,ORDER_TYPE_BUY_LIMIT,ordem.compra_limit); // Deletando ordem pendente
if(ordem.venda_limit != 0)
if(OrderSelect(ordem.venda_limit))
realizar_negocio(TRADE_ACTION_REMOVE,ORDER_TYPE_SELL_LIMIT,ordem.venda_limit);
}
//+------------------------------------------------------------------+
//| Zeragem compulsória |
//+------------------------------------------------------------------+
void zeragem_compulsoria(void)
{
// Verificando todas as posições abertas inclusive serve para contas hedge
for(int i=PositionsTotal()-1; i>=0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC) != in_magic)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
ENUM_POSITION_TYPE tipo = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double vol = PositionGetDouble(POSITION_VOLUME);
// Zerando ordem a mercado, seja de compra ou venda
if(tipo == POSITION_TYPE_BUY)
realizar_negocio(TRADE_ACTION_DEAL,ORDER_TYPE_SELL,ticket,vol,SymbolInfoDouble(_Symbol,SYMBOL_BID));
else
realizar_negocio(TRADE_ACTION_DEAL,ORDER_TYPE_BUY,ticket,vol,SymbolInfoDouble(_Symbol,SYMBOL_ASK));
}
}
//+------------------------------------------------------------------+
//| Normalizar preço |
//+------------------------------------------------------------------+
double normalizar(const double price)
{
// Função que garante que o preço fique com valores aceitos para o ativo
double tamanho = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
int digitos = (int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
double ajuste = (tamanho > 0 && price > 0) ? ((MathRound(price/tamanho))*tamanho) : 0.00;
if(price > 0)
if(ajuste < tamanho)
ajuste = tamanho;
// Ajustando como saída o preço corrigido ou aprovado
return NormalizeDouble(ajuste,digitos);
}
//+------------------------------------------------------------------+
//| Realizando as transações |
//+------------------------------------------------------------------+
bool realizar_negocio(const ENUM_TRADE_REQUEST_ACTIONS acao,
const ENUM_ORDER_TYPE tipo,
const ulong ticket,
const double volume=0.0,
const double price=0.0,
const double stoploss=0.0,
const double takeprofit=0.0,
const ulong ticket_by=0.0)
{
MqlTradeRequest request; // Estrutura da ordem a ser enviada
MqlTradeResult result; // Resultados de verificação
MqlTradeCheckResult check_result; // Dados de verificação
// Zerando residuos da solicitação anterior
ZeroMemory(request);
ZeroMemory(result);
ZeroMemory(check_result);
request.magic = in_magic;
request.symbol = _Symbol;
request.comment = "["+in_nome+"]";
request.type_time = (ENUM_ORDER_TYPE_TIME)in_validade; // Tipo de validade da ordem, obrigatório em algumas corretoras
request.type_filling = (ENUM_ORDER_TYPE_FILLING)in_preenchimento; // Tipo de preenchimento, de acordo com o tipo de mercado
request.price = normalizar(price);
request.volume = NormalizeDouble(volume,2);
request.action = acao;
request.type = tipo;
request.sl = stoploss;
request.tp = takeprofit;
request.position = ticket;
request.position_by = ticket_by;
request.order = ticket;
// Checando a ordem e seus resultados antes do envio da mesma
if(!OrderCheck(request,check_result))
{
printf("[%s] Erro %d na checagem da ordem. Código %d (%s)",in_nome,GetLastError(),check_result.retcode,check_result.comment);
return false;
}
else
printf("[%s] Checagem de ordem bem sucedida. Enviando ordem...",in_nome);
// Se bem sucessedida a verificação a ordem é enviada
if(!OrderSend(request,result))
{
// Mensagem exibida em caso de falha no envio da ordem
printf("[%s] Erro %d no envio da ordem. Código %d (%s)",in_nome,GetLastError(),result.retcode,result.comment);
return false;
}
else
printf("[%s] Sucesso no negócio [%d]. Código %d (%s)",in_nome,result.order,result.retcode,result.comment);
return true;
}
//+------------------------------------------------------------------+
//| Envio de compra limitada |
//+------------------------------------------------------------------+
bool comprar(const double stop, const double take)
{
// Austando na entrada o preço de acordo com o stoplevel mínimo permitido
double price = check_stoplevel(SymbolInfoDouble(_Symbol,SYMBOL_ASK),ORDER_TYPE_BUY_LIMIT);
double sl =(stop > 0.0) ? normalizar(price-(in_stoploss*_Point)) : 0.00; // Stoploss
double tp =(take > 0.0) ? normalizar(price+(in_takeprofit*_Point)) : 0.00; // Takeprofit
// Envio sempre de ordem pendente
return realizar_negocio(TRADE_ACTION_PENDING,ORDER_TYPE_BUY_LIMIT,0,in_volume,price,sl,tp);
}
//+------------------------------------------------------------------+
//| Envio de venda limitada |
//+------------------------------------------------------------------+
bool vender(const double stop, const double take)
{
double price = check_stoplevel(SymbolInfoDouble(_Symbol,SYMBOL_BID),ORDER_TYPE_SELL_LIMIT);
double sl = (stop > 0.0) ? normalizar(price+(in_stoploss*_Point)) : 0.00;
double tp = (take > 0.0) ? normalizar(price-(in_takeprofit*_Point)) : 0.00;
return realizar_negocio(TRADE_ACTION_PENDING,ORDER_TYPE_SELL_LIMIT,0,in_volume,price,sl,tp);
}
//+------------------------------------------------------------------+
//| Sinal e envio de entrada |
//+------------------------------------------------------------------+
bool sinal_entrada(const s_ordens &ordem)
{
double regressao[1] = {0};
double macd[1] = {0};
if(!check_barra()) // Verificando se houve ou não negócio relizado na barra atual
if(CopyBuffer(handle_reg,0,0,1,regressao) > 0) // Confere se copiou corretamente os dados do indicador
if(MathAbs(regressao[0]) >= in_reg_coef) // Verifica o valor do indicador de regressão
if(CopyBuffer(handle_macd,0,0,1,macd) > 0) // Verifica o valor do indicador macd
if(regressao[0] > in_reg_coef)
{
if(!OrderSelect(ordem.venda_limit)) // Verifica se existe alguma ordem pendente, evitar repetidas
if(macd[0] < -in_macd_min)
if(check_spread()) // Checando o spread
return vender(in_stoploss,in_takeprofit); // Enviando ordem
}
else
if(!OrderSelect(ordem.compra_limit)) // Regra para compra
if(macd[0] > in_macd_min)
if(check_spread())
return comprar(in_stoploss,in_takeprofit);
return false; // Não foram enviadas ordem
}
//+------------------------------------------------------------------+
//| Sinal de encerramento de uma posição |
//+------------------------------------------------------------------+
bool sinal_saida(const s_ordens &ordem, const s_posicoes &posicao)
{
if(in_barra_atual)
if(iTime(_Symbol,_Period,0) < posicao.abertura) // Impede saída no mesmo candle
return false;
double regressao[1] = {0};
if(CopyBuffer(handle_reg,0,0,1,regressao) > 0)
if(posicao.volume < 0.00) // Verifica a condicional para uma posição comprada
{
if(regressao[0] < 0.00) // Saída no curzamento da linha central
if(!OrderSelect(ordem.compra_limit)) // Verifica se não há ordem repetida
return comprar(0.0,0.0); // Ordem de saída
}
else
if(regressao[0] > 0.00)
if(!OrderSelect(ordem.venda_limit))
return vender(0.0,0.0);
return false;
}
//+------------------------------------------------------------------+
//| Execução do expert |
//+------------------------------------------------------------------+
void OnTick(void)
{
s_posicoes posicao = posicionamento(); // Contabilizando posições abertas
if(in_hab_painel)
atualizar_painel(posicao); // Atualiza os dados do painel
if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) // Evita que o expert funcione se não for permitido pelas configurações da plataforma
return;
if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)) // Verifica se a conta é habilitada para robôs
return;
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) // Verifica se a opção auto trading do terminal está ativa
return;
s_ordens ordem = pendentes(); // Verificando todas as ordens pendentes
TimeToStruct(TimeCurrent(),hora_atual); // Atualizando o horário atual
// Verifica se não foi acionado o horário de zeragem compulsória, tem prioridade sobre o recurso de hora normal
if(horario_zeragem())
{
deletar_pendentes(ordem); // Deleta todas as ordens pendentes no horário de zeragem
if(posicao.volume != 0.0)
if(check_permissao()) // Confere se já foi ou não enviada a ordem a mercado
zeragem_compulsoria(); // Zerando a mercado todas as posições abertas
}
else
if(posicao.volume != 0.00)
{
if(sinal_saida(ordem,posicao)) // Se possuir posição procura pelo sinal de saída
printf("[%s] Enviado ordem de saída",in_nome);
}
else
if(horario_entrar()) // Se não for horário de zeragem compulsória, verifica se está dentro da janela operacional de horário
{
if(sinal_entrada(ordem))
printf("[%s] Enviado ordem para entrada",in_nome);
}
else
deletar_pendentes(ordem); // Enviando dados das ordens a serem apagadas
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Thiago Oliveira - Olimbot |
//| contato@olimbot.com.br |
//+------------------------------------------------------------------+
#property copyright "Thiago Oliveira - OlimBot"
#property link "olimbot.com.br"
#property version "1.00"
#property description "Última atualização em 11/Janeiro/2022"
#property description " "
#property description "Este expert é constituído sem o uso de bibliotecas padrões do meta trader"
#property description "Todos os recursos são adaptados para uma melhor perfomance"
#property description "Este modelo funciona tanto para contas HEDGINGS quanto para NETTINGS"
#property description "Entradas e saídas são sempre utilizando ordens limitadas para garantir melhor preço na B3"
#property description "Se existir distância mínima para ordem pendente esta é ajustada automaticamente"
#property description "Em contas HEDGINGS a saída é fechada utilizando a posição inversa"
// O indicador personalizado deve estar com o arquivo executável na pasta de indicadores
//#define indicador "Indicators\\Regressao.ex5" // Indicador personalizado que será compilado na pasta Indicators\\...
// Indicador compilado deve estar no formato .ex5
//#resource "\\"+indicador // Compilando junto ao EA o indicador
// Enumerador para o tipo de preenchimento
enum e_filling
{
es_fok = ORDER_FILLING_FOK, // Fok
es_ioc = ORDER_FILLING_IOC, // Ioc
es_return = ORDER_FILLING_RETURN // Return
};
// Enumerador para o tipo de validade da ordem
enum e_time
{
es_gtc = ORDER_TIME_GTC, // Até cancelar
es_day = ORDER_TIME_DAY // Hoje
};
// Caracterização de true e false para sim e não
enum e_sn
{
nao = 0, // Não
sim = 1 // Sim
};
//+------------------------------------------------------------------+
//| Parâmetros de entrada |
//+------------------------------------------------------------------+
sinput group "CONFIGURAÇÃO INICIAL";
sinput string in_nome = "SET OLIMBOT"; // Nome do expert
sinput ulong in_magic = 123; // Magic Number
sinput double in_volume = 1; // Volume
input ENUM_TIMEFRAMES in_timeframe = PERIOD_CURRENT; // Tempo gráfico do EA
sinput group "CONFIGURAÇÃO ADICIONAL";
sinput e_filling in_preenchimento = es_return; // Tipo de preenchimento
sinput e_time in_validade = es_day; // Validade da ordem
sinput e_sn in_hab_painel = true; // Habilitar painel gráfico
sinput e_sn in_hab_indicadores = false; // Inserir indicadores
sinput group "ALVOS DE SAÍDA";
input double in_takeprofit = 0; // TakeProfit (pts)
input double in_stoploss = 0; // StopLoss (pts)
sinput group "INDICADOR RSI";
input int in_reg_periodo = 20; // Período do RSI
input double in_banda_sup = 70; // Banda superior
input double in_banda_inf = 30; // Banda inferior
sinput group "INDICADOR MACD";
input int in_macd_fast = 12; // Período EMA rápida
input int in_macd_slow = 5; // Período EMA lenta
sinput group "AJUSTES DE SINAIS";
input e_sn in_cancel_sinal = true; // Atualizar pendente a cada sinal
input e_sn in_barra_atual = true; // Impedir novas entradas na mesma barra
input double in_macd_min = 0; // Tamanho mínimo do MACD
sinput double in_spread = 0; // Spread máximo (ticks)(0=off)
sinput group "REGRAS DE HORÁRIOS";
input e_sn in_hab_hora = true; // Habilitar verificação de horários
input string in_iniciar = "09:00"; // Horário de iniciar (novas posições)
input string in_parar = "14:45"; // Horário de parar (novas posições)
input e_sn in_hab_zerar = true; // Habilitar zeragem compulsória
input string in_zerar = "14:55"; // Horário de zeragem (compulsória)
int handle_macd, handle_reg; // Manipuladores de indicadores
MqlDateTime hora_iniciar, hora_parar, hora_zerar, hora_atual; // Estruturas de horários
// Estrutura para armazenar os dados das ordens pendentes
struct s_ordens
{
ulong compra_limit; // Número que corresponderá a uma ordem de compra pendente
ulong venda_limit; // Para venda pendente (ordem limitada)
};
// Estrutura para armazenar os dados das posições abertas
struct s_posicoes
{
double volume; // Lot
double lucro; // Lucro real da posição aberta, referente ao verdadeiro preço de saída
datetime abertura; // Horário que se inicio a posição
ulong ticket_compra; // Bilhete da posição se for de compra
ulong ticket_venda; // Bilhete da posição de venda se existir ambas simultanemanetes, são fechadas entre si
};
//+------------------------------------------------------------------+
//| Inicializãção do expert |
//+------------------------------------------------------------------+
int OnInit()
{
ChartSetSymbolPeriod(0,_Symbol,in_timeframe); // Força que o tempo gráfico da janela do robô fique no escolhido
if(!in_hab_indicadores) // Desabilita visualização dos indicadores no backtest, se não permitir inserir
TesterHideIndicators(true); // Deve ser chamado antes da inicialização dos indicadores
// Chamada dos indicadores utilizados
// No macd, o parâmetro de sinal não é necessário para esta estratégia
handle_macd = iMACD(_Symbol,_Period,in_macd_fast,in_macd_slow,1,PRICE_CLOSE); // Indicador nativo mql5
handle_reg = iRSI(_Symbol,_Period,in_reg_periodo,PRICE_CLOSE);
// Confirmando se os indicadores foram inseridos corretamente no EA
if(handle_reg == INVALID_HANDLE)
{
Print("[%s] Erro na criação do manipulador do indicador regressão linear",in_nome);
return INIT_FAILED;
}
if(handle_macd == INVALID_HANDLE)
{
Print("[%s] Erro na criação do manipulador do indicador MACD",in_nome);
return INIT_FAILED;
}
// Passando a referência de horário para o padrão correto
TimeToStruct(StringToTime(in_iniciar),hora_iniciar);
TimeToStruct(StringToTime(in_parar),hora_parar);
TimeToStruct(StringToTime(in_zerar),hora_zerar);
// Verifica se o horário de ínicio realmente antecede o horário máximo de abertura de posição
if(hora_iniciar.hour > hora_parar.hour || (hora_iniciar.hour == hora_parar.hour && hora_iniciar.min >= hora_parar.min))
{
printf("[%s] Configuração de horários de entrada e parada inválidos",in_nome);
return INIT_FAILED;
}
// Se estiver habilitada inicia o desenho do painel gráfico (dashboard)
if(in_hab_painel)
if(!iniciar_painel())
{
printf("[%s] Falha ao iniciar o painel gráfico",in_nome);
return INIT_FAILED;
}
// Adiciona as linhas de preço e retira a separação em grade para melhor visualizar
ChartSetInteger(0,CHART_SHOW_GRID,0,false); // Remove grade do gráfico
ChartSetInteger(0,CHART_SHOW_ASK_LINE,0,true); // Adiciona linha ask
ChartSetInteger(0,CHART_SHOW_BID_LINE,0,true); // Adiciona linha bid
ChartSetInteger(0,CHART_SHOW_LAST_LINE,0,true); // Adiciona linha do último negócio se for mercado de bolsa
if(in_hab_indicadores) // Possibilita a inserção dos indicadores no gráfico do EA
if(remover_indicadores()) // Remove todos os indicadores no gráfico antes de inserir os usados pelo EA
{
ChartIndicatorAdd(0,1,handle_reg); // Inserindo no modo visual o indicador de regressão
ChartIndicatorAdd(0,2,handle_macd); // Inserindo no modo visual o indicador de MACD
}
printf("[%s] ***** Expert Iniciado com sucesso",in_nome);
printf("[%s] ***** Horários: Local %s Corretora %s",
in_nome,TimeToString(TimeLocal(),TIME_SECONDS),TimeToString(TimeCurrent(),TIME_SECONDS));
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Desligamento de expert |
//+------------------------------------------------------------------+
void OnDeinit(const int motivo)
{
if(in_hab_indicadores)
remover_indicadores(); // Remove todos os indicadores do gráfico se a opção estiver habilitada
ObjectsDeleteAll(0,"painel_",0,-1); // Remove o painel do gráfico, se existir
IndicatorRelease(handle_macd); // Liberando mémoria alocada para os indicadores
IndicatorRelease(handle_reg);
printf("[%s] ***** Horários: Local %s Corretora %s",
in_nome,TimeToString(TimeLocal(),TIME_SECONDS),TimeToString(TimeCurrent(),TIME_SECONDS));
printf("[%s] ***** Expert desligado pelo motivo %d",in_nome,motivo);
// A liberação de memória dos indicadores pode ser mais lenta que na inicialização
// Se o EA for inserido enquanto ainda está o no porcesso de liberar mémoria, parte dos dados dos indicadores pode ser perdido
Sleep(1000); // Por isso é necessário aguardar a remoção completa
}
//+------------------------------------------------------------------+
//| Remoção de indicadores |
//+------------------------------------------------------------------+
bool remover_indicadores(void)
{
// Verifica todas as janelas do gráfico
for(int i=(int)ChartGetInteger(0,CHART_WINDOWS_TOTAL)-1; i>=0; i--)
while(ChartIndicatorsTotal(0,i) > 0) // Verifica todos os indicadores em cada subjanela
{
string nome = ChartIndicatorName(0,i,0); // Pega o nome do indicador
if(!ChartIndicatorDelete(0,i,nome)) // Após a obtenção do nome, o mesmo é deletado da janela
break;
}
return true;
}
//+------------------------------------------------------------------+
//| Inicialização do painel gráfico |
//+------------------------------------------------------------------+
bool iniciar_painel(void)
{
// Somente necessário chamar uma vez tal função
// Nome dado ao fundo do painel
string nome = "painel_fundo";
if(ObjectCreate(0,nome,OBJ_RECTANGLE_LABEL,0,0,0))
{
ObjectSetInteger(0,nome,OBJPROP_XDISTANCE,5); // Distância da borda
ObjectSetInteger(0,nome,OBJPROP_YDISTANCE,15);
ObjectSetInteger(0,nome,OBJPROP_XSIZE,250); // Tamanho das cordernadas do painel eixo X
ObjectSetInteger(0,nome,OBJPROP_YSIZE,130); // Eixo Y
ObjectSetInteger(0,nome,OBJPROP_BGCOLOR,clrBlack); // Cor de fundo
ObjectSetInteger(0,nome,OBJPROP_BORDER_TYPE,BORDER_FLAT);
ObjectSetInteger(0,nome,OBJPROP_CORNER,CORNER_LEFT_UPPER); // Referência de posicionamento (borda superior esquerda)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrMaroon); // Cor da borda
ObjectSetInteger(0,nome,OBJPROP_STYLE,STYLE_SOLID);
ObjectSetInteger(0,nome,OBJPROP_WIDTH,3);
ObjectSetInteger(0,nome,OBJPROP_BACK,false); // Força painel para frente do gráfico
ObjectSetInteger(0,nome,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,nome,OBJPROP_SELECTED,false);
ObjectSetInteger(0,nome,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,nome,OBJPROP_ZORDER,0);
}
else
return false;
Sleep(200); // Pausa para processar o desenho do fundo do painel
ChartRedraw(); // Força uma atualização do desenho, garantindo o fundo desenhado antes dos objetos
// Loop para desenhar cada item exibido no painel
for(int i=0; i<8; i++)
{
string texto[8];
nome = "painel_parametro_"+IntegerToString(i); // Nome de cada item pegando a sua posuição como complementação
switch(i)
{
case 0:
texto[i] = "Nome do EA";
break;
case 1:
texto[i] = "Magic Number";
break;
case 2:
texto[i] = "Posição";
break;
case 3:
texto[i] = "Abertura Pos";
break;
case 4:
texto[i] = "Lucro Real";
break;
case 5:
texto[i] = "Oscilação";
break;
case 6:
texto[i] = "Atraso Corretora";
break;
case 7:
texto[i] = "Tempo Nova Barra";
break;
default:
break;
}
// Criando cada item do painel como objeto de texto
if(ObjectCreate(0,nome,OBJ_LABEL,0,0,0))
{
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrSnow); // Cor do texto dos parâmetros
ObjectSetInteger(0,nome,OBJPROP_XDISTANCE,10);
ObjectSetInteger(0,nome,OBJPROP_YDISTANCE,20+(i*15)); // Pula uma linha com esses valores em cada passo do loop
ObjectSetInteger(0,nome,OBJPROP_FONTSIZE,9);
ObjectSetString(0,nome,OBJPROP_FONT,"Rockwell"); // Tipo da fonte de texto
ObjectSetString(0,nome,OBJPROP_TEXT,texto[i]); // Texto a ser exibido
ObjectSetInteger(0,nome,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,nome,OBJPROP_BACK,false);
}
else
return false;
// Criando os itens que receberão os dados no painel
nome = "painel_dados_"+IntegerToString(i); // Nome dos objetos criados, ainda no loop
if(ObjectCreate(0,nome,OBJ_LABEL,0,0,0))
{
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrGray);
ObjectSetInteger(0,nome,OBJPROP_XDISTANCE,140); // Distânciando mais da borda, garantido que ficará lada a lado
ObjectSetInteger(0,nome,OBJPROP_YDISTANCE,20+(i*15)); // Pulando uma linha a cada ciclo do loop
ObjectSetInteger(0,nome,OBJPROP_FONTSIZE,9);
ObjectSetString(0,nome,OBJPROP_FONT,"Rockwell");
ObjectSetString(0,nome,OBJPROP_TEXT,"-");
ObjectSetInteger(0,nome,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,nome,OBJPROP_BACK,false);
}
else
return false;
}
Sleep(200);
ChartRedraw();
// Verifica as posições e envia os dados para o painel
s_posicoes posicao = posicionamento();
atualizar_painel(posicao); // Enviando posicionamento do EA para o painel
return true;
}
//+------------------------------------------------------------------+
//| Atualização dos dados do painel |
//+------------------------------------------------------------------+
void atualizar_painel(const s_posicoes &posicao)
{
// Alguns dados exibidos no painel
string texto[8]; // Receberá os valores exibidos no painel
datetime tempo = iTime(_Symbol,_Period,0)+PeriodSeconds(_Period)-TimeCurrent(); // Tempo restante da barra
double ontem = iClose(_Symbol,PERIOD_D1,1); // Fechamento dia anterior
double dif = iClose(_Symbol,PERIOD_D1,0)-ontem; // DIferença entre fechamentos
double oscilacao = (ontem > 0) ? (dif/ontem)*100 : 0.00; // Cálculo percentual do desempenho diário do ativo
// Atribuindo os valores
for(int i=0; i<8; i++)
{
string nome = "painel_dados_"+IntegerToString(i);
switch(i)
{
case 0:
texto[i] = in_nome;
break;
case 1:
texto[i] = IntegerToString(in_magic);
break;
case 2:
texto[i] = DoubleToString(posicao.volume,2);
if(posicao.volume > 0) // Regra de coloração para alta
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrGreen);
else
if(posicao.volume < 0) // Regra de coloração para baixa
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrRed);
else
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrWhite); // Regra de coloração neutra
break;
case 3:
texto[i] = TimeToString(posicao.abertura,TIME_SECONDS);
break;
case 4:
texto[i] = DoubleToString(posicao.lucro,2);
if(posicao.lucro > 0)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrGreen);
else
if(posicao.lucro < 0)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrRed);
else
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrWhite);
break;
case 5:
texto[i] = DoubleToString(dif,_Digits)+" ("+DoubleToString(oscilacao,2)+"%)";
if(dif > 0)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrGreen);
else
if(dif < 0)
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrRed);
else
ObjectSetInteger(0,nome,OBJPROP_COLOR,clrWhite);
break;
case 6:
texto[i] = TimeToString(TimeLocal()-TimeCurrent(),TIME_SECONDS); // Sincronização do tempo local e da corretora
break;
case 7:
texto[i] = TimeToString(tempo,TIME_SECONDS);
break;
default:
break;
}
ObjectSetString(0,nome,OBJPROP_TEXT,texto[i]); // Atualiza o valor em forma de texto
}
}
//+------------------------------------------------------------------+
//| Checar se horário de operação |
//+------------------------------------------------------------------+
bool horario_entrar(void)
{
if(!in_hab_hora) // Se não for para verificar horários a função retorna que pode continuar
return true;
static bool exibir_msg = true; // Variável estática para evitar excesso de repetição da msg exibida na codicional
// Verifica se está dentro do horário permitido
if(hora_atual.hour > hora_iniciar.hour || (hora_atual.hour == hora_iniciar.hour && hora_atual.min >= hora_iniciar.min))
if(hora_atual.hour < hora_parar.hour || (hora_atual.hour == hora_parar.hour && hora_atual.min < hora_parar.min))
{
if(exibir_msg)
printf("[%s] Horário de operações permitido. Operacional das %s às %s",in_nome,in_iniciar,in_parar);
exibir_msg = false; // Impede a repetição da msg
return true;
}
if(!exibir_msg)
{
if(exibir_msg)
printf("[%s] Fora do horário de operações. Operacional das %s às %s",in_nome,in_iniciar,in_parar);
exibir_msg = true; // Atualiza que deverá repetir a msg se a condicional for satisfeita na próxima chamada
}
return false; // Não stá no horário de novas posições
}
//+------------------------------------------------------------------+
//| Horário de zeragem compulsória |
//+------------------------------------------------------------------+
bool horario_zeragem(void)
{
// Função que verifica horário de zeragem
static bool exibir_msg = true; // Idem recurso anterior
if(in_hab_zerar) // Desabilita e habilita zeragem ou não pr horário
if(hora_atual.hour > hora_zerar.hour || (hora_atual.hour == hora_zerar.hour && hora_atual.min >= hora_zerar.min))
{
if(exibir_msg)
printf("[%s] Horário de zeragem compulsória às %s",in_nome,in_zerar);
exibir_msg = false;
return true; // Confirma que terá que zerar as posições
}
exibir_msg = true;
return false;
}
//+------------------------------------------------------------------+
//| Contador de ordens pendentes |
//+------------------------------------------------------------------+
s_ordens pendentes(void)
{
// Estrutura que armzenará e retornará os valores das ordens pendentes encontradas
s_ordens ordem;
ZeroMemory(ordem);
for(int i=OrdersTotal()-1; i>=0; i--) // Loop para verificar todas as ordens
{
ulong ticket = OrderGetTicket(i); // Pegando separadamente o bilhete atribuído a cada ordem
if(!OrderSelect(ticket))
continue;
if((ulong)OrderGetInteger(ORDER_MAGIC) != in_magic) // Verificar magic number
continue;
if(OrderGetString(ORDER_SYMBOL) != _Symbol) // Verificar simbolo
continue;
ENUM_ORDER_TYPE tipo = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); // Verifica ó tipo de ordem, limitada ou stop
bool deletar_repetida = false;
// Se houver ordem colocada em barra anterior a mesma é deletada na troca de barra
if(in_cancel_sinal) // Pode ser habilitada ou não este recurso via input
if(OrderGetInteger(ORDER_TIME_SETUP) < iTime(_Symbol,_Period,0))
if(realizar_negocio(TRADE_ACTION_REMOVE,tipo,ticket))
continue;
// Se houver mais de uma ordem no mesmo sentido, a mais antiga é deletada
if(tipo == ORDER_TYPE_BUY_LIMIT)
{
if(ordem.compra_limit == 0)
ordem.compra_limit = ticket;
else
deletar_repetida = true; // Confimando que há ordens repetidas
}
else
if(tipo == ORDER_TYPE_SELL_LIMIT)
{
if(ordem.venda_limit == 0)
ordem.venda_limit = ticket;
else
deletar_repetida = true;
}
else
deletar_repetida = true;
if(deletar_repetida)
realizar_negocio(TRADE_ACTION_REMOVE,tipo,ticket); // Deletando ordem repetida, se houver
}
return ordem;
}
//+------------------------------------------------------------------+
//| Posicionamento |
//+------------------------------------------------------------------+
s_posicoes posicionamento(void)
{
// Estrutura para armazenar os dados de ordens executadas
s_posicoes posicao;
ZeroMemory(posicao);
for(int i=PositionsTotal()-1; i>=0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC) != in_magic)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
ENUM_POSITION_TYPE tipo = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double vol = PositionGetDouble(POSITION_VOLUME);
double price = PositionGetDouble(POSITION_PRICE_OPEN);
if(posicao.abertura == 0)
posicao.abertura = (datetime)PositionGetInteger(POSITION_TIME);
if(tipo == POSITION_TYPE_BUY)
{
posicao.volume += vol; // Verifica o volume aberto, positivo para compras
posicao.ticket_compra = ticket;
// Verifica o lucro real da posição com base no preço verdadeiro de saída e não pelo último negócio
if(!OrderCalcProfit(ORDER_TYPE_BUY,_Symbol,vol,price,SymbolInfoDouble(_Symbol,SYMBOL_BID),posicao.lucro))
printf("[%s] Falha no cálculo de lucro da posição comprada %d",in_nome,(string)ticket);
}
else
{
posicao.volume -= vol; // Verifica o volume aberto, negativo para vendas
posicao.ticket_venda = ticket;
if(!OrderCalcProfit(ORDER_TYPE_SELL,_Symbol,vol,price,SymbolInfoDouble(_Symbol,SYMBOL_ASK),posicao.lucro))
printf("[%s] Falha no cálculo de lucro da posição vendida %d",in_nome,(string)ticket);
}
// Se houver compra e venda abertas simultaneamente, conta tipo hedge, serão fechadas uma pela outra
if(posicao.ticket_compra > 0)
if(posicao.ticket_venda > 0)
if(realizar_negocio(TRADE_ACTION_CLOSE_BY,ORDER_TYPE_CLOSE_BY,posicao.ticket_compra,0,0,0,0,posicao.ticket_venda))
{
i = PositionsTotal(); // Reinicia o contador para contabilizar a atualização de posições
ZeroMemory(posicao); // Zera os dados armazenados para verificar novamente
}
}
return posicao;
}
//+------------------------------------------------------------------+
//| Verificação de spread entre compra e venda |
//+------------------------------------------------------------------+
bool check_spread(void)
{
if(in_spread == 0) // Se selecionado zero via input, desabilita a verificação
return true;
double spread = SymbolInfoInteger(_Symbol,SYMBOL_SPREAD)*_Point; // Verificando o spread atual
double spr = normalizar(in_spread*SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE)); // Convertendo para tamanho de tick
if(spread > spr) // Verificando se o spread é superior ao selecionado
{
printf("[%s] Entrada não permitida. Spread de %f supera o máximo de %f",in_nome,spread,spr);
return false;
}
else
return true;
}
//+------------------------------------------------------------------+
//| Verificação da distância mínima do stop |
//+------------------------------------------------------------------+
double check_stoplevel(double price, const ENUM_ORDER_TYPE tipo)
{
// Distância mínima permitida do preço para colocar ordens pendentes
double stoplevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point;
double referencia = 0.00; // Iniciando variavel
switch(tipo)
{
case ORDER_TYPE_BUY_LIMIT:
referencia = SymbolInfoDouble(_Symbol,SYMBOL_ASK)-stoplevel; // Referência para o stoplevel
if(price > referencia)
price = normalizar(referencia); // Altera para o valor mínimo aceito se a ordem estiver fora do parâmetro
break;
case ORDER_TYPE_SELL_LIMIT:
referencia = SymbolInfoDouble(_Symbol,SYMBOL_BID)+stoplevel;
if(price < referencia)
price = normalizar(referencia);
break;
default:
break;
}
return price;
}
//+------------------------------------------------------------------+
//| Filtro de ordens a mercado repetidas |
//+------------------------------------------------------------------+
bool check_permissao(void)
{
// Verifica se tem ordem aguardando ser executada ou não, evita ordens a mercado repetidas ou excessivas
for(int i=OrdersTotal()-1; i>=0; i--)
{
ulong ticket = OrderGetTicket(i);
if(!OrderSelect(ticket))
continue;
if((ulong)OrderGetInteger(ORDER_MAGIC) != in_magic)
continue;
if(OrderGetString(ORDER_SYMBOL) != _Symbol)
continue;
ENUM_ORDER_TYPE tipo = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
// Verificando ordens a mercado que ainda não foram confirmadas pela corretora
if(tipo == ORDER_TYPE_BUY || tipo == ORDER_TYPE_SELL || tipo == SYMBOL_ORDER_CLOSEBY)
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Filtro de posição executada em barra atual |
//+------------------------------------------------------------------+
bool check_barra(void)
{
// Verifica se houve ordem executada na barra atual, evitando entradas excessivas na mesma barra
// Pode ser habilitada ou não
if(!in_barra_atual)
return false;
// Selecionando apenas a barra atual para o período
HistorySelect(iTime(_Symbol,_Period,0),TimeCurrent());
for(int i=HistoryDealsTotal()-1; i>=0; i--)
{
ulong ticket = HistoryDealGetTicket(i);
if(ticket == 0)
continue;
if(HistoryDealGetInteger(ticket,DEAL_MAGIC) != in_magic)
continue;
if(HistoryDealGetString(ticket,DEAL_SYMBOL) != _Symbol)
continue;
ENUM_DEAL_TYPE tipo = (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket,DEAL_TYPE);
// Confirmando que a ordem foi de compra ou venda, não pendente nem fechamento pela oposta
if(tipo == DEAL_TYPE_BUY || tipo == DEAL_TYPE_SELL)
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Deletar ordens pendentes |
//+------------------------------------------------------------------+
void deletar_pendentes(const s_ordens &ordem)
{
// Verifica se o bilhete da ordem existe e remove se positivo
if(ordem.compra_limit != 0)
if(OrderSelect(ordem.compra_limit)) // Confirmando e selecionando a ordem pelo bilhete único
realizar_negocio(TRADE_ACTION_REMOVE,ORDER_TYPE_BUY_LIMIT,ordem.compra_limit); // Deletando ordem pendente
if(ordem.venda_limit != 0)
if(OrderSelect(ordem.venda_limit))
realizar_negocio(TRADE_ACTION_REMOVE,ORDER_TYPE_SELL_LIMIT,ordem.venda_limit);
}
//+------------------------------------------------------------------+
//| Zeragem compulsória |
//+------------------------------------------------------------------+
void zeragem_compulsoria(void)
{
// Verificando todas as posições abertas inclusive serve para contas hedge
for(int i=PositionsTotal()-1; i>=0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC) != in_magic)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
ENUM_POSITION_TYPE tipo = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double vol = PositionGetDouble(POSITION_VOLUME);
// Zerando ordem a mercado, seja de compra ou venda
if(tipo == POSITION_TYPE_BUY)
realizar_negocio(TRADE_ACTION_DEAL,ORDER_TYPE_SELL,ticket,vol,SymbolInfoDouble(_Symbol,SYMBOL_BID));
else
realizar_negocio(TRADE_ACTION_DEAL,ORDER_TYPE_BUY,ticket,vol,SymbolInfoDouble(_Symbol,SYMBOL_ASK));
}
}
//+------------------------------------------------------------------+
//| Normalizar preço |
//+------------------------------------------------------------------+
double normalizar(const double price)
{
// Função que garante que o preço fique com valores aceitos para o ativo
double tamanho = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
int digitos = (int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
double ajuste = (tamanho > 0 && price > 0) ? ((MathRound(price/tamanho))*tamanho) : 0.00;
if(price > 0)
if(ajuste < tamanho)
ajuste = tamanho;
// Ajustando como saída o preço corrigido ou aprovado
return NormalizeDouble(ajuste,digitos);
}
//+------------------------------------------------------------------+
//| Realizando as transações |
//+------------------------------------------------------------------+
bool realizar_negocio(const ENUM_TRADE_REQUEST_ACTIONS acao,
const ENUM_ORDER_TYPE tipo,
const ulong ticket,
const double volume=0.0,
const double price=0.0,
const double stoploss=0.0,
const double takeprofit=0.0,
const ulong ticket_by=0.0)
{
MqlTradeRequest request; // Estrutura da ordem a ser enviada
MqlTradeResult result; // Resultados de verificação
MqlTradeCheckResult check_result; // Dados de verificação
// Zerando residuos da solicitação anterior
ZeroMemory(request);
ZeroMemory(result);
ZeroMemory(check_result);
request.magic = in_magic;
request.symbol = _Symbol;
request.comment = "["+in_nome+"]";
request.type_time = (ENUM_ORDER_TYPE_TIME)in_validade; // Tipo de validade da ordem, obrigatório em algumas corretoras
request.type_filling = (ENUM_ORDER_TYPE_FILLING)in_preenchimento; // Tipo de preenchimento, de acordo com o tipo de mercado
request.price = normalizar(price);
request.volume = NormalizeDouble(volume,2);
request.action = acao;
request.type = tipo;
request.sl = stoploss;
request.tp = takeprofit;
request.position = ticket;
request.position_by = ticket_by;
request.order = ticket;
// Checando a ordem e seus resultados antes do envio da mesma
if(!OrderCheck(request,check_result))
{
printf("[%s] Erro %d na checagem da ordem. Código %d (%s)",in_nome,GetLastError(),check_result.retcode,check_result.comment);
return false;
}
else
printf("[%s] Checagem de ordem bem sucedida. Enviando ordem...",in_nome);
// Se bem sucessedida a verificação a ordem é enviada
if(!OrderSend(request,result))
{
// Mensagem exibida em caso de falha no envio da ordem
printf("[%s] Erro %d no envio da ordem. Código %d (%s)",in_nome,GetLastError(),result.retcode,result.comment);
return false;
}
else
printf("[%s] Sucesso no negócio [%d]. Código %d (%s)",in_nome,result.order,result.retcode,result.comment);
return true;
}
//+------------------------------------------------------------------+
//| Envio de compra limitada |
//+------------------------------------------------------------------+
bool comprar(const double stop, const double take)
{
// Austando na entrada o preço de acordo com o stoplevel mínimo permitido
double price = check_stoplevel(SymbolInfoDouble(_Symbol,SYMBOL_ASK),ORDER_TYPE_BUY_LIMIT);
double sl =(stop > 0.0) ? normalizar(price-(in_stoploss*_Point)) : 0.00; // Stoploss
double tp =(take > 0.0) ? normalizar(price+(in_takeprofit*_Point)) : 0.00; // Takeprofit
// Envio sempre de ordem pendente
return realizar_negocio(TRADE_ACTION_PENDING,ORDER_TYPE_BUY_LIMIT,0,in_volume,price,sl,tp);
}
//+------------------------------------------------------------------+
//| Envio de venda limitada |
//+------------------------------------------------------------------+
bool vender(const double stop, const double take)
{
double price = check_stoplevel(SymbolInfoDouble(_Symbol,SYMBOL_BID),ORDER_TYPE_SELL_LIMIT);
double sl = (stop > 0.0) ? normalizar(price+(in_stoploss*_Point)) : 0.00;
double tp = (take > 0.0) ? normalizar(price-(in_takeprofit*_Point)) : 0.00;
return realizar_negocio(TRADE_ACTION_PENDING,ORDER_TYPE_SELL_LIMIT,0,in_volume,price,sl,tp);
}
//+------------------------------------------------------------------+
//| Sinal e envio de entrada |
//+------------------------------------------------------------------+
bool sinal_entrada(const s_ordens &ordem)
{
double regressao[1] = {0};
double macd[1] = {0};
if(!check_barra()) // Verificando se houve ou não negócio relizado na barra atual
if(CopyBuffer(handle_reg,0,0,1,regressao) > 0) // Confere se copiou corretamente os dados do indicador
// if(MathAbs(regressao[0]) >= in_reg_coef) // Verifica o valor do indicador de regressão
if(CopyBuffer(handle_macd,0,0,1,macd) > 0) // Verifica o valor do indicador macd
if(regressao[0] > in_banda_sup)
{
if(!OrderSelect(ordem.venda_limit)) // Verifica se existe alguma ordem pendente, evitar repetidas
if(macd[0] < -in_macd_min)
if(check_spread()) // Checando o spread
return vender(in_stoploss,in_takeprofit); // Enviando ordem
}
else
if(regressao[0] < in_banda_inf)
if(!OrderSelect(ordem.compra_limit)) // Regra para compra
if(macd[0] > in_macd_min)
if(check_spread())
return comprar(in_stoploss,in_takeprofit);
return false; // Não foram enviadas ordem
}
//+------------------------------------------------------------------+
//| Sinal de encerramento de uma posição |
//+------------------------------------------------------------------+
bool sinal_saida(const s_ordens &ordem, const s_posicoes &posicao)
{
if(in_barra_atual)
if(iTime(_Symbol,_Period,0) < posicao.abertura) // Impede saída no mesmo candle
return false;
double regressao[1] = {0};
if(CopyBuffer(handle_reg,0,0,1,regressao) > 0)
if(posicao.volume < 0.00) // Verifica a condicional para uma posição comprada
{
if(regressao[0] < 50) // Saída no curzamento da linha central
if(!OrderSelect(ordem.compra_limit)) // Verifica se não há ordem repetida
return comprar(0.0,0.0); // Ordem de saída
}
else
if(regressao[0] > 50)
if(!OrderSelect(ordem.venda_limit))
return vender(0.0,0.0);
return false;
}
//+------------------------------------------------------------------+
//| Execução do expert |
//+------------------------------------------------------------------+
void OnTick(void)
{
s_posicoes posicao = posicionamento(); // Contabilizando posições abertas
if(in_hab_painel)
atualizar_painel(posicao); // Atualiza os dados do painel
if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) // Evita que o expert funcione se não for permitido pelas configurações da plataforma
return;
if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)) // Verifica se a conta é habilitada para robôs
return;
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) // Verifica se a opção auto trading do terminal está ativa
return;
s_ordens ordem = pendentes(); // Verificando todas as ordens pendentes
TimeToStruct(TimeCurrent(),hora_atual); // Atualizando o horário atual
// Verifica se não foi acionado o horário de zeragem compulsória, tem prioridade sobre o recurso de hora normal
if(horario_zeragem())
{
deletar_pendentes(ordem); // Deleta todas as ordens pendentes no horário de zeragem
if(posicao.volume != 0.0)
if(check_permissao()) // Confere se já foi ou não enviada a ordem a mercado
zeragem_compulsoria(); // Zerando a mercado todas as posições abertas
}
else
if(posicao.volume != 0.00)
{
if(sinal_saida(ordem,posicao)) // Se possuir posição procura pelo sinal de saída
printf("[%s] Enviado ordem de saída",in_nome);
}
else
if(horario_entrar()) // Se não for horário de zeragem compulsória, verifica se está dentro da janela operacional de horário
{
if(sinal_entrada(ordem))
printf("[%s] Enviado ordem para entrada",in_nome);
}
else
deletar_pendentes(ordem); // Enviando dados das ordens a serem apagadas
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Thiago Oliveira - Olimbot |
//| contato@olimbot.com.br |
//+------------------------------------------------------------------+
#property copyright "Thiago Oliveira - OlimBot"
#property link "olimbot.com.br"
#property version "1.00"
#property description "Última atualização em 22/Junho/2021"
#property description " "
#property description "Este indicador calcula a regressão linear entre o preço de fechamento e uma média móvel exponencial"
#property description "É exibido a distância do preço em relação a regressão"
#property description "A regressão é dividida por uma média simples do desvio padrão do preço"
#property description "Este procedimento suaviza o movimento do indicador"
#property description "O indicador pode ser usado para mostrar força do movimento ou movimentos discrepantes"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_label1 "Ratio"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
input int m_periodo_media = 20; // Período Médio
input double m_desvio = 2.0; // Coeficiente de Desvio
int periodo_media = m_periodo_media;
int handle_ema;
int handle_std;
double RatioBuffer[]; // Buffer dos dados verdadeiros da regressão
//+------------------------------------------------------------------+
//| Inicialização das variavéis do indicador |
//+------------------------------------------------------------------+
int OnInit()
{
if(m_periodo_media < 2) // Garantido que pelo input o valor do período terá um valor mínimo
periodo_media = 2;
// Indicadores usados no cálculo da regressão
handle_std = iStdDev(_Symbol,_Period,periodo_media,0,MODE_EMA,PRICE_CLOSE);
handle_ema = iMA(_Symbol,_Period,periodo_media,0,MODE_EMA,PRICE_CLOSE);
if(handle_ema == INVALID_HANDLE)
{
printf("Falha em obter o handle da EMA");
return INIT_FAILED;
}
if(handle_std== INVALID_HANDLE)
{
printf("Falha em obter o handle do desvio padrão");
return INIT_FAILED;
}
// Selecionando as linhas fixas através do desvioselecionado
IndicatorSetInteger(INDICATOR_LEVELS,2);
IndicatorSetDouble(INDICATOR_LEVELVALUE,0,m_desvio);
IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-m_desvio);
SetIndexBuffer(0,RatioBuffer,INDICATOR_DATA); // Indicando o buffer dos valores
ArraySetAsSeries(RatioBuffer,true);
IndicatorSetInteger(INDICATOR_DIGITS,5);
// Nome dado ao indicador
IndicatorSetString(INDICATOR_SHORTNAME,"Regressão Linear ("+
IntegerToString(periodo_media)+"/"+
DoubleToString(m_desvio,1)+")");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Cálculo do indicador, utilizar como base o fechamento |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double& price[])
{
if(IsStopped())
return 0;
int pos = 0;
int limit_ema = periodo_media+1;
double ema[];
double std[];
if(prev_calculated == 0)
{
pos = rates_total-1;
limit_ema = rates_total;
}
ArraySetAsSeries(ema,true);
ArraySetAsSeries(price,true);
ArraySetAsSeries(std,true);
CopyBuffer(handle_ema,0,0,limit_ema,ema);
CopyBuffer(handle_std,0,0,limit_ema,std);
for(int i=pos; i>=0 && !IsStopped(); i--)
if(i < rates_total-periodo_media)
{
// Dados para calculo da regressão linear
int cnt = 0; // Contador do loop
double mediaX = 0;
double mediaY = 0;
double xy = 0;
double x2 = 0;
double X = 0;
double Y = 0;
double media_std = 0; // Média do desvio padrão
for(int k=periodo_media-1; k>=0; k--) // Verificando valores para cálculo da regressão sobre a média
{
X = ema[i+k];
Y = price[i+k];
mediaX += X;
mediaY += Y;
xy += (X*Y);
x2 += MathPow(X,2);
media_std += std[i+k]; // Média do desvio padrão
cnt++; // Contador de loop para confirmação
}
// Ínicio dos cálculos da regressão
mediaX = (cnt == 0) ? 0.0 : mediaX/cnt;
mediaY = (cnt == 0) ? 0.0 : mediaY/cnt;
media_std /= ((periodo_media == 0) ? 0.0 : periodo_media); // Finalizando a média do desvio padrão
double divisor = x2-(cnt*MathPow(mediaX,2));
double b = (divisor == 0) ? 0.0 : (xy-(cnt*mediaX*mediaY))/divisor;
double a = mediaY-(b*mediaX);
double res = a+(b*X); // Cálculo do residuo da regressão
RatioBuffer[i] = Y-res; // Verificando distância do preço em relação à regressão
RatioBuffer[i] /= (media_std > 0 ? media_std : DBL_MIN); // Estacionarizando a média dividindo pelo desvio padrão
}
return(rates_total-1);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Thiago Oliveira |
//| suporte@olimbot.com.br |
//+------------------------------------------------------------------+
#property copyright "Thiago Oliveira - OlimBot"
#property link "olimbot.com.br"
#property version "1.20"
#property description "Opere de forma consciente"
#property description "Última atualização em 23/Maio/2023"
#include
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots 3
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrYellowGreen
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrFireBrick
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrFireBrick
#property indicator_style1 STYLE_DASH
#property indicator_label1 "Banda central"
#property indicator_label2 "Banda superior"
#property indicator_label3 "Banda inferior"
input int InpBandsPeriod=20; // Período
input double InpDesvio=2.0; // Desvio
input ENUM_MA_METHOD InpMethod=MODE_SMA; // Média
int ExtBandsPeriod,ExtBandsShift;
double ExtBandsDeviationsUp, ExtBandsDeviationsDn;
int ExtPlotBegin=0;
double ExtMLBuffer[];
double ExtTLBuffer[];
double ExtBLBuffer[];
double ExtAmplitude[];
double ExtStdDevBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
void OnInit()
{
if(InpBandsPeriod<2)
{
ExtBandsPeriod=20;
PrintFormat("Valor incorreto de entrada Periodo=%d. O indicador usará valor=%d para cálculo.",InpBandsPeriod,ExtBandsPeriod);
}
else
ExtBandsPeriod=InpBandsPeriod;
if(InpDesvio==0.0)
{
ExtBandsDeviationsUp=2.0;
ExtBandsDeviationsDn=-2.0;
PrintFormat("Valor incorrento de entrada Desvios=%f. O indicador usará valor=%d para cálculo.",InpDesvio,ExtBandsDeviationsUp);
}
else
{
ExtBandsDeviationsUp=InpDesvio;
ExtBandsDeviationsDn=-InpDesvio;
}
SetIndexBuffer(0,ExtMLBuffer,INDICATOR_DATA);
SetIndexBuffer(1,ExtTLBuffer,INDICATOR_DATA);
SetIndexBuffer(2,ExtBLBuffer,INDICATOR_DATA);
SetIndexBuffer(3,ExtStdDevBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(4,ExtAmplitude,INDICATOR_CALCULATIONS);
PlotIndexSetString(0,PLOT_LABEL,"Banda("+string(ExtBandsPeriod)+") Central");
PlotIndexSetString(1,PLOT_LABEL,"Banda("+string(ExtBandsPeriod)+") Superior");
PlotIndexSetString(2,PLOT_LABEL,"Banda("+string(ExtBandsPeriod)+") Inferior");
IndicatorSetString(INDICATOR_SHORTNAME,"Canal de Keltner");
ExtPlotBegin=ExtBandsPeriod-1;
PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,ExtBandsPeriod);
PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,ExtBandsPeriod);
PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,ExtBandsPeriod);
PlotIndexSetInteger(0,PLOT_SHIFT,ExtBandsShift);
PlotIndexSetInteger(1,PLOT_SHIFT,ExtBandsShift);
PlotIndexSetInteger(2,PLOT_SHIFT,ExtBandsShift);
IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
}
//+------------------------------------------------------------------+
//| Bollinger Bands |
//+------------------------------------------------------------------+
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[])
{
if(rates_total1) ? prev_calculated-1 : 0;
for(int i=pos; i